init
This commit is contained in:
commit
a3d1d75a54
|
|
@ -0,0 +1,18 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
unpackage/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
package-lock.json
|
||||
tests/**/coverage/
|
||||
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"version" : "1.0",
|
||||
"configurations" : [
|
||||
{
|
||||
"default" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"h5" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"mp-weixin" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"provider" : "aliyun",
|
||||
"type" : "uniCloud"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,711 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'boxesPinnedApi': httpUrlFormat('/boxes/pinned', _flag),
|
||||
'boxesTagsApi': httpUrlFormat('/boxes/tags', _flag),
|
||||
'boxesApi': httpUrlFormat('/boxes', _flag),
|
||||
'boxRecordApi': httpUrlFormat('/box/record', _flag),
|
||||
'boxOrderApi': httpUrlFormat('/box/order', _flag),
|
||||
'boxOrderReviewApi': httpUrlFormat('/box/order/review', _flag),
|
||||
'boxOrderPaidApi': httpUrlFormat('/box/order/paid', _flag),
|
||||
'orderPaidApi': httpUrlFormat('/order/paid', _flag),
|
||||
'boxRecordResultApi': httpUrlFormat('/box/record/result', _flag),
|
||||
'boxDeliveryPreviewApi': httpUrlFormat('/order/box/delivery/preview', _flag),
|
||||
'boxDeliveryApi': httpUrlFormat('/order/box/delivery', _flag),
|
||||
'boxActivityApi': httpUrlFormat('/boxes/activity', _flag),//活动盲盒
|
||||
'boxActivityListApi': httpUrlFormat('/boxes/activity/list', _flag),//活动盲盒
|
||||
'promptCountApi': httpUrlFormat('/user/prompt/count', _flag),//用户提示卡数量
|
||||
'usePromptApi': httpUrlFormat('/box/order/use/prompt', _flag),//使用提示卡
|
||||
'activityDetailApi': httpUrlFormat('/boxes/activity/detail', _flag),//
|
||||
'activityDetailSourceApi': httpUrlFormat('/boxes/activity/detail/source', _flag),//
|
||||
'activityBuyApi': httpUrlFormat('/coin/gear/activity/box', _flag),//活动盲盒购买
|
||||
'activitySourceBuyApi': httpUrlFormat('/box/order/activity/source', _flag),//活动盲盒购买
|
||||
|
||||
'activityReviewApi': httpUrlFormat('/box/order/activity/review', _flag),//活动盲盒订单预览
|
||||
'activityReviewSourceApi': httpUrlFormat('/box/order/activity/review/source', _flag),//活动盲盒订单预览-新
|
||||
|
||||
'activityOrderApi': httpUrlFormat('/box/order/activity', _flag),
|
||||
'recoveryBatchApi': httpUrlFormat('/box/record/recovery/batch', _flag),
|
||||
'deliveryReviewBatchApi': httpUrlFormat('/order/box/delivery/preview/batch', _flag),
|
||||
'deliveryBatchApi': httpUrlFormat('/order/box/delivery/batch', _flag),
|
||||
'promptExchangeBatchApi': httpUrlFormat('/user/prompt/exchange/batch', _flag),
|
||||
'boxResultApi': httpUrlFormat('/misc/box/result', _flag),
|
||||
'noOpenListApi': httpUrlFormat('/box/record/no/open/list', _flag),//未开奖记录
|
||||
'openListApi': httpUrlFormat('/box/record/open', _flag),//开奖记录
|
||||
'boxesChannelPinnedApi': httpUrlFormat('/boxes/channel/pinned', _flag),//首页盲盒-渠道
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
'orderReceive': httpUrlFormat('/order/receive', _flag),
|
||||
'orderList': httpUrlFormat('/order/mall_order', _flag),
|
||||
'boxOrder': httpUrlFormat('/order/box_order', _flag),
|
||||
'boxRecord': httpUrlFormat('/order/box_record', _flag),
|
||||
'boxOpen': httpUrlFormat('/order/box_open', _flag),
|
||||
'boxPickup': httpUrlFormat('/order/box_pickup', _flag),
|
||||
'boxRecycle': httpUrlFormat('/order/box_recycle', _flag),
|
||||
'boxInfo': httpUrlFormat('/order/box_info', _flag),
|
||||
'orderCancel': httpUrlFormat('/order/cancel', _flag),
|
||||
'boxOpenByBoxId': httpUrlFormat('/order/box_open_by_box_id', _flag),
|
||||
|
||||
'popupAdPageApi': httpUrlFormat('/misc/popup/ad/page', _flag),
|
||||
'popupAdPageChannelApi': httpUrlFormat('/misc/popup/ad/page/channel', _flag),
|
||||
|
||||
|
||||
|
||||
'activityIconApi': httpUrlFormat('/activity/icon', _flag),
|
||||
'activityDetailNewApi': httpUrlFormat('/activity/detail', _flag),
|
||||
|
||||
|
||||
'pointClearApi': httpUrlFormat('/user/point/clear', _flag),
|
||||
'activityTaskAwardApi': httpUrlFormat('/activity/task/award', _flag),
|
||||
|
||||
|
||||
|
||||
|
||||
'activityTaskCreateApi': httpUrlFormat('/box/order/activity/create', _flag),
|
||||
'freeBoxApi': httpUrlFormat('/boxes/free/box', _flag),
|
||||
'activityGuideApi': httpUrlFormat('/activity/guide', _flag),
|
||||
|
||||
|
||||
'indexGuideApi': httpUrlFormat('/user/index/guide', _flag),
|
||||
|
||||
'activityTopLastApi': httpUrlFormat('/activity/top/last', _flag),
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export function boxRecoveryApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/box/record/recovery/${data.id}`, _flag),
|
||||
method: 'PUT',
|
||||
data
|
||||
})
|
||||
}
|
||||
//分页盲盒
|
||||
export function boxesApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxesApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//首页盲盒
|
||||
export function boxesPinnedApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.boxesPinnedApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒标签
|
||||
export function boxesTagsApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxesTagsApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
//盲盒开奖记录
|
||||
export function boxRecordApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxRecordApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 盲盒详情
|
||||
export function boxesDetail(data,header = {}) {
|
||||
const headerObj = {...headerVersion,...header}
|
||||
// header['API-Version'] = ApiVersion()
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/boxes/${data.id}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 创建盲盒订单
|
||||
export function boxOrderApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrderApi,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function boxOrderListApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrderApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 活动盲盒
|
||||
export function boxActivityApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxActivityApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 活动盲盒
|
||||
export function boxActivityListApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxActivityListApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 创建盲盒订单
|
||||
export function boxOrderReviewApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrderReviewApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 创建盲盒订单
|
||||
export function boxOrderPaidApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrderPaidApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function orderPaidApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderPaidApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 盲盒开奖结果
|
||||
export function boxRecordResultApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxRecordResultApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function boxDeliveryPreviewApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded','API-Version': ApiVersion()},
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.boxDeliveryPreviewApi,
|
||||
// url: httpUrlFormat(`/order/box/delivery/preview?boxRecordId=${data.boxRecordId}&quantity=${data.quantity}`, _flag),
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function boxDeliveryApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.boxDeliveryApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function boxesChannelPinnedApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxesChannelPinnedApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
//商城订单列表
|
||||
export function orderList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.order,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除订单
|
||||
export function delOrder(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.order,
|
||||
method: 'DELETE',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 取消订单
|
||||
export function orderCancel(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderCancel,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 确认收货
|
||||
export function orderReceive(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderReceive,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//创建商城订单
|
||||
export function createOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderList,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//订单预览
|
||||
export function orderPreview(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.orderPreview,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒订单列表
|
||||
export function boxOrderList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrder,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
//创建盲盒订单
|
||||
export function boxCreateOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrder,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//礼盒/物品列表
|
||||
export function boxRecord(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxRecord,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//开盲盒
|
||||
export function boxOpen(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
header: headerObj,
|
||||
url: apiUrl.boxOpen,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//提取盲盒
|
||||
export function boxPickup(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxPickup,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//回收盲盒
|
||||
export function boxRecycle(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.boxRecycle,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 订单详情
|
||||
export function orderDetail(no,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/order/${no}`, _flag),
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒详情
|
||||
export function boxInfo(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.boxInfo,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 根据盲盒id开盒
|
||||
export function boxOpenByBoxId(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.boxOpenByBoxId,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 用户提示卡数量
|
||||
export function promptCountApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.promptCountApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 使用提示卡
|
||||
export function usePromptApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.usePromptApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityDetailApi(data,header = {}) {
|
||||
// header['API-Version'] = ApiVersion()
|
||||
const headerObj = {...headerVersion,...header}
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
url: apiUrl.activityDetailApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityDetailSourceApi(data,header = {}) {
|
||||
// header['API-Version'] = ApiVersion()
|
||||
const headerObj = {...headerVersion,...header}
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
url: apiUrl.activityDetailSourceApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function activityBuyApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.activityBuyApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activitySourceBuyApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.activitySourceBuyApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function activityReviewApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.activityReviewApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityReviewSourceApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.activityReviewSourceApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityOrderApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.activityOrderApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function recoveryBatchApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.recoveryBatchApi,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function deliveryReviewBatchApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.deliveryReviewBatchApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function deliveryBatchApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.deliveryBatchApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function promptExchangeBatchApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.promptExchangeBatchApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function boxResultApi(data,header = {}) {
|
||||
const headerObj = {...headerVersion,...header}
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
url: apiUrl.boxResultApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function noOpenListApi(data,header = {}) {
|
||||
const headerObj = {...headerVersion,...header}
|
||||
// header['API-Version'] = ApiVersion()
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
url: apiUrl.noOpenListApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// export function openListApi(data,header = {}) {
|
||||
// // header['API-Version'] = ApiVersion()
|
||||
// const headerObj = {...headerVersion,...header}
|
||||
// return ajaxCarryAuthorization({
|
||||
// header:headerObj,
|
||||
// url: apiUrl.openListApi,
|
||||
// method: 'GET',
|
||||
// data: data
|
||||
// })
|
||||
// }
|
||||
export function openListApi(data,header = {}) {
|
||||
// header['API-Version'] = ApiVersion()
|
||||
const headerObj = {...headerVersion,...header}
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
url: httpUrlFormat(`/box/record/open?ids=${data}`, _flag),
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function popupAdPageApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerVersion,
|
||||
url: apiUrl.popupAdPageApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function popupAdPageChannelApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerVersion,
|
||||
url: apiUrl.popupAdPageChannelApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function activityIconApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.activityIconApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function activityDetailNewApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.activityDetailNewApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function pointClearApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.pointClearApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityTaskAwardApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.activityTaskAwardApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityTaskCreateApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.activityTaskCreateApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 免费开盲盒
|
||||
export function freeBoxApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.freeBoxApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function activityGuideApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.activityGuideApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function setActivityGuideApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.activityGuideApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function indexGuideApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.indexGuideApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function setIndexGuideApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.indexGuideApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function activityTopLastApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.activityTopLastApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'index': httpUrlFormat('/index', _flag),
|
||||
}
|
||||
|
||||
export function index(data) {
|
||||
return ajaxSync({
|
||||
header: headerVersion,
|
||||
url: apiUrl.index,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'generateCode': httpUrlFormat('/generate/code', _flag),//发送短信验证码
|
||||
'sms_check': httpUrlFormat('/sms_check', _flag),//验证短信验证码
|
||||
'register': httpUrlFormat('/register', _flag),//注册
|
||||
'login': httpUrlFormat('/login', _flag),//登录
|
||||
'loginCode': httpUrlFormat('/login/code', _flag),//验证码登录
|
||||
'wechatAppid': httpUrlFormat('/wechat/appid', _flag),//微信获取appid
|
||||
'loginWechat': httpUrlFormat('/login/wechat', _flag),//微信登录
|
||||
'phoneWechat': httpUrlFormat('/wechat/user/phone', _flag),//微信登录
|
||||
'reset_password': httpUrlFormat('/reset_password', _flag),//重置密码
|
||||
'bindPhoneApi': httpUrlFormat('/user/bind/phone', _flag),//绑定手机号
|
||||
|
||||
|
||||
|
||||
|
||||
'loginXc': httpUrlFormat('/login/xiaochuan', _flag),//用户登录第三方小川
|
||||
'agreementDetailApi': httpUrlFormat('/misc/user/agreement/detail', _flag),//用户协议
|
||||
|
||||
|
||||
|
||||
}
|
||||
export function loginXc(data) {
|
||||
return ajaxSync({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.loginXc,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function smsSend(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.generateCode,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function smsCheck(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.sms_check,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function register(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.register,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function loginCode(data) {
|
||||
return ajaxSync({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.loginCode,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function loginWechat(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.loginWechat,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function loginApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.login,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function resetPassword(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.reset_password,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function wechatAppid(data) {
|
||||
return ajaxSync({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.wechatAppid,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function phoneWechat(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxSync({
|
||||
header: headerObj,
|
||||
url: apiUrl.phoneWechat,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function bindPhoneApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.bindPhoneApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function agreementDetailApi(data) {
|
||||
return ajaxSync({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.agreementDetailApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'order': httpUrlFormat('/order', _flag),
|
||||
'orderPreview': httpUrlFormat('/order/preview', _flag),
|
||||
'orderReceive': httpUrlFormat('/order/receive', _flag),
|
||||
'orderList': httpUrlFormat('/order/mall_order', _flag),
|
||||
'boxOrder': httpUrlFormat('/order/box_order', _flag),
|
||||
'boxRecord': httpUrlFormat('/order/box_record', _flag),
|
||||
'boxOpen': httpUrlFormat('/order/box_open', _flag),
|
||||
'boxPickup': httpUrlFormat('/order/box_pickup', _flag),
|
||||
'boxRecycle': httpUrlFormat('/order/box_recycle', _flag),
|
||||
'boxInfo': httpUrlFormat('/order/box_info', _flag),
|
||||
'orderCancel': httpUrlFormat('/order/cancel', _flag),
|
||||
'boxOpenByBoxId': httpUrlFormat('/order/box_open_by_box_id', _flag),
|
||||
'orderRefundApi': httpUrlFormat('/order/refund', _flag),
|
||||
|
||||
|
||||
|
||||
|
||||
'channelGetApi': httpUrlFormat('/pay/channel/get', _flag),
|
||||
'orderReceiptApi': httpUrlFormat('/order/receipt', _flag),
|
||||
|
||||
'refundTimeApi': httpUrlFormat('/misc/mall/refund/expire/time', _flag),//商品退款-过期时间
|
||||
'boxRefundTimeApi': httpUrlFormat('/order/box/record/expire/time', _flag),//盲盒退款-过期时间
|
||||
'boxRefundTimeExpiredApi': httpUrlFormat('/order/box/record/expired', _flag),//盲盒退款-过期时间
|
||||
|
||||
}
|
||||
|
||||
//订单预览
|
||||
export function orderPreview(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
console.log(headerObj)
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.orderPreview,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function channelGetApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.channelGetApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function createShopOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.order,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//商城订单列表
|
||||
export function orderList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.order,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除订单
|
||||
export function delOrder(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.order,
|
||||
method: 'DELETE',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 取消订单
|
||||
export function orderCancel(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderCancel,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 确认收货
|
||||
export function orderReceive(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderReceive,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//创建商城订单
|
||||
export function createOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderList,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒订单列表
|
||||
export function boxOrderList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrder,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
//创建盲盒订单
|
||||
export function boxCreateOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrder,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//礼盒/物品列表
|
||||
export function boxRecord(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxRecord,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//开盲盒
|
||||
export function boxOpen(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOpen,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//提取盲盒
|
||||
export function boxPickup(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxPickup,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//回收盲盒
|
||||
export function boxRecycle(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxRecycle,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 订单详情
|
||||
export function orderDetail(no,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/order/no/${no}`, _flag),
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function orderIdDetail(id) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/box/order/${id}`, _flag),
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
export function orderDetailId(id) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/order/${id}`, _flag),
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
//盲盒详情
|
||||
export function boxInfo(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.boxInfo,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 根据盲盒id开盒
|
||||
export function boxOpenByBoxId(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOpenByBoxId,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function orderReceiptApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderReceiptApi,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function orderRefundApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderRefundApi,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 商品退款过期时间
|
||||
export function refundTimeApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
url: apiUrl.refundTimeApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 盲盒退款过期时间
|
||||
export function boxRefundTimeApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
url: apiUrl.boxRefundTimeApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function boxRefundTimeExpiredApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
url: apiUrl.boxRefundTimeExpiredApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'createOrderPay': httpUrlFormat('/pay/create_order', _flag),
|
||||
'orderPaid': httpUrlFormat('/order/box_order_paid', _flag),
|
||||
'orderShopPaid': httpUrlFormat('/order/order_paid', _flag),
|
||||
'mpScheme': httpUrlFormat('/other/wechat/mp/scheme', _flag),
|
||||
|
||||
}
|
||||
|
||||
export function createOrderPay(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.createOrderPay,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询盲盒订单是否成功
|
||||
export function orderPaid(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderPaid,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 查询商品订单是否成功
|
||||
export function orderShopPaid(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderShopPaid,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// https://test.06zk.com/test/pay/create_order
|
||||
|
||||
export function mpScheme(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.mpScheme,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'productApi': httpUrlFormat('/product', _flag),
|
||||
'productTagsApi': httpUrlFormat('/product/tags', _flag),
|
||||
'boxesTagsApi': httpUrlFormat('/boxes/tags', _flag),
|
||||
'boxesApi': httpUrlFormat('/boxes', _flag),
|
||||
'favorItemApi': httpUrlFormat('/user/favor/item', _flag),
|
||||
'favorItemProductApi': httpUrlFormat('/user/favor/item/product', _flag),
|
||||
|
||||
|
||||
'orderPreview': httpUrlFormat('/order/preview', _flag),
|
||||
'orderReceive': httpUrlFormat('/order/receive', _flag),
|
||||
'orderList': httpUrlFormat('/order/mall_order', _flag),
|
||||
'boxOrder': httpUrlFormat('/order/box_order', _flag),
|
||||
'boxRecord': httpUrlFormat('/order/box_record', _flag),
|
||||
'boxOpen': httpUrlFormat('/order/box_open', _flag),
|
||||
'boxPickup': httpUrlFormat('/order/box_pickup', _flag),
|
||||
'boxRecycle': httpUrlFormat('/order/box_recycle', _flag),
|
||||
'boxInfo': httpUrlFormat('/order/box_info', _flag),
|
||||
'orderCancel': httpUrlFormat('/order/cancel', _flag),
|
||||
'boxOpenByBoxId': httpUrlFormat('/order/box_open_by_box_id', _flag),
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//商品标签
|
||||
export function productTagsApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.productTagsApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//商品分页
|
||||
export function productApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.productApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒标签
|
||||
export function boxesTagsApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxesTagsApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function productDetailApi(data,header = {}) {
|
||||
const headerObj = {...header,...headerVersion}
|
||||
return ajaxCarryAuthorization({
|
||||
header:headerObj,
|
||||
url: httpUrlFormat(`/product/${data.id}`, _flag),
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 收藏
|
||||
export function favorItemApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.favorItemApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除收藏
|
||||
export function delFavorItemApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.favorItemProductApi,
|
||||
method: 'DELETE',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//商城订单列表
|
||||
export function orderList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.order,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除订单
|
||||
export function delOrder(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.order,
|
||||
method: 'DELETE',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 取消订单
|
||||
export function orderCancel(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderCancel,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 确认收货
|
||||
export function orderReceive(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: apiUrl.orderReceive,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//创建商城订单
|
||||
export function createOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderList,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//订单预览
|
||||
export function orderPreview(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.orderPreview,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒订单列表
|
||||
export function boxOrderList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrder,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
//创建盲盒订单
|
||||
export function boxCreateOrder(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxOrder,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//礼盒/物品列表
|
||||
export function boxRecord(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxRecord,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//开盲盒
|
||||
export function boxOpen(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
header: headerObj,
|
||||
url: apiUrl.boxOpen,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//提取盲盒
|
||||
export function boxPickup(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxPickup,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//回收盲盒
|
||||
export function boxRecycle(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.boxRecycle,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 订单详情
|
||||
export function orderDetail(no,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/order/${no}`, _flag),
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//盲盒详情
|
||||
export function boxInfo(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.boxInfo,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 根据盲盒id开盒
|
||||
export function boxOpenByBoxId(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.boxOpenByBoxId,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'topStatusApi': httpUrlFormat('/top/status', _flag),
|
||||
}
|
||||
|
||||
|
||||
export function topUserListApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/user/list/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function topUserSelfApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/user/self/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function topStatusApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.topStatusApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function topUserListLastApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/user/list/last/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function topUserSelfLastApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/user/self/last/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function topRuleApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/rule/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function topExpiredTimeApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/expired/time/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function topDetailApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/top/${data.type}`, _flag),
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'reportHeartbeat': httpUrlFormat('/report/heartbeat', _flag),
|
||||
'reportClick': httpUrlFormat('/report/click', _flag),
|
||||
|
||||
|
||||
}
|
||||
|
||||
//心跳埋点
|
||||
export function reportHeartbeatApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.reportHeartbeat,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//点击埋点
|
||||
export function reportClickApi(data) {
|
||||
// const headerObj = {'Content-Type': 'application/x-www-form-urlencoded',...headerVersion}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.reportClick,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'product': httpUrlFormat('/product', _flag),//商品分页
|
||||
'categories': httpUrlFormat('/product/categories', _flag),//商品分类
|
||||
'rollingImgs': httpUrlFormat('/rolling_imgs', _flag),//商品分类
|
||||
'favorItem': httpUrlFormat('/user/favor_item', _flag),//收藏
|
||||
'deliveryFee': httpUrlFormat('/product/delivery_fee', _flag),//运费
|
||||
'favorItemProduct': httpUrlFormat('/user/favor_item_product', _flag),//运费
|
||||
'searchHistoryRecommend': httpUrlFormat('/search_history/recommend', _flag),//搜索推荐
|
||||
'searchHistoryPersonal': httpUrlFormat('/search/history/personal', _flag),//搜索推荐
|
||||
'orderBoxRecordCount': httpUrlFormat('/order/box_record_count', _flag),//盲盒统计
|
||||
'carriageRule': httpUrlFormat('/misc/carriage/rule', _flag),
|
||||
'carriageGetRule': httpUrlFormat('/misc/carriage/get', _flag),
|
||||
'tagsChannelApi': httpUrlFormat('/product/tags/channel', _flag),
|
||||
'productChannelApi': httpUrlFormat('/product/channel', _flag),
|
||||
'productRecommendChannel': httpUrlFormat('/product/product/recommend/channel', _flag),
|
||||
'productChannelPointApi': httpUrlFormat('/product/channel/points', _flag),
|
||||
|
||||
'tagsPointApi': httpUrlFormat('/misc/product/points/tag', _flag),
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 渠道商品tag
|
||||
export function tagsChannelApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.tagsChannelApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 渠道商品
|
||||
export function productChannelApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.productChannelApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function productRecommendChannel(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.productRecommendChannel,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 渠道商品积分筛选
|
||||
export function productChannelPointApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.productChannelPointApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 运费
|
||||
export function deliveryFee() {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.deliveryFee,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
export function product(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.product,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 商品分类
|
||||
export function categories(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.categories,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 商品详情
|
||||
export function productDetail(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/product/${data}`, _flag),
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
// 商城轮播
|
||||
export function rollingImgs(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.rollingImgs,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
// 收藏列表
|
||||
export function favorItemList(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.favorItem,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 收藏
|
||||
export function favorItem(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.favorItem,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除收藏
|
||||
export function delFavorItem(ids) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: httpUrlFormat(`/user/favor_item${ids}`, _flag),
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
// 删除商品详情收藏
|
||||
export function favorItemProduct(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// url: apiUrl.cart_item,
|
||||
url: httpUrlFormat(`/user/favor_item_product${data}`, _flag),
|
||||
method: 'DELETE',
|
||||
// data: data
|
||||
})
|
||||
}
|
||||
// 搜索推荐
|
||||
export function searchHistoryRecommend(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.searchHistoryRecommend,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 搜索历史
|
||||
export function searchHistoryPersonal(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.searchHistoryPersonal,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 删除搜索历史
|
||||
export function delSearchHistoryPersonal() {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.searchHistoryPersonal,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
// 盲盒统计
|
||||
export function orderBoxRecordCount(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.orderBoxRecordCount,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function carriageRule(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.carriageRule,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function carriageGetRule(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.carriageGetRule,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function tagsPointApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.tagsPointApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'signInInfo': httpUrlFormat('/user/sign/in/info', _flag),//用户签到信息
|
||||
'signIn': httpUrlFormat('/user/sign/in', _flag),//用户签到
|
||||
'taskList': httpUrlFormat('/task/list', _flag),//任务列表
|
||||
|
||||
|
||||
}
|
||||
|
||||
export function signInInfo(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.signInInfo,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function signIn(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.signIn,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function taskListApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: apiUrl.taskList,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function taskReceive(taskId) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/task/receive/${taskId}`, _flag),
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'userInfo': httpUrlFormat('/user/info', _flag),//用户信息
|
||||
'userPoint': httpUrlFormat('/user/point', _flag),//
|
||||
'favorItemApi': httpUrlFormat('/user/favor/item', _flag),//
|
||||
'userAddress': httpUrlFormat('/user/address', _flag),//收获地址
|
||||
'boxCount': httpUrlFormat('/user/box_count', _flag),//
|
||||
'notice': httpUrlFormat('/misc/notice', _flag),//公告
|
||||
'noticeTopApi': httpUrlFormat('/misc/notice/top', _flag),//置顶公告
|
||||
'uploadToken': httpUrlFormat('/user/upload_token', _flag),//上传oss凭证
|
||||
'feedback': httpUrlFormat('/user/feedback', _flag),//意见反馈
|
||||
'customer': httpUrlFormat('/misc/customer', _flag),//
|
||||
'popupAdApi': httpUrlFormat('/misc/popup/ad', _flag),//
|
||||
'couponApi': httpUrlFormat('/coupon', _flag),//优惠券
|
||||
'userCouponApi': httpUrlFormat('/user/conpon', _flag),//用户优惠券
|
||||
'userCouponBatchApi': httpUrlFormat('/user/conpon/reward/batch', _flag),//用户优惠券
|
||||
|
||||
'userCouponRewardApi': httpUrlFormat('/user/conpon/reward', _flag),//兑换优惠券
|
||||
'coinGearApi': httpUrlFormat('/coin/gear', _flag),//代币充值
|
||||
'coinGearRechargeApi': httpUrlFormat('/coin/gear/recharge', _flag),//代币充值订单
|
||||
'payMethodApi': httpUrlFormat('/misc/pay/method', _flag),//获取支付方式
|
||||
'coinPaidApi': httpUrlFormat('/coin/gear/recharge/paid', _flag),//
|
||||
'couponCountApi': httpUrlFormat('/user/coupon/count', _flag),//
|
||||
'miscPromptApi': httpUrlFormat('/misc/prompt', _flag),//
|
||||
'shareApi': httpUrlFormat('/misc/build/share', _flag),//
|
||||
'updateNickNameApi': httpUrlFormat('/user/update/nickname', _flag),//更新用户昵称
|
||||
|
||||
|
||||
|
||||
|
||||
'recordCountApi': httpUrlFormat('/user/box/record/count', _flag),//用户盲盒开奖结果数量
|
||||
'aliyunUploadTokenApi': httpUrlFormat('/misc/aliyun_upload_token', _flag),//获取oss上传凭证
|
||||
'updateAvatarApi': httpUrlFormat('/user/update/avatar', _flag),//更新头像
|
||||
'versionControlApi': httpUrlFormat('/misc/version/control', _flag),//版本控制
|
||||
'userPromptListApi': httpUrlFormat('/user/prompt', _flag),//版本控制
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 更新用户昵称
|
||||
export function updateNickNameApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.updateNickNameApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function favorItemDelApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.favorItemApi,
|
||||
method: 'DELETE',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function favorItemApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.favorItemApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function recordCountApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.recordCountApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function userPoint(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.userPoint,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function userInfo(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.userInfo,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 收货地址列表
|
||||
export function userAddress(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.userAddress,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 新增收货地址
|
||||
export function addAddress(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.userAddress,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 收货地址详情
|
||||
export function addressDetail(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/user/address/${data}`, _flag),
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
// 编辑收货地址
|
||||
export function editAddress(id,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/user/address/${id}`, _flag),
|
||||
method: 'PUT',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
export function delAddress(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/user/address?ids=${data.id}`, _flag),
|
||||
method: 'DELETE',
|
||||
data,data
|
||||
})
|
||||
}
|
||||
|
||||
// 礼盒物品数量
|
||||
export function boxCount(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.boxCount,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
// 系统公告
|
||||
export function notice(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.notice,
|
||||
method: 'GET',
|
||||
data,data
|
||||
})
|
||||
}
|
||||
// 系统公告详情
|
||||
export function noticeDetail(id,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/misc/notice/${id}`, _flag),
|
||||
method: 'GET',
|
||||
data,data
|
||||
})
|
||||
}
|
||||
// 系统公告详情
|
||||
export function uploadToken(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.uploadToken,
|
||||
method: 'GET',
|
||||
data,data
|
||||
})
|
||||
}
|
||||
// 意见反馈
|
||||
export function feedback(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.feedback,
|
||||
method: 'POST',
|
||||
data,data
|
||||
})
|
||||
}
|
||||
export function customerApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.customer,
|
||||
method: 'GET',
|
||||
data,data
|
||||
})
|
||||
}
|
||||
export function noticeTopApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.noticeTopApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function popupAdApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.popupAdApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function couponApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.couponApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function userCouponApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.userCouponApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function userCouponRewardApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.userCouponRewardApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function coinGearApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.coinGearApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function payMethodApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.payMethodApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function coinGearRechargeApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.coinGearRechargeApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function coinPaidApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.coinPaidApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function userCouponBatchApi(userId,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: httpUrlFormat(`/user/conpon/reward/batch?userId=${userId}`, _flag),
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function couponCountApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.couponCountApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function miscPromptApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.miscPromptApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function shareApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.shareApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function aliyunUploadTokenApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.aliyunUploadTokenApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function updateAvatarApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.updateAvatarApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function versionControlApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.versionControlApi,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function userPromptListApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.userPromptListApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import {
|
||||
httpUrlFormat,
|
||||
ajaxCarryAuthorization,ajaxSync
|
||||
} from '@/utils/httpUtils.js'
|
||||
|
||||
import { ApiVersion,AppVersion } from '@/config/baseConfig.js'
|
||||
|
||||
const headerVersion = {
|
||||
'API-Version': ApiVersion(),
|
||||
'APP-Version':AppVersion()
|
||||
}
|
||||
|
||||
const _flag = 'apiBaseUrl' // 请求的api空间
|
||||
|
||||
const apiUrl = {
|
||||
'virtualOrderPreviewApi': httpUrlFormat('/virtual/order/preview', _flag),//商城订单预览
|
||||
'virtualOrderBoxPreviewApi': httpUrlFormat('/virtual/order/box/delivery/preview', _flag),//盲盒订单预览
|
||||
'virtualOrderApi': httpUrlFormat('/virtual/order/box/delivery', _flag),
|
||||
'virtualOrderPaidApi': httpUrlFormat('/virtual/order/paid', _flag),
|
||||
'virtualOrderListApi': httpUrlFormat('/virtual/order', _flag),
|
||||
}
|
||||
|
||||
|
||||
export function virtualShopOrderApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.virtualOrderListApi,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function virtualOrderPreviewApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.virtualOrderPreviewApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function virtualOrderBoxPreviewApi(data) {
|
||||
const headerObj = {...headerVersion,'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerObj,
|
||||
url: apiUrl.virtualOrderBoxPreviewApi,
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 创建订单
|
||||
export function virtualOrderApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.virtualOrderApi,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function virtualOrderPaidApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.virtualOrderPaidApi,
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function virtualOrderListApi(data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
// header: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
url: apiUrl.virtualOrderListApi,
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function virtualOrderDetailId(id) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/virtual/order/${id}`, _flag),
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
export function virtualOrderDetail(no,data) {
|
||||
return ajaxCarryAuthorization({
|
||||
header: headerVersion,
|
||||
url: httpUrlFormat(`/virtual/order/no/${no}`, _flag),
|
||||
method: 'GET',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<script>
|
||||
import {uuid} from '@/utils/jsUtils.js'
|
||||
import { reportHeartbeatApi,reportClickApi } from '@/API/report.js'
|
||||
export default {
|
||||
globalData: {
|
||||
|
||||
},
|
||||
onLaunch: function() {
|
||||
// console.log("我加载咯")
|
||||
// // 获取当前app的版本
|
||||
// const systemInfo = uni.getSystemInfoSync();
|
||||
// // 应用程序版本号
|
||||
// // 条件编译,只在H5渲染
|
||||
// // #ifdef H5
|
||||
// console.log(systemInfo.appVersion,'版本号');
|
||||
// // #endif
|
||||
// console.log(systemInfo.appVersion,'版本号');
|
||||
// let version = systemInfo.appVersion ? systemInfo.appVersion : '0.0.1'
|
||||
uni.setStorageSync('activityShow', false)
|
||||
uni.setStorageSync('firstTime', false),//是否第一次打开
|
||||
reportHeartbeatApi().then(res => {})
|
||||
setInterval(() => {
|
||||
reportHeartbeatApi().then(res => {
|
||||
console.log("我是三十秒的定时器被调用了:",res)
|
||||
})
|
||||
},30000)
|
||||
},
|
||||
onShow: function() {
|
||||
// console.log('App Show')
|
||||
uni.setStorageSync('ad','frist')
|
||||
},
|
||||
onHide: function() {
|
||||
// console.log('App Hide')
|
||||
// uni.removeStorageSync('ad')
|
||||
uni.setStorageSync('ad','frist')
|
||||
uni.removeStorageSync('activityShow')
|
||||
},
|
||||
onUnload:function() {
|
||||
uni.setStorageSync('ad','frist')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/*每个页面公共css */
|
||||
/* #ifndef APP-NVUE */
|
||||
@import url("static/app.css");
|
||||
/* #endif */
|
||||
@font-face {
|
||||
font-family: YouSheBiaoTiHei;
|
||||
src: url('https://box-mall.oss-cn-hangzhou.aliyuncs.com/static/font/YouSheBiaoTiHei.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: Zhxs;
|
||||
src: url('./static/zhxs.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: Impacts;
|
||||
src: url('https://box-mall.oss-cn-hangzhou.aliyuncs.com/static/font/impact-2.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: ZhanKuQingKeHuangYouTi;
|
||||
src: url('https://box-mall.oss-cn-hangzhou.aliyuncs.com/static/font/ZhanKuQingKeHuangYouTi-2.ttf');
|
||||
}
|
||||
.no-data{
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
padding: 10rpx 0 30rpx 0;
|
||||
}
|
||||
|
||||
|
||||
uni-swiper .uni-swiper-dot{
|
||||
width: 5rpx;
|
||||
height: 5rpx;
|
||||
background: rgba(16,16,16,0.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
uni-swiper .uni-swiper-dot-active{
|
||||
width: 16rpx;
|
||||
height: 5rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
FROM nginx
|
||||
EXPOSE 80
|
||||
COPY /unpackage/dist/build/h5 /usr/share/nginx/html
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# 盲盒H5基础版
|
||||
## 登录
|
||||
```bash
|
||||
1.登录页面('账号密码登录、验证码登录')
|
||||
```
|
||||
|
||||
|
||||
## 首页
|
||||
```bash
|
||||
1.对接首页盲盒
|
||||
2.盲盒标签以及标签下的盲盒
|
||||
3.盲盒搜索
|
||||
4.对接置顶公告
|
||||
5.广告弹窗、轮播
|
||||
7.对接盲盒详情
|
||||
8.提示卡('页面完成没有对接功能')
|
||||
9.购买盲盒订单预览页面
|
||||
10.支付('暂未对接支付通道')、购买成功页面
|
||||
11.开奖页面('暂无动画')
|
||||
```
|
||||
## 商城
|
||||
```bash
|
||||
1.商场首页('轮播未完成')
|
||||
2.商场搜索,搜索历史
|
||||
3.商品详情
|
||||
4.商品收藏/取消收藏
|
||||
5.购买提交订单
|
||||
6.购买成功页
|
||||
```
|
||||
## 签到
|
||||
```bash
|
||||
1.页面完成 暂未对接
|
||||
```
|
||||
|
||||
|
||||
## 个人中心
|
||||
```bash
|
||||
1.个人中心已完成
|
||||
2.设置页面('更换头像、修改昵称、关于我们暂未对接')
|
||||
3.隐私政策、用户协议页面完成 暂未更新内容
|
||||
4.优惠券、更多优惠券页面完成 暂未对接
|
||||
5.积分使用记录已完成
|
||||
6.物品页面完成 发货 回收接口已完成('发货回收接口需改成批量 页面已完成暂未对接')
|
||||
7.订单详情已完成(订单详情、确认收货、查看物流)
|
||||
8.盲盒订单已完成
|
||||
9.地址管理已完成(新增地址、设置默认地址)
|
||||
10.我的收藏已完成(取消收藏)
|
||||
```
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="flex a-center content" v-if="lineData">
|
||||
<view><slot name="content"></slot></view>
|
||||
</view>
|
||||
<view class="flex a-center" style="padding-right: 0;">
|
||||
<view class="progress-container" id="container" ref="progressContainer" :style="{ background: inBgColor }">
|
||||
<view
|
||||
class="progress-content flex j-end"
|
||||
id="content"
|
||||
ref="progressContent"
|
||||
:style="{ height: strokeWidth + 'px', background: bgColor, width: contentWidth, transition: `width ${duration / 1000}s ease` }"
|
||||
v-if="isAnimate"
|
||||
>
|
||||
<view class="textInside flex a-center j-center" v-if="textInside && !noData">
|
||||
<view>{{ percentage }}%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!isAnimate" class="progress-content flex j-end" :style="{ width: percentage + '%', height: strokeWidth + 'px', background: bgColor }">
|
||||
<view class="textInside flex a-center j-center" v-if="textInside && !noData">
|
||||
<view class="text">{{ percentage }}%</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<!-- <view class="percentage" v-if="!textInside && !lineData && !noData && !isAnimate">{{ percentage }}%</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiProgress',
|
||||
components: {},
|
||||
props: {
|
||||
// 进度条的值
|
||||
percentage: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
// 是否内联显示数据
|
||||
textInside: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 进度条高度
|
||||
strokeWidth: {
|
||||
type: [Number, String],
|
||||
default: 6
|
||||
},
|
||||
// 默认动画时长
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 2000
|
||||
},
|
||||
// 是否有动画
|
||||
isAnimate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#15B9D0'
|
||||
},
|
||||
// 是否不显示数据
|
||||
noData: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否自定义显示内容
|
||||
lineData: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 自定义底色
|
||||
inBgColor: {
|
||||
type: String,
|
||||
default: '#DCDADA'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
timer: null,
|
||||
containerWidth: 0,
|
||||
contentWidth: 0
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
if (this.isAnimate) {
|
||||
// #ifdef H5
|
||||
this.$nextTick(() => {
|
||||
let progressContainer = this.$refs.progressContainer.$el;
|
||||
let progressContent = this.$refs.progressContent.$el;
|
||||
let style = window.getComputedStyle(progressContainer, null);
|
||||
let width = style.width.replace('px', '') * ((this.percentage * 1) / 100);
|
||||
progressContent.style.width = width.toFixed(2) + 'px';
|
||||
progressContent.style.transition = `width ${this.duration / 1000}s ease`;
|
||||
});
|
||||
// #endif
|
||||
const container = uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
.selectAll('#container');
|
||||
const content = uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
.selectAll('#content');
|
||||
container.boundingClientRect().exec(res1 => {
|
||||
this.contentWidth = res1[0][0].width * 1 * ((this.percentage * 1) / 100).toFixed(2) + 'px';
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.start();
|
||||
});
|
||||
},
|
||||
created() {},
|
||||
filters: {},
|
||||
computed: {},
|
||||
watch: {},
|
||||
directives: {}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.content {
|
||||
margin-bottom: 10rpx;
|
||||
.c-per {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
border-radius: 100rpx;
|
||||
.progress-content {
|
||||
border-radius: 100rpx;
|
||||
width: 0;
|
||||
}
|
||||
.textInside {
|
||||
color: #fff;
|
||||
margin-right: 10rpx;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
.percentage {
|
||||
margin-left: 6rpx;
|
||||
font-size: 12rpx;
|
||||
width: 30rpx;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.a-center {
|
||||
align-items: center;
|
||||
}
|
||||
.j-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.j-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.content {
|
||||
margin-bottom: 10rpx;
|
||||
color: #666;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<!-- 底部固定布局组件 -->
|
||||
<template>
|
||||
<view class="BottomFixed" :style="[styleCom]">
|
||||
<view class="BottomNavBarFixed" :style="{height: barHeightCom, bottom: fixedBottomCom, 'background-color': bgColor}">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BottomFixed',
|
||||
props: {
|
||||
// 高度
|
||||
barHeight: {
|
||||
type: [String, Number],
|
||||
default: 60
|
||||
},
|
||||
// 单位
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'px'
|
||||
},
|
||||
// 背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 组件外层元素是否需要高度
|
||||
pHasH: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 固定偏移距离
|
||||
fixedBottom: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleCom () {
|
||||
if (this.pHasH) {
|
||||
return {
|
||||
height: `${this.barHeight}${this.unit}`
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
barHeightCom () {
|
||||
return `${this.barHeight}${this.unit}`
|
||||
},
|
||||
fixedBottomCom () {
|
||||
return `${this.fixedBottom}${this.unit}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.BottomFixed {
|
||||
position: relative;
|
||||
|
||||
.BottomNavBarFixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<!-- 引导页组件 -->
|
||||
<template>
|
||||
<view class="codeElfGuide">
|
||||
<swiper
|
||||
class="swiperView"
|
||||
indicator-color="rgba(255, 255, 255, .5)"
|
||||
indicator-active-color="#fff"
|
||||
:indicator-dots="dotsShow"
|
||||
:autoplay="autoplay"
|
||||
:duration="duration"
|
||||
@change="swiperChange">
|
||||
<template v-for="item in guideList">
|
||||
<swiper-item :key="item.id">
|
||||
<view class="swiperItemView" :style="{ 'background-color': item.bg }">
|
||||
<image class="imgView" :src="item.img" mode="aspectFit"></image>
|
||||
<view class="titleView">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
<view class="dec">{{ item.dec }}</view>
|
||||
</view>
|
||||
<view v-if="(item.id + 1) === guideList.length" class="actBtnView">
|
||||
<virtual-button style="width: 300rpx;" btnHeight="80" type="376AF5" shape="round" @click="launchFlag">
|
||||
<view style="font-size: 28rpx;">{{ experience }}</view>
|
||||
</virtual-button>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</template>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CodeElfGuide',
|
||||
data() {
|
||||
return {
|
||||
autoplay: false,
|
||||
duration: 500,
|
||||
dotsShow: true, // 指示点是否显示
|
||||
experience: '立即体验',
|
||||
guideList: [
|
||||
{ id: 0, img: '/static/components/guidePages/2.png', bg: '#00022D', title: '专业行情机制', dec: '实时更新行业动态数据 及时把握行情趋势' },
|
||||
{ id: 1, img: '/static/components/guidePages/3.1.png', bg: '#171C30', title: '账户安全模式', dec: '实时更新行业动态数据 及时把握行情趋势' },
|
||||
{ id: 2, img: '/static/components/guidePages/1.png', bg: '#000000', title: '去中心化交易', dec: '实时更新行业动态数据 及时把握行情趋势' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
launchFlag: function(){
|
||||
/**
|
||||
* 向本地存储中设置launchFlag的值,即启动标识;
|
||||
*/
|
||||
uni.setStorage({
|
||||
key: 'launchFlag',
|
||||
data: true,
|
||||
});
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
},
|
||||
swiperChange (e) {
|
||||
const _current = e.detail.current
|
||||
if (_current === 2) {
|
||||
this.dotsShow = false
|
||||
} else {
|
||||
this.dotsShow = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.codeElfGuide,
|
||||
.swiperView {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.swiperView {
|
||||
// 覆盖样式
|
||||
/deep/ .uni-swiper-dots.uni-swiper-dots-horizontal {
|
||||
bottom: 90rpx;
|
||||
}
|
||||
|
||||
.swiperItemView {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.imgView {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.titleView {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 360rpx;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
.title {
|
||||
font-size: 48rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.dec {
|
||||
margin-top: 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: #CECECE;
|
||||
}
|
||||
}
|
||||
|
||||
.actBtnView {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 90rpx;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<!-- 自定义radio组件 -->
|
||||
<template>
|
||||
<view class="CustomRadioItem" @click="changeCheckHandle">
|
||||
<image v-if="isAnonymous" src="@/static/components/customRadioItem/goods_checkbox_pressed.png" mode="" class="anonymity-img"></image>
|
||||
<image v-if="!isAnonymous" src="@/static/components/customRadioItem/goods_checkbox_normal.png" mode="" class="anonymity-img"></image>
|
||||
<slot>
|
||||
<text>{{ radioTitle }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CustomRadioItem',
|
||||
props: {
|
||||
radioTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否选中
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'checked',
|
||||
event: 'change'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isAnonymous: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
checked: function (nVal) {
|
||||
this.isAnonymous = nVal
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.isAnonymous = this.checked
|
||||
},
|
||||
updated() {
|
||||
this.isAnonymous = this.checked
|
||||
},
|
||||
methods: {
|
||||
changeCheckHandle () {
|
||||
this.isAnonymous = !this.isAnonymous
|
||||
this.$emit('change', this.isAnonymous);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.CustomRadioItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: #564F5F;
|
||||
|
||||
.anonymity-img{
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<!-- empty空数据提示组件 -->
|
||||
<template>
|
||||
<view class="empty" :style="styleObj">
|
||||
<view v-if="emptyImg.length > 0" class="emptyImgView">
|
||||
<image class="emptyImg" :src="emptyImg" mode=""></image>
|
||||
</view>
|
||||
<view class="emptyTitle">
|
||||
{{ title }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Empty',
|
||||
props: {
|
||||
// 提示文本
|
||||
title: {
|
||||
type: String,
|
||||
default: '暂无数据'
|
||||
},
|
||||
// 提示图片路径
|
||||
emptyImg: {
|
||||
type: String,
|
||||
default: '/static/new/mine/empty.png'
|
||||
},
|
||||
// 自定义样式
|
||||
styleObj: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.empty {
|
||||
.emptyImgView {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.emptyImg {
|
||||
width: 456rpx;
|
||||
height: 262rpx;
|
||||
}
|
||||
}
|
||||
.emptyTitle {
|
||||
padding-top: 41rpx;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 20rpx;
|
||||
color: #888888;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
<template>
|
||||
<view :style="[customStyle]" class="u-icon" @tap="click" :class="['u-icon--' + labelPos]">
|
||||
<image class="u-icon__img" v-if="isImg" :src="name" :mode="imgMode" :style="[imgStyle]"></image>
|
||||
<text v-else class="u-icon__icon" :class="customClass" :style="[iconStyle]" :hover-class="hoverClass" @touchstart="touchstart"></text>
|
||||
<text v-if="label" class="u-icon__label" :style="{
|
||||
color: labelColor,
|
||||
fontSize: addUnit(labelSize),
|
||||
marginLeft: labelPos == 'right' ? addUnit(marginLeft) : 0,
|
||||
marginTop: labelPos == 'bottom' ? addUnit(marginTop) : 0,
|
||||
marginRight: labelPos == 'left' ? addUnit(marginRight) : 0,
|
||||
marginBottom: labelPos == 'top' ? addUnit(marginBottom) : 0,
|
||||
}">{{label}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* icon 图标
|
||||
* @description 基于字体的图标集,包含了大多数常见场景的图标。
|
||||
* @tutorial https://www.uviewui.com/components/icon.html
|
||||
* @property {String} name 图标名称,见示例图标集
|
||||
* @property {String} color 图标颜色(默认inherit)
|
||||
* @property {String | Number} size 图标字体大小,单位rpx(默认32)
|
||||
* @property {String | Number} label-size label字体大小,单位rpx(默认28)
|
||||
* @property {String} label 图标右侧的label文字(默认28)
|
||||
* @property {String} label-pos label文字相对于图标的位置,只能right或bottom(默认right)
|
||||
* @property {String} label-color label字体颜色(默认#606266)
|
||||
* @property {Object} custom-style icon的样式,对象形式
|
||||
* @property {String} custom-prefix 自定义字体图标库时,需要写上此值
|
||||
* @property {String | Number} margin-left label在右侧时与图标的距离,单位rpx(默认6)
|
||||
* @property {String | Number} margin-top label在下方时与图标的距离,单位rpx(默认6)
|
||||
* @property {String | Number} margin-bottom label在上方时与图标的距离,单位rpx(默认6)
|
||||
* @property {String | Number} margin-right label在左侧时与图标的距离,单位rpx(默认6)
|
||||
* @property {String} label-pos label相对于图标的位置,只能right或bottom(默认right)
|
||||
* @property {String} index 一个用于区分多个图标的值,点击图标时通过click事件传出
|
||||
* @property {String} hover-class 图标按下去的样式类,用法同uni的view组件的hover-class参数,详情见官网
|
||||
* @property {String} width 显示图片小图标时的宽度
|
||||
* @property {String} height 显示图片小图标时的高度
|
||||
* @property {String} top 图标在垂直方向上的定位
|
||||
* @event {Function} click 点击图标时触发
|
||||
* @example <icon-view name="photo" color="#2979ff" size="28"></icon-view>
|
||||
*/
|
||||
export default {
|
||||
name: 'iconView',
|
||||
props: {
|
||||
// 图标类名
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 图标颜色,可接受主题色
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 字体大小,单位rpx
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 'inherit'
|
||||
},
|
||||
// 是否显示粗体
|
||||
bold: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
|
||||
index: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 触摸图标时的类名
|
||||
hoverClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 自定义扩展前缀,方便用户扩展自己的图标库
|
||||
customPrefix: {
|
||||
type: String,
|
||||
default: 'uicon'
|
||||
},
|
||||
// 图标右边或者下面的文字
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// label的位置,只能右边或者下边
|
||||
labelPos: {
|
||||
type: String,
|
||||
default: 'right'
|
||||
},
|
||||
// label的大小
|
||||
labelSize: {
|
||||
type: [String, Number],
|
||||
default: '28'
|
||||
},
|
||||
// label的颜色
|
||||
labelColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// label与图标的距离(横向排列)
|
||||
marginLeft: {
|
||||
type: [String, Number],
|
||||
default: '6'
|
||||
},
|
||||
// label与图标的距离(竖向排列)
|
||||
marginTop: {
|
||||
type: [String, Number],
|
||||
default: '6'
|
||||
},
|
||||
// label与图标的距离(竖向排列)
|
||||
marginRight: {
|
||||
type: [String, Number],
|
||||
default: '6'
|
||||
},
|
||||
// label与图标的距离(竖向排列)
|
||||
marginBottom: {
|
||||
type: [String, Number],
|
||||
default: '6'
|
||||
},
|
||||
// 图片的mode
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'widthFix'
|
||||
},
|
||||
// 自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 用于显示图片小图标时,图片的宽度
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 用于显示图片小图标时,图片的高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 用于解决某些情况下,让图标垂直居中的用途
|
||||
top: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 主题名称
|
||||
type: [
|
||||
'primary',
|
||||
'success',
|
||||
'info',
|
||||
'error',
|
||||
'warning'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
customClass() {
|
||||
let classes = [];
|
||||
classes.push(this.customPrefix + '-' + this.name);
|
||||
// uView的自定义图标类名为u-iconfont
|
||||
if (this.customPrefix == 'uicon') classes.push('u-iconfont');
|
||||
else classes.push(this.customPrefix);
|
||||
// 主题色,通过类配置
|
||||
if (this.color && this.type.includes(this.color)) classes.push('u-icon__icon--' + this.color);
|
||||
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
|
||||
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
|
||||
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
|
||||
classes = classes.join(' ');
|
||||
//#endif
|
||||
return classes;
|
||||
},
|
||||
iconStyle() {
|
||||
let style = {};
|
||||
style = {
|
||||
fontSize: this.size == 'inherit' ? 'inherit' : this.addUnit(this.size),
|
||||
fontWeight: this.bold ? 'bold' : 'normal',
|
||||
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
|
||||
top: this.addUnit(this.top)
|
||||
};
|
||||
// 非主题色值时,才当作颜色值
|
||||
if (this.color && !this.type.includes(this.color)) style.color = this.color;
|
||||
return style;
|
||||
},
|
||||
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
|
||||
isImg() {
|
||||
return this.name.indexOf('/') !== -1;
|
||||
},
|
||||
imgStyle() {
|
||||
let style = {};
|
||||
// 如果设置width和height属性,则优先使用,否则使用size属性
|
||||
style.width = this.width ? this.addUnit(this.width) : this.addUnit(this.size);
|
||||
style.height = this.height ? this.addUnit(this.height) : this.addUnit(this.size);
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.$emit('click', this.index);
|
||||
},
|
||||
touchstart() {
|
||||
this.$emit('touchstart', this.index);
|
||||
},
|
||||
// 处理样式单位
|
||||
addUnit (value = 'auto', unit = 'rpx') {
|
||||
value = String(value);
|
||||
// 用uView内置验证规则中的number判断是否为数值
|
||||
return this.testNumber(value) ? `${value}${unit}` : value;
|
||||
},
|
||||
testNumber (value) {
|
||||
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './iconfont.css';
|
||||
.u-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
&--left {
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
}
|
||||
&--right {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
&--top {
|
||||
flex-direction: column-reverse;
|
||||
justify-content: center;
|
||||
}
|
||||
&--bottom {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
&__icon {
|
||||
position: relative;
|
||||
|
||||
&--primary {
|
||||
color: #2979ff;
|
||||
}
|
||||
&--success {
|
||||
color: #19be6b;
|
||||
}
|
||||
&--error {
|
||||
color: #fa3534;
|
||||
}
|
||||
&--warning {
|
||||
color: #ff9900;
|
||||
}
|
||||
&--info {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
&__img {
|
||||
height: auto;
|
||||
will-change: transform;
|
||||
}
|
||||
&__label {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,107 @@
|
|||
<!-- entranceList组件(通用组件) -->
|
||||
<template>
|
||||
<uni-grid class="entrance" :column="column" :showBorder="false" :highlight="false">
|
||||
<uni-grid-item class="entrance-grid-item" v-for="(v, i) in list" :key="i" @click="itemClickHandle(v)">
|
||||
<view class="entrance-item">
|
||||
<!-- <image class="image" :style="[imgStyle]" :src="v[imageKey]" mode=""></image>
|
||||
<text class="title" :style="[titleStyle]">{{ v[titleKey] }}</text> -->
|
||||
<image class="image" :style="{width:v.width,height:v.height}" :src="v[imageKey]" mode=""></image>
|
||||
<text class="title" :style="[titleStyle]">{{ v[titleKey] }}</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
</uni-grid>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'IndexEntrance',
|
||||
props: {
|
||||
column: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 图片大小
|
||||
imageH: {
|
||||
type: [String, Number],
|
||||
default: 60
|
||||
},
|
||||
imageW: {
|
||||
type: [String, Number],
|
||||
default: 60
|
||||
},
|
||||
// 单位
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'rpx'
|
||||
},
|
||||
imageKey: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
},
|
||||
titleKey: {
|
||||
type: String,
|
||||
default: 'title'
|
||||
},
|
||||
// 设置title样式
|
||||
titleStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
imgStyle () {
|
||||
return {
|
||||
width: `${this.imageW}${this.unit}`,
|
||||
height: `${this.imageH}${this.unit}`
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
itemClickHandle (item) {
|
||||
this.$emit('click', item)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.entrance {
|
||||
%flexCenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.entrance-grid-item {
|
||||
@extend %flexCenter;
|
||||
}
|
||||
.entrance-item {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@extend %flexCenter;
|
||||
flex-direction: column;
|
||||
}
|
||||
.image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 15rpx;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,434 @@
|
|||
<p align="center">
|
||||
<a href="https://github.com/liub1934/uni-lb-picker">
|
||||
<img src="https://img.shields.io/github/stars/liub1934/uni-lb-picker">
|
||||
</a>
|
||||
<a href="https://github.com/liub1934/uni-lb-picker/fork">
|
||||
<img src="https://img.shields.io/github/forks/liub1934/uni-lb-picker">
|
||||
</a>
|
||||
<a href="https://github.com/liub1934/uni-lb-picker/issues">
|
||||
<img src="https://img.shields.io/github/issues/liub1934/uni-lb-picker">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/uni-lb-picker">
|
||||
<img src="https://img.shields.io/npm/v/uni-lb-picker">
|
||||
</a>
|
||||
<a href="https://npmcharts.com/compare/uni-lb-picker?minimal=true">
|
||||
<img src="https://img.shields.io/npm/dm/uni-lb-picker">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/liub1934/uni-lb-picker/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/liub1934/uni-lb-picker">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
插件市场里面的 picker 选择器不满足自己的需求,所以自己写了一个简单的 picker 选择器,可扩展、可自定义,一般满足日常需要。
|
||||
Github:[uni-lb-picker](https://github.com/liub1934/uni-lb-picker)
|
||||
插件市场:[uni-lb-picker](https://ext.dcloud.net.cn/plugin?id=1111)
|
||||
|
||||
> 如果问题最好去 github 反馈,插件市场评论区留下五星好评即可,[点我去反馈](https://github.com/liub1934/uni-lb-picker/issues/new)
|
||||
|
||||
> **由于之前`cancel`拼写失误,写成了`cancle`,`v1.08`现已修正,如果之前版本有使用`cancel`事件的,更新后请及时修正。**
|
||||
|
||||
## 兼容性
|
||||
|
||||
App + H5 + 各平台小程序(快应用及 360 未测试,nvue 待支持)
|
||||
|
||||
## 功能
|
||||
|
||||
1、单选
|
||||
2、多级联动,非多级联动,理论支持任意级数
|
||||
3、省市区选择,基于多级联动
|
||||
4、自定义选择器头部确定取消按钮颜色及插槽支持
|
||||
5、选择器可视区自定义滚动个数
|
||||
6、自定义数据字段,满足不同人的需求
|
||||
7、自定义选择器样式
|
||||
8、单选及非联动选择支持扁平化的简单数据,如下形式:
|
||||
|
||||
```javascript
|
||||
// 单选列表
|
||||
list1: ['选项1', '选项2', '选项2'],
|
||||
// 非联动选择列表
|
||||
list2: [
|
||||
['选项1', '选项2', '选项3'],
|
||||
['选项11', '选项22', '选项33'],
|
||||
['选项111', '选项222', '选项333']
|
||||
]
|
||||
```
|
||||
|
||||
## 引入插件
|
||||
|
||||
单独引入,在需要使用的页面上 import 引入即可
|
||||
|
||||
```html
|
||||
<template>
|
||||
<view>
|
||||
<lb-picker></lb-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LbPicker from '@/components/lb-picker'
|
||||
export default {
|
||||
components: {
|
||||
LbPicker
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
全局引入,`main.js`中 import 引入并注册即可全局使用
|
||||
|
||||
```jsvascript
|
||||
import LbPicker from '@/components/lb-picker'
|
||||
Vue.component("lb-picker", LbPicker)
|
||||
```
|
||||
|
||||
easycom 引入
|
||||
|
||||
`pages.json`加上如下配置:
|
||||
|
||||
```json
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"lb-picker": "@/components/lb-picker/index.vue"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
npm 安装引入:
|
||||
|
||||
```shell
|
||||
npm install uni-lb-picker
|
||||
```
|
||||
|
||||
```jsvascript
|
||||
import LbPicker from 'uni-lb-picker'
|
||||
```
|
||||
|
||||
## 选择器数据格式
|
||||
|
||||
### 单选
|
||||
|
||||
常规数据
|
||||
|
||||
```javascript
|
||||
list: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
扁平化简单数据
|
||||
|
||||
```javascript
|
||||
list: ['选项1', '选项2']
|
||||
```
|
||||
|
||||
### 多级联动
|
||||
|
||||
```javascript
|
||||
list: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
children: [
|
||||
{
|
||||
label: '选项1-1',
|
||||
value: '1-1',
|
||||
children: [
|
||||
{
|
||||
label: '选项1-1-1',
|
||||
value: '1-1-1'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 非联动选择
|
||||
|
||||
常规数据
|
||||
|
||||
```javascript
|
||||
list: [
|
||||
[
|
||||
{ label: '选项1', value: '1' },
|
||||
{ label: '选项2', value: '2' },
|
||||
{ label: '选项3', value: '3' }
|
||||
],
|
||||
[
|
||||
{ label: '选项11', value: '11' },
|
||||
{ label: '选项22', value: '22' },
|
||||
{ label: '选项33', value: '33' }
|
||||
],
|
||||
[
|
||||
{ label: '选项111', value: '111' },
|
||||
{ label: '选项222', value: '222' },
|
||||
{ label: '选项333', value: '333' }
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
扁平化简单数据
|
||||
|
||||
```javascript
|
||||
list: [
|
||||
['选项1', '选项2', '选项3'],
|
||||
['选项11', '选项22', '选项33'],
|
||||
['选项111', '选项222', '选项333']
|
||||
]
|
||||
```
|
||||
|
||||
## 调用显示选择器
|
||||
|
||||
通过`ref`形式手动调用`show`方法显示,隐藏同理调用`hide`
|
||||
|
||||
```text
|
||||
<lb-picker ref="picker"></lb-picker>
|
||||
|
||||
this.$refs.picker.show() // 显示
|
||||
this.$refs.picker.hide() // 隐藏
|
||||
```
|
||||
|
||||
## 绑定值及设置默认值
|
||||
|
||||
支持 vue 中`v-model`写法绑定值,无需自己维护选中值的索引。
|
||||
|
||||
```javascript
|
||||
<lb-picker v-model="value1"></lb-picker>
|
||||
<lb-picker v-model="value2"></lb-picker>
|
||||
|
||||
data () {
|
||||
return {
|
||||
value1: '' // 单选
|
||||
value2: [] // 多列联动选择
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 多个选择器
|
||||
|
||||
通过设置不同的`ref`,然后调用即可
|
||||
|
||||
```javascript
|
||||
<lb-picker ref="picker1"></lb-picker>
|
||||
<lb-picker ref="picker2"></lb-picker>
|
||||
|
||||
this.$refs.picker1.show() // picker1显示
|
||||
this.$refs.picker2.show() // picker2显示
|
||||
```
|
||||
|
||||
## 省市区选择
|
||||
|
||||
省市区选择是基于多列联动选择,数据来源:[https://github.com/modood/Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China),
|
||||
省市区文件位于`/pages/demos/area-data-min.js`,自行引入即可,可参考`demo3省市区选择`,
|
||||
也可使用自己已有的省市区数据,如果数据字段不一样,也可以自定义,参考下方自定义数据字段。
|
||||
|
||||
## 自定义数据字段
|
||||
|
||||
为了满足不同人的需求,插件支持自定义数据字段名称, 插件默认的数据字段如下形式:
|
||||
|
||||
```javascript
|
||||
list: [
|
||||
{
|
||||
label: '选择1',
|
||||
value: 1,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
label: '选择1',
|
||||
value: 1,
|
||||
children: []
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
如果你的数据字段和上面不一样,如下形式:
|
||||
|
||||
```javascript
|
||||
list: [
|
||||
{
|
||||
text: '选择1',
|
||||
id: 1,
|
||||
child: []
|
||||
},
|
||||
{
|
||||
text: '选择1',
|
||||
id: 1,
|
||||
child: []
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
通过设置参数中的`props`即可,如下所示:
|
||||
|
||||
```javascript
|
||||
<lb-picker :props="myProps"></lb-picker>
|
||||
|
||||
data () {
|
||||
return {
|
||||
myProps: {
|
||||
label: 'text',
|
||||
value: 'id',
|
||||
children: 'child'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 插槽使用
|
||||
|
||||
选择器支持一些可自定义化的插槽,如选择器的取消和确定文字按钮,如果需要对其自定义处理的话,比如加个 icon 图标之类的,可使用插槽,使用方法如下:
|
||||
|
||||
```html
|
||||
<lb-picker>
|
||||
<view slot="cancel-text">我是自定义取消</view>
|
||||
<view slot="confirm-text">我是自定义确定</view>
|
||||
</lb-picker>
|
||||
```
|
||||
|
||||
其他插槽见下。
|
||||
|
||||
## 参数及事件
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | :------------------ | :--------------------------------------------------------------- | :------------------------------------------------ |
|
||||
| value/v-model | 绑定值,联动选择为 Array 类型 | String/Number/Array | - | - |
|
||||
| mode | 选择器类型,支持单列,多列联动 | String | selector 单选/multiSelector 多级联动/unlinkedSelector 多级非联动 | selector |
|
||||
| list | 选择器数据(v1.0.7 单选及非联动多选支持扁平数据:['选项 1', '选项 2']) | Array | - | - |
|
||||
| level | 多列联动层级,仅 mode 为 multiSelector 有效 | Number | - | 2 |
|
||||
| props | 自定义数据字段 | Object | - | {label:'label',value:'value',children:'children'} |
|
||||
| cancel-text | 取消文字 | String | - | 取消 |
|
||||
| cancel-color | 取消文字颜色 | String | - | #999 |
|
||||
| confirm-text | 确定文字 | String | - | 确定 |
|
||||
| confirm-color | 确定文字颜色 | String | - | #007aff |
|
||||
| empty-text | (v1.0.7 新增)选择器列表为空的时候显示的文字 | String | - | 暂无数据 |
|
||||
| empty-color | (v1.0.7 新增)暂无数据文字颜色 | String | - | #999 |
|
||||
| column-num | 可视滚动区域内滚动个数,最好设置奇数值 | Number | - | 5 |
|
||||
| radius | 选择器顶部圆角,支持 rpx,如 radius="10rpx" | String | - | - |
|
||||
| ~~column-style~~ | ~~选择器默认样式(已弃用,见下方自定义样式说明)~~ | Object | - | - |
|
||||
| ~~active-column-style~~ | ~~选择器选中样式(已弃用,见下方自定义样式说明)~~ | Object | - | - |
|
||||
| loading | 选择器是否显示加载中,可使用 loading 插槽自定义加载效果 | Boolean | - | - |
|
||||
| mask-color | 遮罩层颜色 | String | - | rgba(0, 0, 0, 0.4) |
|
||||
| close-on-click-mask | 点击遮罩层是否关闭选择器 | Boolean | true/false | true |
|
||||
| ~~change-on-init~~ | ~~(v1.0.7 已弃用)初始化时是否触发 change 事件~~ | Boolean | true/false | - |
|
||||
| dataset | (v1.0.7 新增)可以向组件中传递任意的自定义的数据(对象形式数据),如`:dataset="{name:'test'}"`,在`confirm`或`change`事件中可以取到 | Object | - | - |
|
||||
| show-header | (v1.0.8 新增)是否显示选择器头部 | Boolean | - | true |
|
||||
| inline | (v1.0.8 新增)inline 模式,开启后默认显示选择器,无需点击弹出,可以配合`show-header`一起使用 | Boolean | - | - |
|
||||
| z-index | (v1.0.9 新增)选择器层级,遮罩层默认-1 | Number | - | 999 |
|
||||
|
||||
### 方法
|
||||
|
||||
| 方法名 | 说明 | 参数 | 返回值 |
|
||||
| :------------- | :------------------------------------- | :-------------- | :----------------------------------------------------------------------------------------------------------- |
|
||||
| show | 打开选择器 | - | |
|
||||
| hide | 关闭选择器 | - | |
|
||||
| getColumnsInfo | (v1.1.0 新增)根据 value 获取选择器信息 | 绑定值的`value` | 同`change` `confirm`回调参数,如果传入的`value`获取不到信息则只返回一个含有`dataset`的对象,具体自行打印查看 |
|
||||
|
||||
### Events
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| :------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| show | 选择器打开时触发 | - |
|
||||
| hide | 选择器隐藏时触发 | - |
|
||||
| change | 选择器滚动时触发,此时不会改变绑定的值 | `{ index, item, value, change }` `index`触发滚动后新的索引,单选时是具体的索引值,多列联动选择时为数组。`item`触发滚动后新的的完整内容,包括`label`、`value`等,单选时为对象,多列选择时为数组对象。`value`触发滚动后新的 value 值,单列选择时为具体值,多列联动选择时为数组。`change`触发事件的类型,详情参考下面的 change 事件备注 |
|
||||
| confirm | 点击选择器确定时触发,此时会改变绑定的值 | 同上`change`事件说明 |
|
||||
| cancel | 点击选择器取消时触发 | 同上`change`事件说明 |
|
||||
|
||||
### `change` 事件备注
|
||||
|
||||
如果绑定的值是空的,`change`触发后里面的内容都是列表的第一项。
|
||||
`change`事件会在以下情况触发:
|
||||
|
||||
- 初始化
|
||||
- 绑定值 value 变化
|
||||
- 选择器 list 列表变化
|
||||
- 滚动选择器
|
||||
|
||||
以上情况会在回调函数中都可以取到`change`变化的类型,对应上面的情况包括以下:
|
||||
|
||||
- `init`
|
||||
- `value`
|
||||
- `list`
|
||||
- `scroll`
|
||||
|
||||
根据这些类型大家可以在`change`的时候按需处理自己的业务逻辑,`init`现在指挥在调用选择器弹出的时候触发。
|
||||
下面的说明情况已失效,如需要在页面显示的时候根据`value`的值显示相应的中文,调用`v1.10`新增的方法`getColumnsInfo`,传入绑定的值即可获取到你想要的所有信息。
|
||||
~~比如一种常见的情况,有默认值的时候需要显示默认值的文字,此时可以`change`事件中判断`change`的类型是否是`init`,如果是的话可以取事件回调中的`item`进行显示绑定值对应的文字信息。~~
|
||||
|
||||
```javascript
|
||||
handleChange (e) {
|
||||
if (e.change === 'init') {
|
||||
console.log(e.item.label) // 单选 选项1
|
||||
console.log(e.item.map(item => item.label).join('-')) // 多选 选项1-选项11
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 插槽
|
||||
|
||||
| 插槽名 | 说明 |
|
||||
| :------------ | :------------------ |
|
||||
| cancel-text | 选择器取消文字插槽 |
|
||||
| action-center | 选择器顶部中间插槽 |
|
||||
| confirm-text | 选择器确定文字插槽 |
|
||||
| loading | 选择器 loading 插槽 |
|
||||
| empty | 选择器 空数据 插槽 |
|
||||
|
||||
### 选择器自定义样式
|
||||
|
||||
原先的`column-style`和`active-column-style`已弃用,如需修改默认样式及选中样式参考`demo9`
|
||||
|
||||
```css
|
||||
<style lang="scss" scoped>
|
||||
/deep/ .lb-picker {
|
||||
.lb-picker-column-label {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
.lb-picker-column-active {
|
||||
.lb-picker-column-label {
|
||||
color: #007aff;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 获取选中值的文字
|
||||
|
||||
`@confirm`事件中可以拿到:
|
||||
|
||||
单选:
|
||||
|
||||
```javascript
|
||||
handleConfirm (e) {
|
||||
console.log(e.item.label) // 选项1
|
||||
}
|
||||
```
|
||||
|
||||
联动选择:
|
||||
|
||||
```javascript
|
||||
handleConfirm (e) {
|
||||
console.log(e.item.map(item => item.label).join('-')) // 选项1-选项11
|
||||
}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
微信小程序端,滚动时在 iOS 自带振动反馈,可在系统设置 -> 声音与触感 -> 系统触感反馈中关闭
|
||||
|
||||
## 其他
|
||||
|
||||
其他功能参考示例 Demo 代码。
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,45 @@
|
|||
import { getColumns } from '../utils'
|
||||
export const commonMixin = {
|
||||
data () {
|
||||
return {
|
||||
indicatorStyle: `height: 34px`
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.init('init')
|
||||
},
|
||||
methods: {
|
||||
init (changeType) {
|
||||
if (this.list && this.list.length) {
|
||||
const column = getColumns({
|
||||
value: this.value,
|
||||
list: this.list,
|
||||
mode: this.mode,
|
||||
props: this.props,
|
||||
level: this.level
|
||||
})
|
||||
const { columns, value, item, index } = column
|
||||
this.selectValue = value
|
||||
this.selectItem = item
|
||||
this.pickerColumns = columns
|
||||
this.pickerValue = index
|
||||
this.$emit('change', {
|
||||
value: this.selectValue,
|
||||
item: this.selectItem,
|
||||
index: this.pickerValue,
|
||||
change: changeType
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value () {
|
||||
if (!this.isConfirmChange) {
|
||||
this.init('value')
|
||||
}
|
||||
},
|
||||
list () {
|
||||
this.init('list')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<view class="lb-multi-selector lb-picker-item"
|
||||
:style="{ height: height }">
|
||||
<picker-view :value="pickerValue"
|
||||
:indicator-style="indicatorStyle"
|
||||
:style="{ height: height }"
|
||||
@change="handleChange">
|
||||
<picker-view-column v-for="(column, index) in pickerColumns"
|
||||
:key="index">
|
||||
<view v-for="(item, i) in column || []"
|
||||
:class="[
|
||||
'lb-picker-column',
|
||||
item[props.value] === selectValue[index]
|
||||
? 'lb-picker-column-active'
|
||||
: ''
|
||||
]"
|
||||
:key="i">
|
||||
<text class="lb-picker-column-label">
|
||||
{{ item[props.label] || item }}
|
||||
</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { commonMixin } from '../mixins'
|
||||
export default {
|
||||
props: {
|
||||
value: Array,
|
||||
list: Array,
|
||||
mode: String,
|
||||
props: Object,
|
||||
level: Number,
|
||||
visible: Boolean,
|
||||
height: String,
|
||||
isConfirmChange: Boolean
|
||||
},
|
||||
mixins: [commonMixin],
|
||||
data () {
|
||||
return {
|
||||
pickerValue: [],
|
||||
pickerColumns: [],
|
||||
selectValue: [],
|
||||
selectItem: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (item) {
|
||||
const pickerValue = item.detail.value
|
||||
const columnIndex = pickerValue.findIndex((item, i) => item !== this.pickerValue[i])
|
||||
const valueIndex = pickerValue[columnIndex]
|
||||
this.setPickerChange(pickerValue, valueIndex, columnIndex)
|
||||
},
|
||||
setPickerChange (pickerValue, valueIndex, columnIndex) {
|
||||
for (let i = 0; i < this.level; i++) {
|
||||
if (i > columnIndex) {
|
||||
pickerValue[i] = 0
|
||||
const column =
|
||||
this.pickerColumns[i - 1][valueIndex] ||
|
||||
this.pickerColumns[i - 1][0]
|
||||
this.$set(this.pickerColumns, i, column[this.props.children] || [])
|
||||
valueIndex = 0
|
||||
}
|
||||
this.$set(this.pickerValue, i, pickerValue[i])
|
||||
const selectItem = this.pickerColumns[i][pickerValue[i]]
|
||||
if (selectItem) {
|
||||
this.selectItem[i] = selectItem
|
||||
this.selectValue[i] = selectItem[this.props.value]
|
||||
} else {
|
||||
const spliceNum = this.level - i
|
||||
this.pickerValue.splice(i, spliceNum)
|
||||
this.selectValue.splice(i, spliceNum)
|
||||
this.selectItem.splice(i, spliceNum)
|
||||
this.pickerColumns.splice(i, spliceNum)
|
||||
break
|
||||
}
|
||||
}
|
||||
this.$emit('change', {
|
||||
value: this.selectValue,
|
||||
item: this.selectItem,
|
||||
index: this.pickerValue,
|
||||
change: 'scroll'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../style/picker-item.scss";
|
||||
</style>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<view class="lb-selector-picker lb-picker-item"
|
||||
:style="{ height: height }">
|
||||
<picker-view :value="pickerValue"
|
||||
:style="{ height: height }"
|
||||
:indicator-style="indicatorStyle"
|
||||
@change="handleChange">
|
||||
<picker-view-column>
|
||||
<view v-for="(item, i) in list"
|
||||
:class="[
|
||||
'lb-picker-column',
|
||||
(item[props.value] || item) === selectValue
|
||||
? 'lb-picker-column-active'
|
||||
: ''
|
||||
]"
|
||||
:key="i">
|
||||
<text class="lb-picker-column-label">
|
||||
{{ item[props.label] || item }}
|
||||
</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isObject } from '../utils'
|
||||
import { commonMixin } from '../mixins'
|
||||
export default {
|
||||
props: {
|
||||
value: [String, Number],
|
||||
list: Array,
|
||||
mode: String,
|
||||
props: Object,
|
||||
visible: Boolean,
|
||||
height: String,
|
||||
isConfirmChange: Boolean
|
||||
},
|
||||
mixins: [commonMixin],
|
||||
data () {
|
||||
return {
|
||||
pickerValue: [],
|
||||
selectValue: '',
|
||||
selectItem: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (item) {
|
||||
const index = item.detail.value[0] || 0
|
||||
this.selectItem = this.list[index]
|
||||
this.selectValue = isObject(this.selectItem)
|
||||
? this.selectItem[this.props.value]
|
||||
: this.selectItem
|
||||
this.pickerValue = item.detail.value
|
||||
this.$emit('change', {
|
||||
value: this.selectValue,
|
||||
item: this.selectItem,
|
||||
index: index,
|
||||
change: 'scroll'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../style/picker-item.scss";
|
||||
</style>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<template>
|
||||
<view class="lb-selector-picker lb-picker-item"
|
||||
:style="{ height: height }">
|
||||
<picker-view :value="pickerValue"
|
||||
:indicator-style="indicatorStyle"
|
||||
:style="{ height: height }"
|
||||
@change="handleChange">
|
||||
<picker-view-column v-for="(column, index) in pickerColumns"
|
||||
:key="index">
|
||||
<view v-for="(item, i) in column || []"
|
||||
:class="[
|
||||
'lb-picker-column',
|
||||
(item[props.value] || item) === selectValue[index]
|
||||
? 'lb-picker-column-active'
|
||||
: ''
|
||||
]"
|
||||
:key="i">
|
||||
<text class="lb-picker-column-label">
|
||||
{{ item[props.label] || item }}
|
||||
</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isObject } from '../utils'
|
||||
import { commonMixin } from '../mixins'
|
||||
export default {
|
||||
props: {
|
||||
value: Array,
|
||||
list: Array,
|
||||
mode: String,
|
||||
props: Object,
|
||||
visible: Boolean,
|
||||
height: String,
|
||||
isConfirmChange: Boolean
|
||||
},
|
||||
mixins: [commonMixin],
|
||||
data () {
|
||||
return {
|
||||
pickerValue: [],
|
||||
pickerColumns: [],
|
||||
selectValue: [],
|
||||
selectItem: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (item) {
|
||||
const pickerValue = item.detail.value
|
||||
const columnIndex = pickerValue.findIndex((item, i) => item !== this.pickerValue[i])
|
||||
if (columnIndex > -1) {
|
||||
const valueIndex = pickerValue[columnIndex]
|
||||
const columnItem = this.list[columnIndex][valueIndex]
|
||||
const valueItem = isObject(columnItem)
|
||||
? columnItem[this.props.value]
|
||||
: columnItem
|
||||
this.pickerValue = pickerValue
|
||||
this.$set(this.selectValue, columnIndex, valueItem)
|
||||
this.$set(this.selectItem, columnIndex, columnItem)
|
||||
this.$emit('change', {
|
||||
value: this.selectValue,
|
||||
item: this.selectItem,
|
||||
index: this.pickerValue,
|
||||
change: 'scroll'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../style/picker-item.scss";
|
||||
</style>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.lb-picker-column {
|
||||
height: 34px;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lb-picker-column-label {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
transition-property: color;
|
||||
transition-duration: 0.3s;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
.lb-picker {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lb-picker-mask {
|
||||
background-color: rgba(0, 0, 0, 0.0);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.lb-picker-mask-animation {
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.lb-picker-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lb-picker-container-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: translateY(100%);
|
||||
/* #ifndef APP-PLUS */
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.lb-picker-container-animation {
|
||||
transition-property: transform;
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.lb-picker-container-show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.lb-picker-header {
|
||||
position: relative;
|
||||
height: 45px;
|
||||
background-color: #fff;
|
||||
/* #ifdef APP-NVUE */
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #e5e5e5;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
/* #ifndef APP-PLUS */
|
||||
.lb-picker-header::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
clear: both;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
color: #e5e5e5;
|
||||
transform-origin: 0 100%;
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.lb-picker-action {
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lb-picker-action-cancel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lb-picker-action-cancel-text {
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.lb-picker-action-confirm {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lb-picker-action-confirm-text {
|
||||
font-size: 16px;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.lb-picker-content {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.lb-picker-loading,
|
||||
.lb-picker-empty {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lb-picker-empty-text {
|
||||
color: #999;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.lb-picker-loading-img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
/* #ifndef APP-NVUE */
|
||||
animation: rotating 2s linear infinite;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
@keyframes rotating {
|
||||
0% {
|
||||
transform: rotate(0deg)
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(1turn)
|
||||
}
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* 判断是否是对象
|
||||
*
|
||||
* @export
|
||||
* @param {*} val
|
||||
* @returns true/false
|
||||
*/
|
||||
export function isObject (val) {
|
||||
return Object.prototype.toString.call(val) === '[object Object]'
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据value获取columns信息
|
||||
*
|
||||
* @export
|
||||
* @param {*} { value, list, mode, props, level }
|
||||
* @param {number} [type=2] 查询不到value数据返回数据类型 1空值null 2默认第一个选项
|
||||
* @returns
|
||||
*/
|
||||
export function getColumns ({ value, list, mode, props, level }, type = 2) {
|
||||
let pickerValue = []
|
||||
let pickerColumns = []
|
||||
let selectValue = []
|
||||
let selectItem = []
|
||||
let columnsInfo = null
|
||||
switch (mode) {
|
||||
case 'selector':
|
||||
let index = list.findIndex(item => {
|
||||
return isObject(item) ? item[props.value] === value : item === value
|
||||
})
|
||||
if (index === -1 && type === 1) {
|
||||
columnsInfo = null
|
||||
} else {
|
||||
index = index > -1 ? index : 0
|
||||
selectItem = list[index]
|
||||
selectValue = isObject(selectItem)
|
||||
? selectItem[props.value]
|
||||
: selectItem
|
||||
pickerColumns = list
|
||||
pickerValue = [index]
|
||||
columnsInfo = {
|
||||
index: pickerValue,
|
||||
value: selectValue,
|
||||
item: selectItem,
|
||||
columns: pickerColumns
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'multiSelector':
|
||||
const setPickerItems = (data = [], index = 0) => {
|
||||
if (!data.length) return
|
||||
const defaultValue = value || []
|
||||
if (index < level) {
|
||||
const value = defaultValue[index] || ''
|
||||
let i = data.findIndex(item => item[props.value] === value)
|
||||
if (i === -1 && type === 1) return
|
||||
i = i > -1 ? i : 0
|
||||
pickerValue[index] = i
|
||||
pickerColumns[index] = data
|
||||
if (data[i]) {
|
||||
selectValue[index] = data[i][props.value]
|
||||
selectItem[index] = data[i]
|
||||
setPickerItems(data[i][props.children] || [], index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
setPickerItems(list)
|
||||
if (!selectValue.length && type === 1) {
|
||||
columnsInfo = null
|
||||
} else {
|
||||
columnsInfo = {
|
||||
index: pickerValue,
|
||||
value: selectValue,
|
||||
item: selectItem,
|
||||
columns: pickerColumns
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'unlinkedSelector':
|
||||
list.forEach((item, i) => {
|
||||
let index = item.findIndex(item => {
|
||||
return isObject(item)
|
||||
? item[props.value] === value[i]
|
||||
: item === value[i]
|
||||
})
|
||||
if (index === -1 && type === 1) return
|
||||
index = index > -1 ? index : 0
|
||||
const columnItem = list[i][index]
|
||||
const valueItem = isObject(columnItem)
|
||||
? columnItem[props.value]
|
||||
: columnItem
|
||||
pickerValue[i] = index
|
||||
selectValue[i] = valueItem
|
||||
selectItem[i] = columnItem
|
||||
})
|
||||
pickerColumns = list
|
||||
if (!selectValue.length && type === 1) {
|
||||
columnsInfo = null
|
||||
} else {
|
||||
columnsInfo = {
|
||||
index: pickerValue,
|
||||
value: selectValue,
|
||||
item: selectItem,
|
||||
columns: pickerColumns
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return columnsInfo
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<!-- 遮罩 -->
|
||||
<template>
|
||||
<view class="u-mask" hover-stop-propagation :style="[maskStyle, zoomStyle]" @tap="click" @touchmove.stop.prevent :class="{
|
||||
'u-mask-zoom': zoom,
|
||||
'u-mask-show': show
|
||||
}">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* mask 遮罩
|
||||
* @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景
|
||||
* @tutorial https://www.uviewui.com/components/mask.html
|
||||
* @property {Boolean} show 是否显示遮罩(默认false)
|
||||
* @property {String Number} z-index z-index 层级(默认1070)
|
||||
* @property {Object} custom-style 自定义样式对象,见上方说明
|
||||
* @property {String Number} duration 动画时长,单位毫秒(默认300)
|
||||
* @property {Boolean} zoom 是否使用scale对这招进行缩放(默认true)
|
||||
* @property {Boolean} mask-click-able 遮罩是否可点击,为false时点击不会发送click事件(默认true)
|
||||
* @event {Function} click mask-click-able为true时,点击遮罩发送此事件
|
||||
* @example <mask-view :show="show" @click="show = false"></mask-view>
|
||||
*/
|
||||
export default {
|
||||
name: "maskView",
|
||||
props: {
|
||||
// 是否显示遮罩
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 层级z-index
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 用户自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 遮罩的动画样式, 是否使用使用zoom进行scale进行缩放
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 遮罩的过渡时间,单位为ms
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 300
|
||||
},
|
||||
// 是否可以通过点击遮罩进行关闭
|
||||
maskClickAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
zoomStyle: {
|
||||
transform: ''
|
||||
},
|
||||
scale: 'scale(1.2, 1.2)'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(n) {
|
||||
if(n && this.zoom) {
|
||||
// 当展示遮罩的时候,设置scale为1,达到缩小(原来为1.2)的效果
|
||||
this.zoomStyle.transform = 'scale(1, 1)';
|
||||
} else if(!n && this.zoom) {
|
||||
// 当隐藏遮罩的时候,设置scale为1.2,达到放大(因为显示遮罩时已重置为1)的效果
|
||||
this.zoomStyle.transform = this.scale;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
maskStyle() {
|
||||
let style = {};
|
||||
style.backgroundColor = "rgba(0, 0, 0, 0.6)";
|
||||
if(this.show) style.zIndex = this.zIndex ? this.zIndex : 10070;
|
||||
else style.zIndex = -1;
|
||||
style.transition = `all ${this.duration / 1000}s ease-in-out`;
|
||||
// 判断用户传递的对象是否为空,不为空就进行合并
|
||||
if (Object.keys(this.customStyle).length) style = { ...style,
|
||||
...this.customStyle
|
||||
};
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (!this.maskClickAble) return;
|
||||
this.$emit('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.u-mask-show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.u-mask-zoom {
|
||||
transform: scale(1.2, 1.2);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/* 下拉刷新区域 */
|
||||
.mescroll-downwarp {
|
||||
position: absolute;
|
||||
top: -100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 下拉刷新--内容区,定位于区域底部 */
|
||||
.mescroll-downwarp .downwarp-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
min-height: 60rpx;
|
||||
padding: 20rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 下拉刷新--提示文本 */
|
||||
.mescroll-downwarp .downwarp-tip {
|
||||
display: inline-block;
|
||||
font-size: 28rpx;
|
||||
vertical-align: middle;
|
||||
margin-left: 16rpx;
|
||||
/* color: gray; 已在style设置color,此处删去*/
|
||||
}
|
||||
|
||||
/* 下拉刷新--旋转进度条 */
|
||||
.mescroll-downwarp .downwarp-progress {
|
||||
display: inline-block;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid gray;
|
||||
border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* 旋转动画 */
|
||||
.mescroll-downwarp .mescroll-rotate {
|
||||
animation: mescrollDownRotate 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes mescrollDownRotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<!-- 下拉刷新区域 -->
|
||||
<template>
|
||||
<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.textColor, 'transform':downRotate}"></view>
|
||||
<view class="downwarp-tip">{{downText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
option: Object , // down的配置项
|
||||
type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption(){
|
||||
return this.option || {}
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading(){
|
||||
return this.type === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate(){
|
||||
return 'rotate(' + 360 * this.rate + 'deg)'
|
||||
},
|
||||
// 文本提示
|
||||
downText(){
|
||||
switch (this.type){
|
||||
case 1: return this.mOption.textInOffset;
|
||||
case 2: return this.mOption.textOutOffset;
|
||||
case 3: return this.mOption.textLoading;
|
||||
case 4: return this.mOption.textLoading;
|
||||
default: return this.mOption.textInOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "./mescroll-down.css";
|
||||
</style>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<!--空布局
|
||||
|
||||
可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
|
||||
import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
|
||||
<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
-->
|
||||
<template>
|
||||
<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
|
||||
<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
|
||||
<view v-if="tip" class="empty-tip">{{ tip }}</view>
|
||||
<view v-if="option.btnText" class="empty-btn" @click="emptyClick">{{ option.btnText }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入全局配置
|
||||
import GlobalOption from './../mescroll-uni-option.js';
|
||||
export default {
|
||||
props: {
|
||||
// empty的配置项: 默认为GlobalOption.up.empty
|
||||
option: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
// 使用computed获取配置,用于支持option的动态配置
|
||||
computed: {
|
||||
// 图标
|
||||
icon() {
|
||||
return this.option.icon == null ? GlobalOption.up.empty.icon : this.option.icon; // 此处不使用短路求值, 用于支持传空串不显示图标
|
||||
},
|
||||
// 文本提示
|
||||
tip() {
|
||||
return this.option.tip == null ? GlobalOption.up.empty.tip : this.option.tip; // 此处不使用短路求值, 用于支持传空串不显示文本提示
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 点击按钮
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 无任何数据的空布局 */
|
||||
.mescroll-empty {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 100rpx 50rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mescroll-empty.empty-fixed {
|
||||
z-index: 99;
|
||||
position: absolute; /*transform会使fixed失效,最终会降级为absolute */
|
||||
top: 100rpx;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.mescroll-empty .empty-icon {
|
||||
width: 280rpx;
|
||||
height: 280rpx;
|
||||
}
|
||||
|
||||
.mescroll-empty .empty-tip {
|
||||
margin-top: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.mescroll-empty .empty-btn {
|
||||
display: inline-block;
|
||||
margin-top: 40rpx;
|
||||
min-width: 200rpx;
|
||||
padding: 18rpx;
|
||||
font-size: 28rpx;
|
||||
border: 2rpx solid #e04b28;
|
||||
border-radius: 60rpx;
|
||||
color: #e04b28;
|
||||
}
|
||||
|
||||
.mescroll-empty .empty-btn:active {
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<!-- 回到顶部的按钮 -->
|
||||
<template>
|
||||
<image
|
||||
v-if="mOption.src"
|
||||
class="mescroll-totop"
|
||||
:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
|
||||
:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
|
||||
:src="mOption.src"
|
||||
mode="widthFix"
|
||||
@click="toTopClick"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
// up.toTop的配置项
|
||||
option: Object,
|
||||
// 是否显示
|
||||
value: false
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption(){
|
||||
return this.option || {}
|
||||
},
|
||||
// 优先显示左边
|
||||
left(){
|
||||
return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
|
||||
},
|
||||
// 右边距离 (优先显示左边)
|
||||
right() {
|
||||
return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addUnit(num){
|
||||
if(!num) return 0;
|
||||
if(typeof num === 'number') return num + 'rpx';
|
||||
return num
|
||||
},
|
||||
toTopClick() {
|
||||
this.$emit('input', false); // 使v-model生效
|
||||
this.$emit('click'); // 派发点击事件
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 回到顶部的按钮 */
|
||||
.mescroll-totop {
|
||||
z-index: 9990;
|
||||
position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
|
||||
right: 20rpx;
|
||||
bottom: 120rpx;
|
||||
width: 72rpx;
|
||||
height: auto;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s; /* 过渡 */
|
||||
margin-bottom: var(--window-bottom); /* css变量 */
|
||||
}
|
||||
|
||||
/* 适配 iPhoneX */
|
||||
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
|
||||
.mescroll-totop-safearea {
|
||||
margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
|
||||
margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示 -- 淡入 */
|
||||
.mescroll-totop-in {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 隐藏 -- 淡出且不接收事件*/
|
||||
.mescroll-totop-out {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* 上拉加载区域 */
|
||||
.mescroll-upwarp {
|
||||
box-sizing: border-box;
|
||||
min-height: 110rpx;
|
||||
padding: 30rpx 0;
|
||||
text-align: center;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/*提示文本 */
|
||||
.mescroll-upwarp .upwarp-tip,
|
||||
.mescroll-upwarp .upwarp-nodata {
|
||||
display: inline-block;
|
||||
font-size: 28rpx;
|
||||
vertical-align: middle;
|
||||
/* color: gray; 已在style设置color,此处删去*/
|
||||
}
|
||||
|
||||
.mescroll-upwarp .upwarp-tip {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/*旋转进度条 */
|
||||
.mescroll-upwarp .upwarp-progress {
|
||||
display: inline-block;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid gray;
|
||||
border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* 旋转动画 */
|
||||
.mescroll-upwarp .mescroll-rotate {
|
||||
animation: mescrollUpRotate 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes mescrollUpRotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<!-- 上拉加载区域 -->
|
||||
<template>
|
||||
<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="isUpLoading">
|
||||
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.textColor}"></view>
|
||||
<view class="upwarp-tip">{{ mOption.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
option: Object, // up的配置项
|
||||
type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption() {
|
||||
return this.option || {};
|
||||
},
|
||||
// 加载中
|
||||
isUpLoading() {
|
||||
return this.type === 1;
|
||||
},
|
||||
// 没有更多了
|
||||
isUpNoMore() {
|
||||
return this.type === 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import './mescroll-up.css';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
.mescroll-body {
|
||||
position: relative; /* 下拉刷新区域相对自身定位 */
|
||||
height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
|
||||
overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
|
||||
box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
|
||||
}
|
||||
|
||||
/* 适配 iPhoneX */
|
||||
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
|
||||
.mescroll-safearea {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
<template>
|
||||
<view
|
||||
class="mescroll-body mescroll-render-touch"
|
||||
:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}"
|
||||
@touchstart="wxsBiz.touchstartEvent"
|
||||
@touchmove="wxsBiz.touchmoveEvent"
|
||||
@touchend="wxsBiz.touchendEvent"
|
||||
@touchcancel="wxsBiz.touchendEvent"
|
||||
:change:prop="wxsBiz.propObserver"
|
||||
:prop="wxsProp"
|
||||
>
|
||||
<!-- 状态栏 -->
|
||||
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
|
||||
|
||||
<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
|
||||
<view class="downwarp-tip">{{downText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 空布局 -->
|
||||
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
|
||||
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="upLoadType===1">
|
||||
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
|
||||
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 适配iPhoneX -->
|
||||
<view v-if="safearea" class="mescroll-safearea"></view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS || H5 -->
|
||||
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
|
||||
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 微信小程序, app, h5使用wxs -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS || H5-->
|
||||
<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- app, h5使用renderjs -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<script module="renderBiz" lang="renderjs">
|
||||
import renderBiz from './wxs/renderjs.js';
|
||||
export default {
|
||||
mixins: [renderBiz]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<script>
|
||||
// 引入mescroll-uni.js,处理核心逻辑
|
||||
import MeScroll from './mescroll-uni.js';
|
||||
// 引入全局配置
|
||||
import GlobalOption from './mescroll-uni-option.js';
|
||||
// 引入空布局组件
|
||||
import MescrollEmpty from './components/mescroll-empty.vue';
|
||||
// 引入回到顶部组件
|
||||
import MescrollTop from './components/mescroll-top.vue';
|
||||
// 引入兼容wxs(含renderjs)写法的mixins
|
||||
import WxsMixin from './wxs/mixins.js';
|
||||
|
||||
export default {
|
||||
mixins: [WxsMixin],
|
||||
components: {
|
||||
MescrollEmpty,
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: {optDown:{},optUp:{}}, // mescroll实例
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
|
||||
downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
statusBarHeight: 0 // 状态栏高度
|
||||
};
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
minHeight(){
|
||||
return this.toPx(this.height || '100%') + 'px'
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top)
|
||||
},
|
||||
padTop() {
|
||||
return this.numTop + 'px';
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom);
|
||||
},
|
||||
padBottom() {
|
||||
return this.numBottom + 'px';
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset() {
|
||||
return this.downLoadType === 3 || this.downLoadType === 4;
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : '';
|
||||
},
|
||||
translateY() {
|
||||
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading(){
|
||||
return this.downLoadType === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate(){
|
||||
return 'rotate(' + 360 * this.downRate + 'deg)'
|
||||
},
|
||||
// 文本提示
|
||||
downText(){
|
||||
if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
|
||||
switch (this.downLoadType){
|
||||
case 1: return this.mescroll.optDown.textInOffset;
|
||||
case 2: return this.mescroll.optDown.textOutOffset;
|
||||
case 3: return this.mescroll.optDown.textLoading;
|
||||
case 4: return this.mescroll.optDown.textLoading;
|
||||
default: return this.mescroll.optDown.textInOffset;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//number,rpx,upx,px,% --> px的数值
|
||||
toPx(num) {
|
||||
if (typeof num === 'string') {
|
||||
if (num.indexOf('px') !== -1) {
|
||||
if (num.indexOf('rpx') !== -1) {
|
||||
// "10rpx"
|
||||
num = num.replace('rpx', '');
|
||||
} else if (num.indexOf('upx') !== -1) {
|
||||
// "10upx"
|
||||
num = num.replace('upx', '');
|
||||
} else {
|
||||
// "10px"
|
||||
return Number(num.replace('px', ''));
|
||||
}
|
||||
} else if (num.indexOf('%') !== -1) {
|
||||
// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
|
||||
let rate = Number(num.replace('%', '')) / 100;
|
||||
return this.windowHeight * rate;
|
||||
}
|
||||
}
|
||||
return num ? uni.upx2px(Number(num)) : 0;
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick', this.mescroll);
|
||||
},
|
||||
// 点击回到顶部的按钮回调
|
||||
toTopClick() {
|
||||
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
|
||||
this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
created() {
|
||||
let vm = this;
|
||||
|
||||
let diyOption = {
|
||||
// 下拉刷新的配置
|
||||
down: {
|
||||
inOffset() {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset() {
|
||||
vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
onMoving(mescroll, rate, downHight) {
|
||||
// 下拉过程中的回调,滑动过程一直在执行;
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
|
||||
},
|
||||
showLoading(mescroll, downHight) {
|
||||
vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
endDownScroll() {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
|
||||
vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
|
||||
if(vm.downLoadType === 4) vm.downLoadType = 0
|
||||
},300)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll);
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll(mescroll) {
|
||||
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
|
||||
},
|
||||
// 空布局
|
||||
empty: {
|
||||
onShow(isShow) {
|
||||
// 显示隐藏的回调
|
||||
vm.isShowEmpty = isShow;
|
||||
}
|
||||
},
|
||||
// 回到顶部
|
||||
toTop: {
|
||||
onShow(isShow) {
|
||||
// 显示隐藏的回调
|
||||
vm.isShowToTop = isShow;
|
||||
}
|
||||
},
|
||||
// 派发上拉加载的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('up', mescroll);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
|
||||
let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
|
||||
MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
|
||||
|
||||
// 初始化MeScroll对象
|
||||
vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
|
||||
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是page的scroll,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
if(typeof y === 'string'){
|
||||
// 滚动到指定view (y必须为元素的id,不带#)
|
||||
setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
|
||||
uni.createSelectorQuery().select('#'+y).boundingClientRect(function(rect){
|
||||
let top = rect.top
|
||||
top += vm.mescroll.getScrollTop()
|
||||
uni.pageScrollTo({
|
||||
scrollTop: top,
|
||||
duration: t
|
||||
})
|
||||
}).exec()
|
||||
},30)
|
||||
} else{
|
||||
// 滚动到指定位置 (y必须为数字)
|
||||
uni.pageScrollTo({
|
||||
scrollTop: y,
|
||||
duration: t
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "./mescroll-body.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
@import './components/mescroll-up.css';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// mescroll-body 和 mescroll-uni 通用
|
||||
|
||||
// import MescrollUni from "./mescroll-uni.vue";
|
||||
// import MescrollBody from "./mescroll-body.vue";
|
||||
|
||||
const MescrollMixin = {
|
||||
// components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
|
||||
// MescrollUni,
|
||||
// MescrollBody
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
mescroll: null //mescroll实例对象
|
||||
}
|
||||
},
|
||||
// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
|
||||
onPullDownRefresh(){
|
||||
this.mescroll && this.mescroll.onPullDownRefresh();
|
||||
},
|
||||
// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
|
||||
onPageScroll(e) {
|
||||
this.mescroll && this.mescroll.onPageScroll(e);
|
||||
},
|
||||
// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
|
||||
onReachBottom() {
|
||||
this.mescroll && this.mescroll.onReachBottom();
|
||||
},
|
||||
methods: {
|
||||
// mescroll组件初始化的回调,可获取到mescroll对象
|
||||
mescrollInit(mescroll) {
|
||||
this.mescroll = mescroll;
|
||||
this.mescrollInitByRef(); // 兼容字节跳动小程序
|
||||
},
|
||||
// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序: http://www.mescroll.com/qa.html?v=20200107#q26)
|
||||
mescrollInitByRef() {
|
||||
if(!this.mescroll || !this.mescroll.resetUpScroll){
|
||||
let mescrollRef = this.$refs.mescrollRef;
|
||||
if(mescrollRef) this.mescroll = mescrollRef.mescroll
|
||||
}
|
||||
},
|
||||
// 下拉刷新的回调 (mixin默认resetUpScroll)
|
||||
downCallback() {
|
||||
if(this.mescroll.optUp.use){
|
||||
this.mescroll.resetUpScroll()
|
||||
}else{
|
||||
setTimeout(()=>{
|
||||
this.mescroll.endSuccess();
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
// 上拉加载的回调
|
||||
upCallback() {
|
||||
// mixin默认延时500自动结束加载
|
||||
setTimeout(()=>{
|
||||
this.mescroll.endErr();
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default MescrollMixin;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 全局配置
|
||||
// mescroll-body 和 mescroll-uni 通用
|
||||
const GlobalOption = {
|
||||
down: {
|
||||
// 其他down的配置参数也可以写,这里只展示了常用的配置:
|
||||
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
|
||||
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
|
||||
native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
|
||||
},
|
||||
up: {
|
||||
// 其他up的配置参数也可以写,这里只展示了常用的配置:
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
textNoMore: '-- END --', // 没有更多数据的提示文本
|
||||
offset: 80, // 距底部多远时,触发upCallback
|
||||
toTop: {
|
||||
// 回到顶部按钮,需配置src才显示
|
||||
src: "http://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
|
||||
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
|
||||
right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
|
||||
},
|
||||
empty: {
|
||||
use: true, // 是否显示空布局
|
||||
icon: "http://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
|
||||
tip: '~ 空空如也 ~' // 提示
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GlobalOption
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
.mescroll-uni-warp{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mescroll-uni-content{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mescroll-uni {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 200rpx;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
|
||||
}
|
||||
|
||||
/* 定位的方式固定高度 */
|
||||
.mescroll-uni-fixed{
|
||||
z-index: 1;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: auto; /* 使right生效 */
|
||||
height: auto; /* 使bottom生效 */
|
||||
}
|
||||
|
||||
/* 适配 iPhoneX */
|
||||
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
|
||||
.mescroll-safearea {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,788 @@
|
|||
/* mescroll
|
||||
* version 1.3.1
|
||||
* 2020-07-27 wenju
|
||||
* http://www.mescroll.com
|
||||
*/
|
||||
|
||||
export default function MeScroll(options, isScrollBody) {
|
||||
let me = this;
|
||||
me.version = '1.3.1'; // mescroll版本号
|
||||
me.options = options || {}; // 配置
|
||||
me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
|
||||
|
||||
me.isDownScrolling = false; // 是否在执行下拉刷新的回调
|
||||
me.isUpScrolling = false; // 是否在执行上拉加载的回调
|
||||
let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
|
||||
|
||||
// 初始化下拉刷新
|
||||
me.initDownScroll();
|
||||
// 初始化上拉加载,则初始化
|
||||
me.initUpScroll();
|
||||
|
||||
// 自动加载
|
||||
setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
|
||||
// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
|
||||
if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
|
||||
if (me.optDown.autoShowLoading) {
|
||||
me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
|
||||
} else {
|
||||
me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
|
||||
}
|
||||
}
|
||||
// 自动触发上拉加载
|
||||
if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
|
||||
setTimeout(function(){
|
||||
me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
|
||||
},100)
|
||||
}
|
||||
}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
|
||||
}
|
||||
|
||||
/* 配置参数:下拉刷新 */
|
||||
MeScroll.prototype.extendDownScroll = function(optDown) {
|
||||
// 下拉刷新的配置
|
||||
MeScroll.extend(optDown, {
|
||||
use: true, // 是否启用下拉刷新; 默认true
|
||||
auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
|
||||
native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
|
||||
autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
|
||||
isLock: false, // 是否锁定下拉刷新,默认false;
|
||||
offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
|
||||
startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
|
||||
inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
|
||||
outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
|
||||
bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
|
||||
minAngle: 45, // 向下滑动最少偏移的角度,取值区间 [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
|
||||
textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
|
||||
textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
|
||||
textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
|
||||
inited: null, // 下拉刷新初始化完毕的回调
|
||||
inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
|
||||
outOffset: null, // 下拉的距离大于offset那一刻的回调
|
||||
onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
|
||||
beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
|
||||
showLoading: null, // 显示下拉刷新进度的回调
|
||||
afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
|
||||
beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
|
||||
endDownScroll: null, // 结束下拉刷新的回调
|
||||
afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
|
||||
callback: function(mescroll) {
|
||||
// 下拉刷新的回调;默认重置上拉加载列表为第一页
|
||||
mescroll.resetUpScroll();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* 配置参数:上拉加载 */
|
||||
MeScroll.prototype.extendUpScroll = function(optUp) {
|
||||
// 上拉加载的配置
|
||||
MeScroll.extend(optUp, {
|
||||
use: true, // 是否启用上拉加载; 默认true
|
||||
auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
|
||||
isLock: false, // 是否锁定上拉加载,默认false;
|
||||
isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
|
||||
callback: null, // 上拉加载的回调;function(page,mescroll){ }
|
||||
page: {
|
||||
num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
|
||||
size: 10, // 每页数据的数量
|
||||
time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
|
||||
},
|
||||
noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
|
||||
offset: 80, // 距底部多远时,触发upCallback
|
||||
textLoading: '加载中 ...', // 加载中的提示文本
|
||||
textNoMore: '-- END --', // 没有更多数据的提示文本
|
||||
bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
|
||||
textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
|
||||
inited: null, // 初始化完毕的回调
|
||||
showLoading: null, // 显示加载中的回调
|
||||
showNoMore: null, // 显示无更多数据的回调
|
||||
hideUpScroll: null, // 隐藏上拉加载的回调
|
||||
errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
|
||||
toTop: {
|
||||
// 回到顶部按钮,需配置src才显示
|
||||
src: null, // 图片路径,默认null (绝对路径或网络图)
|
||||
offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
|
||||
duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
|
||||
btnClick: null, // 点击按钮的回调
|
||||
onShow: null, // 是否显示的回调
|
||||
zIndex: 9990, // fixed定位z-index值
|
||||
left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
|
||||
right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
|
||||
bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
|
||||
safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
|
||||
width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
|
||||
radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
|
||||
},
|
||||
empty: {
|
||||
use: true, // 是否显示空布局
|
||||
icon: null, // 图标路径
|
||||
tip: '~ 暂无相关数据 ~', // 提示
|
||||
btnText: '', // 按钮
|
||||
btnClick: null, // 点击按钮的回调
|
||||
onShow: null, // 是否显示的回调
|
||||
fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
|
||||
top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
|
||||
zIndex: 99 // fixed定位z-index值
|
||||
},
|
||||
onScroll: false // 是否监听滚动事件
|
||||
})
|
||||
}
|
||||
|
||||
/* 配置参数 */
|
||||
MeScroll.extend = function(userOption, defaultOption) {
|
||||
if (!userOption) return defaultOption;
|
||||
for (let key in defaultOption) {
|
||||
if (userOption[key] == null) {
|
||||
let def = defaultOption[key];
|
||||
if (def != null && typeof def === 'object') {
|
||||
userOption[key] = MeScroll.extend({}, def); // 深度匹配
|
||||
} else {
|
||||
userOption[key] = def;
|
||||
}
|
||||
} else if (typeof userOption[key] === 'object') {
|
||||
MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
|
||||
}
|
||||
}
|
||||
return userOption;
|
||||
}
|
||||
|
||||
/* 简单判断是否配置了颜色 (非透明,非白色) */
|
||||
MeScroll.prototype.hasColor = function(color) {
|
||||
if(!color) return false;
|
||||
let c = color.toLowerCase();
|
||||
return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
|
||||
}
|
||||
|
||||
/* -------初始化下拉刷新------- */
|
||||
MeScroll.prototype.initDownScroll = function() {
|
||||
let me = this;
|
||||
// 配置参数
|
||||
me.optDown = me.options.down || {};
|
||||
if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
|
||||
me.extendDownScroll(me.optDown);
|
||||
|
||||
// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
|
||||
if(me.isScrollBody && me.optDown.native){
|
||||
me.optDown.use = false
|
||||
}else{
|
||||
me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
|
||||
}
|
||||
|
||||
me.downHight = 0; // 下拉区域的高度
|
||||
|
||||
// 在页面中加入下拉布局
|
||||
if (me.optDown.use && me.optDown.inited) {
|
||||
// 初始化完毕的回调
|
||||
setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
|
||||
me.optDown.inited(me);
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/* 列表touchstart事件 */
|
||||
MeScroll.prototype.touchstartEvent = function(e) {
|
||||
if (!this.optDown.use) return;
|
||||
|
||||
this.startPoint = this.getPoint(e); // 记录起点
|
||||
this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
|
||||
this.startAngle = 0; // 初始角度
|
||||
this.lastPoint = this.startPoint; // 重置上次move的点
|
||||
this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
|
||||
this.inTouchend = false; // 标记不是touchend
|
||||
}
|
||||
|
||||
/* 列表touchmove事件 */
|
||||
MeScroll.prototype.touchmoveEvent = function(e) {
|
||||
if (!this.optDown.use) return;
|
||||
let me = this;
|
||||
|
||||
let scrollTop = me.getScrollTop(); // 当前滚动条的距离
|
||||
let curPoint = me.getPoint(e); // 当前点
|
||||
|
||||
let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
|
||||
// 向下拉 && 在顶部
|
||||
// mescroll-body,直接判定在顶部即可
|
||||
// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
|
||||
// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
|
||||
if (moveY > 0 && (
|
||||
(me.isScrollBody && scrollTop <= 0)
|
||||
||
|
||||
(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
|
||||
)) {
|
||||
// 可下拉的条件
|
||||
if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
|
||||
me.optUp.isBoth))) {
|
||||
|
||||
// 下拉的初始角度是否在配置的范围内
|
||||
if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
|
||||
if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
|
||||
|
||||
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
|
||||
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
|
||||
me.inTouchend = true; // 标记执行touchend
|
||||
me.touchendEvent(); // 提前触发touchend
|
||||
return;
|
||||
}
|
||||
|
||||
me.preventDefault(e); // 阻止默认事件
|
||||
|
||||
let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
|
||||
|
||||
// 下拉距离 < 指定距离
|
||||
if (me.downHight < me.optDown.offset) {
|
||||
if (me.movetype !== 1) {
|
||||
me.movetype = 1; // 加入标记,保证只执行一次
|
||||
me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
|
||||
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
|
||||
}
|
||||
me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
|
||||
|
||||
// 指定距离 <= 下拉距离
|
||||
} else {
|
||||
if (me.movetype !== 2) {
|
||||
me.movetype = 2; // 加入标记,保证只执行一次
|
||||
me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
|
||||
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
|
||||
}
|
||||
if (diff > 0) { // 向下拉
|
||||
me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
|
||||
} else { // 向上收
|
||||
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
|
||||
}
|
||||
}
|
||||
|
||||
me.downHight = Math.round(me.downHight) // 取整
|
||||
let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
|
||||
me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
|
||||
}
|
||||
}
|
||||
|
||||
me.lastPoint = curPoint; // 记录本次移动的点
|
||||
}
|
||||
|
||||
/* 列表touchend事件 */
|
||||
MeScroll.prototype.touchendEvent = function(e) {
|
||||
if (!this.optDown.use) return;
|
||||
// 如果下拉区域高度已改变,则需重置回来
|
||||
if (this.isMoveDown) {
|
||||
if (this.downHight >= this.optDown.offset) {
|
||||
// 符合触发刷新的条件
|
||||
this.triggerDownScroll();
|
||||
} else {
|
||||
// 不符合的话 则重置
|
||||
this.downHight = 0;
|
||||
this.endDownScrollCall(this);
|
||||
}
|
||||
this.movetype = 0;
|
||||
this.isMoveDown = false;
|
||||
} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
|
||||
let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
// 上滑
|
||||
if (isScrollUp) {
|
||||
// 需检查滑动的角度
|
||||
let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
|
||||
if (angle > 80) {
|
||||
// 检查并触发上拉
|
||||
this.triggerUpScroll(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 根据点击滑动事件获取第一个手指的坐标 */
|
||||
MeScroll.prototype.getPoint = function(e) {
|
||||
if (!e) {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
if (e.touches && e.touches[0]) {
|
||||
return {
|
||||
x: e.touches[0].pageX,
|
||||
y: e.touches[0].pageY
|
||||
}
|
||||
} else if (e.changedTouches && e.changedTouches[0]) {
|
||||
return {
|
||||
x: e.changedTouches[0].pageX,
|
||||
y: e.changedTouches[0].pageY
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 计算两点之间的角度: 区间 [0,90]*/
|
||||
MeScroll.prototype.getAngle = function(p1, p2) {
|
||||
let x = Math.abs(p1.x - p2.x);
|
||||
let y = Math.abs(p1.y - p2.y);
|
||||
let z = Math.sqrt(x * x + y * y);
|
||||
let angle = 0;
|
||||
if (z !== 0) {
|
||||
angle = Math.asin(y / z) / Math.PI * 180;
|
||||
}
|
||||
return angle
|
||||
}
|
||||
|
||||
/* 触发下拉刷新 */
|
||||
MeScroll.prototype.triggerDownScroll = function() {
|
||||
if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
|
||||
//return true则处于完全自定义状态
|
||||
} else {
|
||||
this.showDownScroll(); // 下拉刷新中...
|
||||
!this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示下拉进度布局 */
|
||||
MeScroll.prototype.showDownScroll = function() {
|
||||
this.isDownScrolling = true; // 标记下拉中
|
||||
if (this.optDown.native) {
|
||||
uni.startPullDownRefresh(); // 系统自带的下拉刷新
|
||||
this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
|
||||
} else{
|
||||
this.downHight = this.optDown.offset; // 更新下拉区域高度
|
||||
this.showDownLoadingCall(this.downHight); // 下拉刷新中...
|
||||
}
|
||||
}
|
||||
|
||||
MeScroll.prototype.showDownLoadingCall = function(downHight) {
|
||||
this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
|
||||
this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
|
||||
}
|
||||
|
||||
/* 显示系统自带的下拉刷新时需要处理的业务 */
|
||||
MeScroll.prototype.onPullDownRefresh = function() {
|
||||
this.isDownScrolling = true; // 标记下拉中
|
||||
this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
|
||||
this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
|
||||
}
|
||||
|
||||
/* 结束下拉刷新 */
|
||||
MeScroll.prototype.endDownScroll = function() {
|
||||
if (this.optDown.native) { // 结束原生下拉刷新
|
||||
this.isDownScrolling = false;
|
||||
this.endDownScrollCall(this);
|
||||
uni.stopPullDownRefresh();
|
||||
return
|
||||
}
|
||||
let me = this;
|
||||
// 结束下拉刷新的方法
|
||||
let endScroll = function() {
|
||||
me.downHight = 0;
|
||||
me.isDownScrolling = false;
|
||||
me.endDownScrollCall(me);
|
||||
if(!me.isScrollBody){
|
||||
me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
|
||||
me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
|
||||
}
|
||||
}
|
||||
// 结束下拉刷新时的回调
|
||||
let delay = 0;
|
||||
if (me.optDown.beforeEndDownScroll) delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
|
||||
if (typeof delay === 'number' && delay > 0) {
|
||||
setTimeout(endScroll, delay);
|
||||
} else {
|
||||
endScroll();
|
||||
}
|
||||
}
|
||||
|
||||
MeScroll.prototype.endDownScrollCall = function() {
|
||||
this.optDown.endDownScroll && this.optDown.endDownScroll(this);
|
||||
this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
|
||||
}
|
||||
|
||||
/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
|
||||
MeScroll.prototype.lockDownScroll = function(isLock) {
|
||||
if (isLock == null) isLock = true;
|
||||
this.optDown.isLock = isLock;
|
||||
}
|
||||
|
||||
/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
|
||||
MeScroll.prototype.lockUpScroll = function(isLock) {
|
||||
if (isLock == null) isLock = true;
|
||||
this.optUp.isLock = isLock;
|
||||
}
|
||||
|
||||
/* -------初始化上拉加载------- */
|
||||
MeScroll.prototype.initUpScroll = function() {
|
||||
let me = this;
|
||||
// 配置参数
|
||||
me.optUp = me.options.up || {use: false}
|
||||
if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
|
||||
me.extendUpScroll(me.optUp);
|
||||
|
||||
if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
|
||||
me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
|
||||
me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
|
||||
|
||||
// 初始化完毕的回调
|
||||
if (me.optUp.inited) {
|
||||
setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
|
||||
me.optUp.inited(me);
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/*滚动到底部的事件 (仅mescroll-body生效)*/
|
||||
MeScroll.prototype.onReachBottom = function() {
|
||||
if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
|
||||
if (!this.optUp.isLock && this.optUp.hasNext) {
|
||||
this.triggerUpScroll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*列表滚动事件 (仅mescroll-body生效)*/
|
||||
MeScroll.prototype.onPageScroll = function(e) {
|
||||
if (!this.isScrollBody) return;
|
||||
|
||||
// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
|
||||
this.setScrollTop(e.scrollTop);
|
||||
|
||||
// 顶部按钮的显示隐藏
|
||||
if (e.scrollTop >= this.optUp.toTop.offset) {
|
||||
this.showTopBtn();
|
||||
} else {
|
||||
this.hideTopBtn();
|
||||
}
|
||||
}
|
||||
|
||||
/*列表滚动事件*/
|
||||
MeScroll.prototype.scroll = function(e, onScroll) {
|
||||
// 更新滚动条的位置
|
||||
this.setScrollTop(e.scrollTop);
|
||||
// 更新滚动内容高度
|
||||
this.setScrollHeight(e.scrollHeight);
|
||||
|
||||
// 向上滑还是向下滑动
|
||||
if (this.preScrollY == null) this.preScrollY = 0;
|
||||
this.isScrollUp = e.scrollTop - this.preScrollY > 0;
|
||||
this.preScrollY = e.scrollTop;
|
||||
|
||||
// 上滑 && 检查并触发上拉
|
||||
this.isScrollUp && this.triggerUpScroll(true);
|
||||
|
||||
// 顶部按钮的显示隐藏
|
||||
if (e.scrollTop >= this.optUp.toTop.offset) {
|
||||
this.showTopBtn();
|
||||
} else {
|
||||
this.hideTopBtn();
|
||||
}
|
||||
|
||||
// 滑动监听
|
||||
this.optUp.onScroll && onScroll && onScroll()
|
||||
}
|
||||
|
||||
/* 触发上拉加载 */
|
||||
MeScroll.prototype.triggerUpScroll = function(isCheck) {
|
||||
if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
|
||||
// 是否校验在底部; 默认不校验
|
||||
if (isCheck === true) {
|
||||
let canUp = false;
|
||||
// 还有下一页 && 没有锁定 && 不在下拉中
|
||||
if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
|
||||
if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
|
||||
canUp = true; // 标记可上拉
|
||||
}
|
||||
}
|
||||
if (canUp === false) return;
|
||||
}
|
||||
this.showUpScroll(); // 上拉加载中...
|
||||
this.optUp.page.num++; // 预先加一页,如果失败则减回
|
||||
this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
|
||||
this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
|
||||
this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
|
||||
this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
|
||||
this.optUp.callback(this); // 执行回调,联网加载数据
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示上拉加载中 */
|
||||
MeScroll.prototype.showUpScroll = function() {
|
||||
this.isUpScrolling = true; // 标记上拉加载中
|
||||
this.optUp.showLoading && this.optUp.showLoading(this); // 回调
|
||||
}
|
||||
|
||||
/* 显示上拉无更多数据 */
|
||||
MeScroll.prototype.showNoMore = function() {
|
||||
this.optUp.hasNext = false; // 标记无更多数据
|
||||
this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
|
||||
}
|
||||
|
||||
/* 隐藏上拉区域**/
|
||||
MeScroll.prototype.hideUpScroll = function() {
|
||||
this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
|
||||
}
|
||||
|
||||
/* 结束上拉加载 */
|
||||
MeScroll.prototype.endUpScroll = function(isShowNoMore) {
|
||||
if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
|
||||
if (isShowNoMore) {
|
||||
this.showNoMore(); // isShowNoMore=true,显示无更多数据
|
||||
} else {
|
||||
this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
|
||||
}
|
||||
}
|
||||
this.isUpScrolling = false; // 标记结束上拉加载
|
||||
}
|
||||
|
||||
/* 重置上拉加载列表为第一页
|
||||
*isShowLoading 是否显示进度布局;
|
||||
* 1.默认null,不传参,则显示上拉加载的进度布局
|
||||
* 2.传参true, 则显示下拉刷新的进度布局
|
||||
* 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
|
||||
*/
|
||||
MeScroll.prototype.resetUpScroll = function(isShowLoading) {
|
||||
if (this.optUp && this.optUp.use) {
|
||||
let page = this.optUp.page;
|
||||
this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
|
||||
this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
|
||||
page.num = this.startNum; // 重置为第一页
|
||||
page.time = null; // 重置时间为空
|
||||
if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
|
||||
if (isShowLoading == null) {
|
||||
this.removeEmpty(); // 移除空布局
|
||||
this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
|
||||
} else {
|
||||
this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
|
||||
}
|
||||
}
|
||||
this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
|
||||
this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
|
||||
this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
|
||||
this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
|
||||
this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置page.num的值 */
|
||||
MeScroll.prototype.setPageNum = function(num) {
|
||||
this.optUp.page.num = num - 1;
|
||||
}
|
||||
|
||||
/* 设置page.size的值 */
|
||||
MeScroll.prototype.setPageSize = function(size) {
|
||||
this.optUp.page.size = size;
|
||||
}
|
||||
|
||||
/* 联网回调成功,结束下拉刷新和上拉加载
|
||||
* dataSize: 当前页的数据量(必传)
|
||||
* totalPage: 总页数(必传)
|
||||
* systime: 服务器时间 (可空)
|
||||
*/
|
||||
MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
|
||||
let hasNext;
|
||||
if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
|
||||
this.endSuccess(dataSize, hasNext, systime);
|
||||
}
|
||||
|
||||
/* 联网回调成功,结束下拉刷新和上拉加载
|
||||
* dataSize: 当前页的数据量(必传)
|
||||
* totalSize: 列表所有数据总数量(必传)
|
||||
* systime: 服务器时间 (可空)
|
||||
*/
|
||||
MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
|
||||
let hasNext;
|
||||
if (this.optUp.use && totalSize != null) {
|
||||
let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
|
||||
hasNext = loadSize < totalSize; // 是否还有下一页
|
||||
}
|
||||
this.endSuccess(dataSize, hasNext, systime);
|
||||
}
|
||||
|
||||
/* 联网回调成功,结束下拉刷新和上拉加载
|
||||
* dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
|
||||
* hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
|
||||
* systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
|
||||
*/
|
||||
MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
|
||||
let me = this;
|
||||
// 结束下拉刷新
|
||||
if (me.isDownScrolling) me.endDownScroll();
|
||||
|
||||
// 结束上拉加载
|
||||
if (me.optUp.use) {
|
||||
let isShowNoMore; // 是否已无更多数据
|
||||
if (dataSize != null) {
|
||||
let pageNum = me.optUp.page.num; // 当前页码
|
||||
let pageSize = me.optUp.page.size; // 每页长度
|
||||
// 如果是第一页
|
||||
if (pageNum === 1) {
|
||||
if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
|
||||
}
|
||||
if (dataSize < pageSize || hasNext === false) {
|
||||
// 返回的数据不满一页时,则说明已无更多数据
|
||||
me.optUp.hasNext = false;
|
||||
if (dataSize === 0 && pageNum === 1) {
|
||||
// 如果第一页无任何数据且配置了空布局
|
||||
isShowNoMore = false;
|
||||
me.showEmpty();
|
||||
} else {
|
||||
// 总列表数少于配置的数量,则不显示无更多数据
|
||||
let allDataSize = (pageNum - 1) * pageSize + dataSize;
|
||||
if (allDataSize < me.optUp.noMoreSize) {
|
||||
isShowNoMore = false;
|
||||
} else {
|
||||
isShowNoMore = true;
|
||||
}
|
||||
me.removeEmpty(); // 移除空布局
|
||||
}
|
||||
} else {
|
||||
// 还有下一页
|
||||
isShowNoMore = false;
|
||||
me.optUp.hasNext = true;
|
||||
me.removeEmpty(); // 移除空布局
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏上拉
|
||||
me.endUpScroll(isShowNoMore);
|
||||
}
|
||||
}
|
||||
|
||||
/* 回调失败,结束下拉刷新和上拉加载 */
|
||||
MeScroll.prototype.endErr = function(errDistance) {
|
||||
// 结束下拉,回调失败重置回原来的页码和时间
|
||||
if (this.isDownScrolling) {
|
||||
let page = this.optUp.page;
|
||||
if (page && this.prePageNum) {
|
||||
page.num = this.prePageNum;
|
||||
page.time = this.prePageTime;
|
||||
}
|
||||
this.endDownScroll();
|
||||
}
|
||||
// 结束上拉,回调失败重置回原来的页码
|
||||
if (this.isUpScrolling) {
|
||||
this.optUp.page.num--;
|
||||
this.endUpScroll(false);
|
||||
// 如果是mescroll-body,则需往回滚一定距离
|
||||
if(this.isScrollBody && errDistance !== 0){ // 不处理0
|
||||
if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
|
||||
this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示空布局 */
|
||||
MeScroll.prototype.showEmpty = function() {
|
||||
this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
|
||||
}
|
||||
|
||||
/* 移除空布局 */
|
||||
MeScroll.prototype.removeEmpty = function() {
|
||||
this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
|
||||
}
|
||||
|
||||
/* 显示回到顶部的按钮 */
|
||||
MeScroll.prototype.showTopBtn = function() {
|
||||
if (!this.topBtnShow) {
|
||||
this.topBtnShow = true;
|
||||
this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* 隐藏回到顶部的按钮 */
|
||||
MeScroll.prototype.hideTopBtn = function() {
|
||||
if (this.topBtnShow) {
|
||||
this.topBtnShow = false;
|
||||
this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取滚动条的位置 */
|
||||
MeScroll.prototype.getScrollTop = function() {
|
||||
return this.scrollTop || 0
|
||||
}
|
||||
|
||||
/* 记录滚动条的位置 */
|
||||
MeScroll.prototype.setScrollTop = function(y) {
|
||||
this.scrollTop = y;
|
||||
}
|
||||
|
||||
/* 滚动到指定位置 */
|
||||
MeScroll.prototype.scrollTo = function(y, t) {
|
||||
this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
|
||||
}
|
||||
|
||||
/* 自定义scrollTo */
|
||||
MeScroll.prototype.resetScrollTo = function(myScrollTo) {
|
||||
this.myScrollTo = myScrollTo
|
||||
}
|
||||
|
||||
/* 滚动条到底部的距离 */
|
||||
MeScroll.prototype.getScrollBottom = function() {
|
||||
return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
|
||||
}
|
||||
|
||||
/* 计步器
|
||||
star: 开始值
|
||||
end: 结束值
|
||||
callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
|
||||
t: 计步时长,传0则直接回调end值;不传则默认300ms
|
||||
rate: 周期;不传则默认30ms计步一次
|
||||
* */
|
||||
MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
|
||||
let diff = end - star; // 差值
|
||||
if (t === 0 || diff === 0) {
|
||||
callback && callback(end);
|
||||
return;
|
||||
}
|
||||
t = t || 300; // 时长 300ms
|
||||
rate = rate || 30; // 周期 30ms
|
||||
let count = t / rate; // 次数
|
||||
let step = diff / count; // 步长
|
||||
let i = 0; // 计数
|
||||
let timer = setInterval(function() {
|
||||
if (i < count - 1) {
|
||||
star += step;
|
||||
callback && callback(star, timer);
|
||||
i++;
|
||||
} else {
|
||||
callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, rate);
|
||||
}
|
||||
|
||||
/* 滚动容器的高度 */
|
||||
MeScroll.prototype.getClientHeight = function(isReal) {
|
||||
let h = this.clientHeight || 0
|
||||
if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
|
||||
h = this.getBodyHeight()
|
||||
}
|
||||
return h
|
||||
}
|
||||
MeScroll.prototype.setClientHeight = function(h) {
|
||||
this.clientHeight = h;
|
||||
}
|
||||
|
||||
/* 滚动内容的高度 */
|
||||
MeScroll.prototype.getScrollHeight = function() {
|
||||
return this.scrollHeight || 0;
|
||||
}
|
||||
MeScroll.prototype.setScrollHeight = function(h) {
|
||||
this.scrollHeight = h;
|
||||
}
|
||||
|
||||
/* body的高度 */
|
||||
MeScroll.prototype.getBodyHeight = function() {
|
||||
return this.bodyHeight || 0;
|
||||
}
|
||||
MeScroll.prototype.setBodyHeight = function(h) {
|
||||
this.bodyHeight = h;
|
||||
}
|
||||
|
||||
/* 阻止浏览器默认滚动事件 */
|
||||
MeScroll.prototype.preventDefault = function(e) {
|
||||
// 小程序不支持e.preventDefault, 已在wxs中禁止
|
||||
// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
|
||||
// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
|
||||
if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
|
||||
}
|
||||
|
|
@ -0,0 +1,413 @@
|
|||
<template>
|
||||
<view class="mescroll-uni-warp">
|
||||
<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-into-view="scrollToViewId" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true">
|
||||
<view class="mescroll-uni-content mescroll-render-touch"
|
||||
@touchstart="wxsBiz.touchstartEvent"
|
||||
@touchmove="wxsBiz.touchmoveEvent"
|
||||
@touchend="wxsBiz.touchendEvent"
|
||||
@touchcancel="wxsBiz.touchendEvent"
|
||||
:change:prop="wxsBiz.propObserver"
|
||||
:prop="wxsProp">
|
||||
<!-- 状态栏 -->
|
||||
<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
|
||||
|
||||
<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
|
||||
<view class="downwarp-tip">{{downText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 空布局 -->
|
||||
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
|
||||
|
||||
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
|
||||
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="upLoadType===1">
|
||||
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
|
||||
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
|
||||
</view>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 适配iPhoneX -->
|
||||
<view v-if="safearea" class="mescroll-safearea"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS || H5 -->
|
||||
<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
|
||||
<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 微信小程序, app, h5使用wxs -->
|
||||
<!-- #ifdef MP-WEIXIN || APP-PLUS || H5-->
|
||||
<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- app, h5使用renderjs -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<script module="renderBiz" lang="renderjs">
|
||||
import renderBiz from './wxs/renderjs.js';
|
||||
export default {
|
||||
mixins:[renderBiz]
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<script>
|
||||
// 引入mescroll-uni.js,处理核心逻辑
|
||||
import MeScroll from './mescroll-uni.js';
|
||||
// 引入全局配置
|
||||
import GlobalOption from './mescroll-uni-option.js';
|
||||
// 引入空布局组件
|
||||
import MescrollEmpty from './components/mescroll-empty.vue';
|
||||
// 引入回到顶部组件
|
||||
import MescrollTop from './components/mescroll-top.vue';
|
||||
// 引入兼容wxs(含renderjs)写法的mixins
|
||||
import WxsMixin from './wxs/mixins.js';
|
||||
|
||||
export default {
|
||||
mixins: [WxsMixin],
|
||||
components: {
|
||||
MescrollEmpty,
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: {optDown:{},optUp:{}}, // mescroll实例
|
||||
viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
|
||||
downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
|
||||
upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
scrollTop: 0, // 滚动条的位置
|
||||
scrollAnim: false, // 是否开启滚动动画
|
||||
windowTop: 0, // 可使用窗口的顶部位置
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
statusBarHeight: 0, // 状态栏高度
|
||||
scrollToViewId: '' // 滚动到指定view的id
|
||||
}
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
fixed: { // 是否通过fixed固定mescroll的高度, 默认true
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 是否使用fixed定位 (当height有值,则不使用)
|
||||
isFixed(){
|
||||
return !this.height && this.fixed
|
||||
},
|
||||
// mescroll的高度
|
||||
scrollHeight(){
|
||||
if (this.isFixed) {
|
||||
return "auto"
|
||||
} else if(this.height){
|
||||
return this.toPx(this.height) + 'px'
|
||||
}else{
|
||||
return "100%"
|
||||
}
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top)
|
||||
},
|
||||
fixedTop() {
|
||||
return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
|
||||
},
|
||||
padTop() {
|
||||
return !this.isFixed ? this.numTop + 'px' : 0
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom)
|
||||
},
|
||||
fixedBottom() {
|
||||
return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
|
||||
},
|
||||
padBottom() {
|
||||
return !this.isFixed ? this.numBottom + 'px' : 0
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset(){
|
||||
return this.downLoadType===3 || this.downLoadType===4
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : '';
|
||||
},
|
||||
translateY() {
|
||||
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 列表是否可滑动
|
||||
scrollable(){
|
||||
return this.downLoadType===0 || this.isDownReset
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading(){
|
||||
return this.downLoadType === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate(){
|
||||
return 'rotate(' + 360 * this.downRate + 'deg)'
|
||||
},
|
||||
// 文本提示
|
||||
downText(){
|
||||
if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
|
||||
switch (this.downLoadType){
|
||||
case 1: return this.mescroll.optDown.textInOffset;
|
||||
case 2: return this.mescroll.optDown.textOutOffset;
|
||||
case 3: return this.mescroll.optDown.textLoading;
|
||||
case 4: return this.mescroll.optDown.textLoading;
|
||||
default: return this.mescroll.optDown.textInOffset;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//number,rpx,upx,px,% --> px的数值
|
||||
toPx(num){
|
||||
if(typeof num === "string"){
|
||||
if (num.indexOf('px') !== -1) {
|
||||
if(num.indexOf('rpx') !== -1) { // "10rpx"
|
||||
num = num.replace('rpx', '');
|
||||
} else if(num.indexOf('upx') !== -1) { // "10upx"
|
||||
num = num.replace('upx', '');
|
||||
} else { // "10px"
|
||||
return Number(num.replace('px', ''))
|
||||
}
|
||||
}else if (num.indexOf('%') !== -1){
|
||||
// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
|
||||
let rate = Number(num.replace("%","")) / 100
|
||||
return this.windowHeight * rate
|
||||
}
|
||||
}
|
||||
return num ? uni.upx2px(Number(num)) : 0
|
||||
},
|
||||
//注册列表滚动事件,用于下拉刷新和上拉加载
|
||||
scroll(e) {
|
||||
this.mescroll.scroll(e.detail, () => {
|
||||
this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
|
||||
})
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
emptyClick() {
|
||||
this.$emit('emptyclick', this.mescroll)
|
||||
},
|
||||
// 点击回到顶部的按钮回调
|
||||
toTopClick() {
|
||||
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
|
||||
this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
|
||||
},
|
||||
// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
|
||||
setClientHeight() {
|
||||
if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
|
||||
this.isExec = true; // 避免多次获取
|
||||
this.$nextTick(() => { // 确保dom已渲染
|
||||
let query = uni.createSelectorQuery();
|
||||
// #ifndef MP-ALIPAY
|
||||
query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
|
||||
// #endif
|
||||
let view = query.select('#' + this.viewId);
|
||||
view.boundingClientRect(data => {
|
||||
this.isExec = false;
|
||||
if (data) {
|
||||
this.mescroll.setClientHeight(data.height);
|
||||
} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
|
||||
this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
|
||||
setTimeout(() => {
|
||||
this.setClientHeight()
|
||||
}, this.clientNum * 100)
|
||||
}
|
||||
}).exec();
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
created() {
|
||||
let vm = this;
|
||||
|
||||
let diyOption = {
|
||||
// 下拉刷新的配置
|
||||
down: {
|
||||
inOffset() {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset() {
|
||||
vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
onMoving(mescroll, rate, downHight) {
|
||||
// 下拉过程中的回调,滑动过程一直在执行;
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
|
||||
},
|
||||
showLoading(mescroll, downHight) {
|
||||
vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
endDownScroll() {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
vm.downResetTimer && clearTimeout(vm.downResetTimer)
|
||||
vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
|
||||
if(vm.downLoadType===4) vm.downLoadType = 0
|
||||
},300)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll)
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll(mescroll) {
|
||||
vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
|
||||
},
|
||||
// 空布局
|
||||
empty: {
|
||||
onShow(isShow) { // 显示隐藏的回调
|
||||
vm.isShowEmpty = isShow;
|
||||
}
|
||||
},
|
||||
// 回到顶部
|
||||
toTop: {
|
||||
onShow(isShow) { // 显示隐藏的回调
|
||||
vm.isShowToTop = isShow;
|
||||
}
|
||||
},
|
||||
// 派发上拉加载的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('up', mescroll);
|
||||
// 更新容器的高度 (多mescroll的情况)
|
||||
vm.setClientHeight()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
|
||||
let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
|
||||
MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
|
||||
|
||||
// 初始化MeScroll对象
|
||||
vm.mescroll = new MeScroll(myOption);
|
||||
vm.mescroll.viewId = vm.viewId; // 附带id
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if(sys.windowTop) vm.windowTop = sys.windowTop;
|
||||
if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
|
||||
if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是scrollview,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
|
||||
if(typeof y === 'string'){ // 第一个参数如果为字符串,则使用scroll-into-view
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序暂不支持slot里面的scroll-into-view,只能计算位置实现
|
||||
uni.createSelectorQuery().select('#'+vm.viewId).boundingClientRect(function(rect){
|
||||
let mescrollTop = rect.top // mescroll到顶部的距离
|
||||
uni.createSelectorQuery().select('#'+y).boundingClientRect(function(rect){
|
||||
let curY = vm.mescroll.getScrollTop()
|
||||
let top = rect.top - mescrollTop
|
||||
top += curY
|
||||
if(!vm.isFixed) top -= vm.numTop
|
||||
vm.scrollTop = curY;
|
||||
vm.$nextTick(function() {
|
||||
vm.scrollTop = top
|
||||
})
|
||||
}).exec()
|
||||
}).exec()
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
if (vm.scrollToViewId != y) {
|
||||
vm.scrollToViewId = y;
|
||||
} else{
|
||||
vm.scrollToViewId = ''; // scrollToViewId必须变化才会生效,所以此处先置空再赋值
|
||||
vm.$nextTick(function(){
|
||||
vm.scrollToViewId = y;
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
return;
|
||||
}
|
||||
let curY = vm.mescroll.getScrollTop()
|
||||
if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
|
||||
vm.scrollTop = curY;
|
||||
vm.$nextTick(function() {
|
||||
vm.scrollTop = y
|
||||
})
|
||||
} else {
|
||||
vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
|
||||
vm.scrollTop = step
|
||||
}, t)
|
||||
}
|
||||
})
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 设置容器的高度
|
||||
this.setClientHeight()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "./mescroll-uni.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
@import './components/mescroll-up.css';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
|
||||
* 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
|
||||
* 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
|
||||
*/
|
||||
const MescrollCompMixin = {
|
||||
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
|
||||
onPageScroll(e) {
|
||||
this.handlePageScroll(e)
|
||||
},
|
||||
onReachBottom() {
|
||||
this.handleReachBottom()
|
||||
},
|
||||
// 当down的native: true时, 还需传递此方法进到子组件
|
||||
onPullDownRefresh(){
|
||||
this.handlePullDownRefresh()
|
||||
},
|
||||
// mescroll-body写在子子子...组件的情况 (多级)
|
||||
data() {
|
||||
return {
|
||||
mescroll: {
|
||||
onPageScroll: e=>{
|
||||
this.handlePageScroll(e)
|
||||
},
|
||||
onReachBottom: ()=>{
|
||||
this.handleReachBottom()
|
||||
},
|
||||
onPullDownRefresh: ()=>{
|
||||
this.handlePullDownRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
handlePageScroll(e){
|
||||
let item = this.$refs["mescrollItem"];
|
||||
if(item && item.mescroll) item.mescroll.onPageScroll(e);
|
||||
},
|
||||
handleReachBottom(){
|
||||
let item = this.$refs["mescrollItem"];
|
||||
if(item && item.mescroll) item.mescroll.onReachBottom();
|
||||
},
|
||||
handlePullDownRefresh(){
|
||||
let item = this.$refs["mescrollItem"];
|
||||
if(item && item.mescroll) item.mescroll.onPullDownRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MescrollCompMixin;
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
|
||||
*/
|
||||
const MescrollMoreItemMixin = {
|
||||
// 支付宝小程序不支持props的mixin,需写在具体的页面中
|
||||
// #ifndef MP-ALIPAY
|
||||
props:{
|
||||
i: Number, // 每个tab页的专属下标
|
||||
index: { // 当前tab的下标
|
||||
type: Number,
|
||||
default(){
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
data() {
|
||||
return {
|
||||
downOption:{
|
||||
auto:false // 不自动加载
|
||||
},
|
||||
upOption:{
|
||||
auto:false // 不自动加载
|
||||
},
|
||||
isInit: false // 当前tab是否已初始化
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
// 监听下标的变化
|
||||
index(val){
|
||||
if (this.i === val && !this.isInit) {
|
||||
this.isInit = true; // 标记为true
|
||||
this.mescroll && this.mescroll.triggerDownScroll();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
|
||||
mescrollInit(mescroll) {
|
||||
this.mescroll = mescroll;
|
||||
this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
|
||||
// 自动加载当前tab的数据
|
||||
if(this.i === this.index){
|
||||
this.isInit = true; // 标记为true
|
||||
this.mescroll.triggerDownScroll();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default MescrollMoreItemMixin;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
|
||||
* 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
|
||||
* 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
|
||||
*/
|
||||
const MescrollMoreMixin = {
|
||||
data() {
|
||||
return {
|
||||
tabIndex: 0 // 当前tab下标
|
||||
}
|
||||
},
|
||||
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
|
||||
onPageScroll(e) {
|
||||
let mescroll = this.getMescroll(this.tabIndex);
|
||||
mescroll && mescroll.onPageScroll(e);
|
||||
},
|
||||
onReachBottom() {
|
||||
let mescroll = this.getMescroll(this.tabIndex);
|
||||
mescroll && mescroll.onReachBottom();
|
||||
},
|
||||
// 当down的native: true时, 还需传递此方法进到子组件
|
||||
onPullDownRefresh(){
|
||||
let mescroll = this.getMescroll(this.tabIndex);
|
||||
mescroll && mescroll.onPullDownRefresh();
|
||||
},
|
||||
methods:{
|
||||
// 根据下标获取对应子组件的mescroll
|
||||
getMescroll(i){
|
||||
if(!this.mescrollItems) this.mescrollItems = [];
|
||||
if(!this.mescrollItems[i]) {
|
||||
// v-for中的refs
|
||||
let vForItem = this.$refs["mescrollItem"];
|
||||
if(vForItem){
|
||||
this.mescrollItems[i] = vForItem[i]
|
||||
}else{
|
||||
// 普通的refs,不可重复
|
||||
this.mescrollItems[i] = this.$refs["mescrollItem"+i];
|
||||
}
|
||||
}
|
||||
let item = this.mescrollItems[i]
|
||||
return item ? item.mescroll : null
|
||||
},
|
||||
// 切换tab,恢复滚动条位置
|
||||
tabChange(i){
|
||||
let mescroll = this.getMescroll(i);
|
||||
if(mescroll){
|
||||
// 延时(比$nextTick靠谱一些),确保元素已渲染
|
||||
setTimeout(()=>{
|
||||
mescroll.scrollTo(mescroll.getScrollTop(),0)
|
||||
},30)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MescrollMoreMixin;
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
|
||||
const WxsMixin = {
|
||||
data() {
|
||||
return {
|
||||
// 传入wxs视图层的数据 (响应式)
|
||||
wxsProp: {
|
||||
optDown:{}, // 下拉刷新的配置
|
||||
scrollTop:0, // 滚动条的距离
|
||||
bodyHeight:0, // body的高度
|
||||
isDownScrolling:false, // 是否正在下拉刷新中
|
||||
isUpScrolling:false, // 是否正在上拉加载中
|
||||
isScrollBody:true, // 是否为mescroll-body滚动
|
||||
isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
|
||||
t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
|
||||
},
|
||||
|
||||
// 标记调用wxs视图层的方法
|
||||
callProp: {
|
||||
callType: '', // 方法名
|
||||
t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
|
||||
},
|
||||
|
||||
// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
|
||||
// #ifndef MP-WEIXIN || APP-PLUS || H5
|
||||
wxsBiz: {
|
||||
//注册列表touchstart事件,用于下拉刷新
|
||||
touchstartEvent: e=> {
|
||||
this.mescroll.touchstartEvent(e);
|
||||
},
|
||||
//注册列表touchmove事件,用于下拉刷新
|
||||
touchmoveEvent: e=> {
|
||||
this.mescroll.touchmoveEvent(e);
|
||||
},
|
||||
//注册列表touchend事件,用于下拉刷新
|
||||
touchendEvent: e=> {
|
||||
this.mescroll.touchendEvent(e);
|
||||
},
|
||||
propObserver(){}, // 抹平wxs的写法
|
||||
callObserver(){} // 抹平wxs的写法
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
|
||||
// #ifndef APP-PLUS || H5
|
||||
renderBiz: {
|
||||
propObserver(){} // 抹平renderjs的写法
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// wxs视图层调用逻辑层的回调
|
||||
wxsCall(msg){
|
||||
if(msg.type === 'setWxsProp'){
|
||||
// 更新wxsProp数据 (值改变才触发更新)
|
||||
this.wxsProp = {
|
||||
optDown: this.mescroll.optDown,
|
||||
scrollTop: this.mescroll.getScrollTop(),
|
||||
bodyHeight: this.mescroll.getBodyHeight(),
|
||||
isDownScrolling: this.mescroll.isDownScrolling,
|
||||
isUpScrolling: this.mescroll.isUpScrolling,
|
||||
isUpBoth: this.mescroll.optUp.isBoth,
|
||||
isScrollBody:this.mescroll.isScrollBody,
|
||||
t: Date.now()
|
||||
}
|
||||
}else if(msg.type === 'setLoadType'){
|
||||
// 设置inOffset,outOffset的状态
|
||||
this.downLoadType = msg.downLoadType
|
||||
}else if(msg.type === 'triggerDownScroll'){
|
||||
// 主动触发下拉刷新
|
||||
this.mescroll.triggerDownScroll();
|
||||
}else if(msg.type === 'endDownScroll'){
|
||||
// 结束下拉刷新
|
||||
this.mescroll.endDownScroll();
|
||||
}else if(msg.type === 'triggerUpScroll'){
|
||||
// 主动触发上拉加载
|
||||
this.mescroll.triggerUpScroll(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// #ifdef MP-WEIXIN || APP-PLUS || H5
|
||||
// 配置主动触发wxs显示加载进度的回调
|
||||
this.mescroll.optDown.afterLoading = ()=>{
|
||||
this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
|
||||
}
|
||||
// 配置主动触发wxs隐藏加载进度的回调
|
||||
this.mescroll.optDown.afterEndDownScroll = ()=>{
|
||||
this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
|
||||
setTimeout(()=>{
|
||||
if(this.downLoadType === 4 || this.downLoadType === 0){
|
||||
this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
|
||||
}
|
||||
},320)
|
||||
}
|
||||
// 初始化wxs的数据
|
||||
this.wxsCall({type: 'setWxsProp'})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
export default WxsMixin;
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
|
||||
// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
|
||||
// https://uniapp.dcloud.io/frame?id=renderjs
|
||||
|
||||
// 与wxs的me实例一致
|
||||
var me = {}
|
||||
|
||||
// 初始化window对象的touch事件 (仅初始化一次)
|
||||
if(window && !window.$mescrollRenderInit){
|
||||
window.$mescrollRenderInit = true
|
||||
|
||||
|
||||
window.addEventListener('touchstart', function(e){
|
||||
if (me.disabled()) return;
|
||||
me.startPoint = me.getPoint(e); // 记录起点
|
||||
}, {passive: true})
|
||||
|
||||
|
||||
window.addEventListener('touchmove', function(e){
|
||||
if (me.disabled()) return;
|
||||
if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
|
||||
|
||||
var curPoint = me.getPoint(e); // 当前点
|
||||
var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
// 向下拉
|
||||
if (moveY > 0) {
|
||||
// 可下拉的条件
|
||||
if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
|
||||
|
||||
// 只有touch在mescroll的view上面,才禁止bounce
|
||||
var el = e.target;
|
||||
var isMescrollTouch = false;
|
||||
while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
|
||||
var cls = el.classList;
|
||||
if (cls && cls.contains('mescroll-render-touch')) {
|
||||
isMescrollTouch = true
|
||||
break;
|
||||
}
|
||||
el = el.parentNode; // 继续检查其父元素
|
||||
}
|
||||
// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
|
||||
if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
|
||||
}
|
||||
}
|
||||
}, {passive: false})
|
||||
}
|
||||
|
||||
/* 获取滚动条的位置 */
|
||||
me.getScrollTop = function() {
|
||||
return me.scrollTop || 0
|
||||
}
|
||||
|
||||
/* 是否禁用下拉刷新 */
|
||||
me.disabled = function(){
|
||||
return !me.optDown || !me.optDown.use || me.optDown.native
|
||||
}
|
||||
|
||||
/* 根据点击滑动事件获取第一个手指的坐标 */
|
||||
me.getPoint = function(e) {
|
||||
if (!e) {
|
||||
return {x: 0,y: 0}
|
||||
}
|
||||
if (e.touches && e.touches[0]) {
|
||||
return {x: e.touches[0].pageX,y: e.touches[0].pageY}
|
||||
} else if (e.changedTouches && e.changedTouches[0]) {
|
||||
return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
|
||||
} else {
|
||||
return {x: e.clientX,y: e.clientY}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听逻辑层数据的变化 (实时更新数据)
|
||||
*/
|
||||
function propObserver(wxsProp) {
|
||||
me.optDown = wxsProp.optDown
|
||||
me.scrollTop = wxsProp.scrollTop
|
||||
me.isDownScrolling = wxsProp.isDownScrolling
|
||||
me.isUpScrolling = wxsProp.isUpScrolling
|
||||
me.isUpBoth = wxsProp.isUpBoth
|
||||
}
|
||||
|
||||
/* 导出模块 */
|
||||
const renderBiz = {
|
||||
data() {
|
||||
return {
|
||||
propObserver: propObserver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default renderBiz;
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
|
||||
// https://uniapp.dcloud.io/frame?id=wxs
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html
|
||||
|
||||
// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
|
||||
var me = {}
|
||||
|
||||
// ------ 自定义下拉刷新动画 start ------
|
||||
|
||||
/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
|
||||
me.onMoving = function (ins, rate, downHight){
|
||||
ins.requestAnimationFrame(function () {
|
||||
ins.selectComponent('.mescroll-wxs-content').setStyle({
|
||||
'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
|
||||
'transform': 'translateY(' + downHight + 'px)',
|
||||
'transition': ''
|
||||
})
|
||||
// 环形进度条
|
||||
var progress = ins.selectComponent('.mescroll-wxs-progress')
|
||||
progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
|
||||
})
|
||||
}
|
||||
|
||||
/* 显示下拉刷新进度 */
|
||||
me.showLoading = function (ins){
|
||||
me.downHight = me.optDown.offset
|
||||
ins.requestAnimationFrame(function () {
|
||||
ins.selectComponent('.mescroll-wxs-content').setStyle({
|
||||
'will-change': 'auto',
|
||||
'transform': 'translateY(' + me.downHight + 'px)',
|
||||
'transition': 'transform 300ms'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/* 结束下拉 */
|
||||
me.endDownScroll = function (ins){
|
||||
me.downHight = 0;
|
||||
me.isDownScrolling = false;
|
||||
ins.requestAnimationFrame(function () {
|
||||
ins.selectComponent('.mescroll-wxs-content').setStyle({
|
||||
'will-change': 'auto',
|
||||
'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
|
||||
'transition': 'transform 300ms'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
|
||||
me.clearTransform = function (ins){
|
||||
ins.requestAnimationFrame(function () {
|
||||
ins.selectComponent('.mescroll-wxs-content').setStyle({
|
||||
'will-change': '',
|
||||
'transform': '',
|
||||
'transition': ''
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ------ 自定义下拉刷新动画 end ------
|
||||
|
||||
/**
|
||||
* 监听逻辑层数据的变化 (实时更新数据)
|
||||
*/
|
||||
function propObserver(wxsProp) {
|
||||
me.optDown = wxsProp.optDown
|
||||
me.scrollTop = wxsProp.scrollTop
|
||||
me.bodyHeight = wxsProp.bodyHeight
|
||||
me.isDownScrolling = wxsProp.isDownScrolling
|
||||
me.isUpScrolling = wxsProp.isUpScrolling
|
||||
me.isUpBoth = wxsProp.isUpBoth
|
||||
me.isScrollBody = wxsProp.isScrollBody
|
||||
me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听逻辑层数据的变化 (调用wxs的方法)
|
||||
*/
|
||||
function callObserver(callProp, oldValue, ins) {
|
||||
if (me.disabled()) return;
|
||||
if(callProp.callType){
|
||||
// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
|
||||
if(callProp.callType === 'showLoading'){
|
||||
me.showLoading(ins)
|
||||
}else if(callProp.callType === 'endDownScroll'){
|
||||
me.endDownScroll(ins)
|
||||
}else if(callProp.callType === 'clearTransform'){
|
||||
me.clearTransform(ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* touch事件
|
||||
*/
|
||||
function touchstartEvent(e, ins) {
|
||||
me.downHight = 0; // 下拉的距离
|
||||
me.startPoint = me.getPoint(e); // 记录起点
|
||||
me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
|
||||
me.startAngle = 0; // 初始角度
|
||||
me.lastPoint = me.startPoint; // 重置上次move的点
|
||||
me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
|
||||
me.inTouchend = false; // 标记不是touchend
|
||||
|
||||
me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
|
||||
}
|
||||
|
||||
function touchmoveEvent(e, ins) {
|
||||
var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
|
||||
|
||||
if (me.disabled()) return isPrevent;
|
||||
|
||||
var scrollTop = me.getScrollTop(); // 当前滚动条的距离
|
||||
var curPoint = me.getPoint(e); // 当前点
|
||||
|
||||
var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
|
||||
// 向下拉 && 在顶部
|
||||
// mescroll-body,直接判定在顶部即可
|
||||
// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
|
||||
// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
|
||||
if (moveY > 0 && (
|
||||
(me.isScrollBody && scrollTop <= 0)
|
||||
||
|
||||
(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
|
||||
)) {
|
||||
// 可下拉的条件
|
||||
if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
|
||||
me.isUpBoth))) {
|
||||
|
||||
// 下拉的角度是否在配置的范围内
|
||||
if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
|
||||
if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
|
||||
|
||||
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
|
||||
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
|
||||
me.inTouchend = true; // 标记执行touchend
|
||||
touchendEvent(e, ins); // 提前触发touchend
|
||||
return isPrevent;
|
||||
}
|
||||
|
||||
isPrevent = false // 小程序是return false
|
||||
|
||||
var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
|
||||
|
||||
// 下拉距离 < 指定距离
|
||||
if (me.downHight < me.optDown.offset) {
|
||||
if (me.movetype !== 1) {
|
||||
me.movetype = 1; // 加入标记,保证只执行一次
|
||||
// me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
|
||||
me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
|
||||
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
|
||||
}
|
||||
me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
|
||||
|
||||
// 指定距离 <= 下拉距离
|
||||
} else {
|
||||
if (me.movetype !== 2) {
|
||||
me.movetype = 2; // 加入标记,保证只执行一次
|
||||
// me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
|
||||
me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
|
||||
me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
|
||||
}
|
||||
if (diff > 0) { // 向下拉
|
||||
me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
|
||||
} else { // 向上收
|
||||
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
|
||||
}
|
||||
}
|
||||
|
||||
me.downHight = Math.round(me.downHight) // 取整
|
||||
var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
|
||||
// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
|
||||
me.onMoving(ins, rate, me.downHight)
|
||||
}
|
||||
}
|
||||
|
||||
me.lastPoint = curPoint; // 记录本次移动的点
|
||||
|
||||
return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
|
||||
}
|
||||
|
||||
function touchendEvent(e, ins) {
|
||||
// 如果下拉区域高度已改变,则需重置回来
|
||||
if (me.isMoveDown) {
|
||||
if (me.downHight >= me.optDown.offset) {
|
||||
// 符合触发刷新的条件
|
||||
me.downHight = me.optDown.offset; // 更新下拉区域高度
|
||||
// me.triggerDownScroll();
|
||||
me.callMethod(ins, {type: 'triggerDownScroll'})
|
||||
} else {
|
||||
// 不符合的话 则重置
|
||||
me.downHight = 0;
|
||||
// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
|
||||
me.callMethod(ins, {type: 'endDownScroll'})
|
||||
}
|
||||
me.movetype = 0;
|
||||
me.isMoveDown = false;
|
||||
} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
|
||||
var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
||||
// 上滑
|
||||
if (isScrollUp) {
|
||||
// 需检查滑动的角度
|
||||
var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
|
||||
if (angle > 80) {
|
||||
// 检查并触发上拉
|
||||
// me.triggerUpScroll(true);
|
||||
me.callMethod(ins, {type: 'triggerUpScroll'})
|
||||
}
|
||||
}
|
||||
}
|
||||
me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
|
||||
}
|
||||
|
||||
/* 是否禁用下拉刷新 */
|
||||
me.disabled = function(){
|
||||
return !me.optDown || !me.optDown.use || me.optDown.native
|
||||
}
|
||||
|
||||
/* 根据点击滑动事件获取第一个手指的坐标 */
|
||||
me.getPoint = function(e) {
|
||||
if (!e) {
|
||||
return {x: 0,y: 0}
|
||||
}
|
||||
if (e.touches && e.touches[0]) {
|
||||
return {x: e.touches[0].pageX,y: e.touches[0].pageY}
|
||||
} else if (e.changedTouches && e.changedTouches[0]) {
|
||||
return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
|
||||
} else {
|
||||
return {x: e.clientX,y: e.clientY}
|
||||
}
|
||||
}
|
||||
|
||||
/* 计算两点之间的角度: 区间 [0,90]*/
|
||||
me.getAngle = function (p1, p2) {
|
||||
var x = Math.abs(p1.x - p2.x);
|
||||
var y = Math.abs(p1.y - p2.y);
|
||||
var z = Math.sqrt(x * x + y * y);
|
||||
var angle = 0;
|
||||
if (z !== 0) {
|
||||
angle = Math.asin(y / z) / Math.PI * 180;
|
||||
}
|
||||
return angle
|
||||
}
|
||||
|
||||
/* 获取滚动条的位置 */
|
||||
me.getScrollTop = function() {
|
||||
return me.scrollTop || 0
|
||||
}
|
||||
|
||||
/* 获取body的高度 */
|
||||
me.getBodyHeight = function() {
|
||||
return me.bodyHeight || 0;
|
||||
}
|
||||
|
||||
/* 调用逻辑层的方法 */
|
||||
me.callMethod = function(ins, param) {
|
||||
if(ins) ins.callMethod('wxsCall', param)
|
||||
}
|
||||
|
||||
/* 导出模块 */
|
||||
module.exports = {
|
||||
propObserver: propObserver,
|
||||
callObserver: callObserver,
|
||||
touchstartEvent: touchstartEvent,
|
||||
touchmoveEvent: touchmoveEvent,
|
||||
touchendEvent: touchendEvent
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<!-- 公告轮播 -->
|
||||
<template>
|
||||
<view class="NoticeList" :style="'background-color: '+bgColor+';'">
|
||||
<view class="notice-image">
|
||||
<image :src="imageUrl" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="notice-content">
|
||||
<view v-if="list.length < 1">
|
||||
<text class="content-item-text" :style="'color: '+color+';'">暂无消息</text>
|
||||
</view>
|
||||
<swiper v-else class="content-item" :vertical="vertical" :autoplay="true" :interval="interval" :circular="circular">
|
||||
<swiper-item v-for="(item, index) in list" :key="index" @click="noticeItemClickHandle(item)">
|
||||
<text class="content-item-text" :style="'color: '+color+';'">{{ item[itemKey] }}</text>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NoticeList',
|
||||
props:{
|
||||
list: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 默认图片
|
||||
imageUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#564F5F'
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#F5F5F5'
|
||||
},
|
||||
// 滚动方向是否为纵向
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 自动切换时间间隔
|
||||
interval: {
|
||||
type: Number,
|
||||
default: 5 * 1000
|
||||
},
|
||||
// 是否衔接滑动,即到最后一张时接着滑动,是否自动切换到第一张
|
||||
circular: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 公告显示的取值字段
|
||||
itemKey: {
|
||||
type: String,
|
||||
default: 'title'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
noticeItemClickHandle (item) {
|
||||
this.$emit('click', item)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.NoticeList{
|
||||
width: 100%;
|
||||
height: 38rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.notice-image {
|
||||
line-height: 38rpx;
|
||||
padding-right: 20rpx;
|
||||
// vertical-align: middle;
|
||||
// text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
image {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
.notice-content {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
font-size: 24rpx;
|
||||
|
||||
.content-item {
|
||||
width: 100%;
|
||||
height: 38rpx;
|
||||
text-align: left;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
.content-item-text{
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 40rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<picker mode="multiSelector"
|
||||
:value="multiIndex"
|
||||
:range="multiArray"
|
||||
@change="handleValueChange"
|
||||
@columnchange="handleColumnChange">
|
||||
<slot></slot>
|
||||
</picker>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const CHINA_REGIONS = require('./regions.json')
|
||||
export default {
|
||||
props:{
|
||||
defaultRegions:{
|
||||
type:Array,
|
||||
default(){
|
||||
return []
|
||||
}
|
||||
},
|
||||
defaultRegionCode:{
|
||||
type:String
|
||||
},
|
||||
defaultRegion:[String,Array]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cityArr:CHINA_REGIONS[0].childs,
|
||||
districtArr:CHINA_REGIONS[0].childs[0].childs,
|
||||
multiIndex: [0, 0, 0],
|
||||
isInitMultiArray:true,
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
defaultRegion:{
|
||||
handler(region,oldRegion){
|
||||
if(Array.isArray(region)){
|
||||
// 避免传的是字面量的时候重复触发
|
||||
oldRegion = oldRegion || []
|
||||
if(region.join('')!==oldRegion.join('')){
|
||||
this.handleDefaultRegion(region)
|
||||
}
|
||||
}else if(region&®ion.length == 6){
|
||||
this.handleDefaultRegion(region)
|
||||
}else{
|
||||
console.warn('defaultRegion非有效格式')
|
||||
}
|
||||
},
|
||||
immediate:true,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
multiArray(){
|
||||
console.log(this.pickedArr)
|
||||
return this.pickedArr.map(arr=>arr.map(item=>item.name))
|
||||
},
|
||||
pickedArr(){
|
||||
// 进行初始化
|
||||
if(this.isInitMultiArray){
|
||||
return [
|
||||
CHINA_REGIONS,
|
||||
CHINA_REGIONS[0].childs,
|
||||
CHINA_REGIONS[0].childs[0].childs
|
||||
]
|
||||
}
|
||||
return [CHINA_REGIONS,this.cityArr,this.districtArr];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleColumnChange(e){
|
||||
// console.log(e);
|
||||
this.isInitMultiArray = false;
|
||||
const that = this;
|
||||
let col = e.detail.column;
|
||||
let row = e.detail.value;
|
||||
that.multiIndex[col] = row;
|
||||
try{
|
||||
switch(col){
|
||||
case 0:
|
||||
if(CHINA_REGIONS[that.multiIndex[0]].childs.length==0){
|
||||
that.cityArr = that.districtArr = [CHINA_REGIONS[that.multiIndex[0]]]
|
||||
break;
|
||||
}
|
||||
that.cityArr = CHINA_REGIONS[that.multiIndex[0]].childs
|
||||
that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[that.multiIndex[1]].childs
|
||||
break;
|
||||
case 1:
|
||||
that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[that.multiIndex[1]].childs
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
}
|
||||
}catch(e){
|
||||
// console.log(e);
|
||||
that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[0].childs
|
||||
}
|
||||
|
||||
},
|
||||
handleValueChange(e){
|
||||
// 结构赋值
|
||||
let [index0,index1,index2] = e.detail.value;
|
||||
let [arr0,arr1,arr2] = this.pickedArr;
|
||||
let address = [arr0[index0],arr1[index1],arr2[index2]];
|
||||
// console.log(address);
|
||||
this.$emit('getRegion',address)
|
||||
},
|
||||
handleDefaultRegion(region){
|
||||
const isCode = !Array.isArray(region)
|
||||
this.isInitMultiArray = false;
|
||||
let children = CHINA_REGIONS
|
||||
for(let i=0;i<3;i++){
|
||||
for(let j=0;j<children.length;j++){
|
||||
let condition = isCode?children[j].code==region.slice(0,(i+1)*2):children[j].name.includes(region[i]);
|
||||
if(condition){
|
||||
// 匹配成功进行赋值
|
||||
// console.log(i,j,children.length-1);
|
||||
children = children[j].childs;
|
||||
if(i==0){
|
||||
this.cityArr = children
|
||||
}else if(i==1){
|
||||
this.districtArr = children
|
||||
}
|
||||
this.$set(this.multiIndex,i,j)
|
||||
// console.log(this.multiIndex);
|
||||
break;
|
||||
}else{
|
||||
// 首次匹配失败就用默认的初始化
|
||||
// console.log(i,j,children.length-1);
|
||||
if(i==0 && j==(children.length-1)){
|
||||
this.isInitMultiArray = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,474 @@
|
|||
<template>
|
||||
<view v-if="visibleSync" :style="[customStyle, {
|
||||
zIndex: uZindex - 1
|
||||
}]" :class="{ 'u-drawer-visible': showDrawer }" class="u-drawer" hover-stop-propagation>
|
||||
<mask-view :custom-style="maskCustomStyle" :maskClickAble="maskCloseAble" :z-index="uZindex - 2" :show="showDrawer && mask" @click="maskClick"></mask-view>
|
||||
<view
|
||||
class="u-drawer-content"
|
||||
@tap="modeCenterClose(mode)"
|
||||
:class="[
|
||||
safeAreaInsetBottom ? 'safe-area-inset-bottom' : '',
|
||||
'u-drawer-' + mode,
|
||||
showDrawer ? 'u-drawer-content-visible' : '',
|
||||
zoom && mode == 'center' ? 'u-animation-zoom' : ''
|
||||
]"
|
||||
@touchmove.stop.prevent
|
||||
@tap.stop.prevent
|
||||
:style="[style]"
|
||||
>
|
||||
<view class="u-mode-center-box" @tap.stop.prevent @touchmove.stop.prevent v-if="mode == 'center'" :style="[centerStyle]">
|
||||
<icon-view
|
||||
@click="close"
|
||||
v-if="closeable"
|
||||
class="u-close"
|
||||
:class="['u-close--' + closeIconPos]"
|
||||
:name="closeIcon"
|
||||
:color="closeIconColor"
|
||||
:size="closeIconSize"
|
||||
></icon-view>
|
||||
<scroll-view class="u-drawer__scroll-view" scroll-y="true" v-if="!isView">
|
||||
<slot />
|
||||
</scroll-view>
|
||||
<view class="u-drawer__scroll-view" scroll-y="true" v-if="isView">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="u-drawer__scroll-view" scroll-y="true" v-else>
|
||||
<slot />
|
||||
</scroll-view>
|
||||
<view @tap="close" class="u-close" :class="['u-close--' + closeIconPos]">
|
||||
<icon-view
|
||||
v-if="mode != 'center' && closeable"
|
||||
:name="closeIcon"
|
||||
:color="closeIconColor"
|
||||
:size="closeIconSize"
|
||||
></icon-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* popup 弹窗
|
||||
* @description 弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义
|
||||
* @tutorial https://www.uviewui.com/components/popup.html
|
||||
* @property {String} mode 弹出方向(默认left)
|
||||
* @property {Boolean} mask 是否显示遮罩(默认true)
|
||||
* @property {Stringr | Number} length mode=left | 见官网说明(默认auto)
|
||||
* @property {Boolean} zoom 是否开启缩放动画,只在mode为center时有效(默认true)
|
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
|
||||
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭弹出层(默认true)
|
||||
* @property {Object} custom-style 用户自定义样式
|
||||
* @property {Stringr | Number} negative-top 中部弹出时,往上偏移的值
|
||||
* @property {Numberr | String} border-radius 弹窗圆角值(默认0)
|
||||
* @property {Numberr | String} z-index 弹出内容的z-index值(默认1075)
|
||||
* @property {Boolean} closeable 是否显示关闭图标(默认false)
|
||||
* @property {String} close-icon 关闭图标的名称,只能uView的内置图标
|
||||
* @property {String} close-icon-pos 自定义关闭图标位置(默认top-right)
|
||||
* @property {String} close-icon-color 关闭图标的颜色(默认#909399)
|
||||
* @property {Number | String} close-icon-size 关闭图标的大小,单位rpx(默认30)
|
||||
* @property Boolean} isView 是否可以超出内容区
|
||||
* @event {Function} open 弹出层打开
|
||||
* @event {Function} close 弹出层收起
|
||||
* @example <popup-view v-model="show"><view>出淤泥而不染,濯清涟而不妖</view></popup-view>
|
||||
*/
|
||||
export default {
|
||||
name: 'popupView',
|
||||
props: {
|
||||
/**
|
||||
* 显示状态
|
||||
*/
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 弹出方向,left|right|top|bottom|center
|
||||
*/
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
/**
|
||||
* 是否显示遮罩
|
||||
*/
|
||||
mask: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 抽屉的宽度(mode=left|right),或者高度(mode=top|bottom),单位rpx,或者"auto"
|
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度
|
||||
length: {
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
},
|
||||
// 是否开启缩放动画,只在mode=center时有效
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否可以通过点击遮罩进行关闭
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否可以超过内容区
|
||||
isView: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 用户自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 此为内部参数,不在文档对外使用,为了解决Picker和keyboard等融合了弹窗的组件
|
||||
// 对v-model双向绑定多层调用造成报错不能修改props值的问题
|
||||
popup: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 显示显示弹窗的圆角,单位rpx
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 是否显示关闭图标
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 关闭图标的名称,只能uView的内置图标
|
||||
closeIcon: {
|
||||
type: String,
|
||||
default: 'close'
|
||||
},
|
||||
// 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角
|
||||
closeIconPos: {
|
||||
type: String,
|
||||
default: 'top-right'
|
||||
},
|
||||
// 关闭图标的颜色
|
||||
closeIconColor: {
|
||||
type: String,
|
||||
default: '#909399'
|
||||
},
|
||||
// 关闭图标的大小,单位rpx
|
||||
closeIconSize: {
|
||||
type: [String, Number],
|
||||
default: '30'
|
||||
},
|
||||
// 宽度,只对左,右,中部弹出时起作用,单位rpx,或者"auto"
|
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数
|
||||
width: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 高度,只对上,下,中部弹出时起作用,单位rpx,或者"auto"
|
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数
|
||||
height: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 给一个负的margin-top,往上偏移,避免和键盘重合的情况,仅在mode=center时有效
|
||||
negativeTop: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 遮罩的样式,一般用于修改遮罩的透明度
|
||||
maskCustomStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 中间内容块的背景设置
|
||||
centerBoxBgColor: {
|
||||
type: [String, Boolean],
|
||||
default: '#ffffff'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: [String, Boolean],
|
||||
default: '#ffffff'
|
||||
},
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visibleSync: false,
|
||||
showDrawer: false,
|
||||
timer: null,
|
||||
closeFromInner: false, // value的值改变,是发生在内部还是外部
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 根据mode的位置,设定其弹窗的宽度(mode = left|right),或者高度(mode = top|bottom)
|
||||
style() {
|
||||
let style = {};
|
||||
// 如果是左边或者上边弹出时,需要给translate设置为负值,用于隐藏
|
||||
if (this.mode == 'left' || this.mode == 'right') {
|
||||
style = {
|
||||
width: this.width ? this.getUnitValue(this.width) : this.getUnitValue(this.length),
|
||||
height: '100%',
|
||||
transform: `translate3D(${this.mode == 'left' ? '-100%' : '100%'},0px,0px)`
|
||||
};
|
||||
} else if (this.mode == 'top' || this.mode == 'bottom') {
|
||||
style = {
|
||||
width: '100%',
|
||||
height: this.height ? this.getUnitValue(this.height) : this.getUnitValue(this.length),
|
||||
transform: `translate3D(0px,${this.mode == 'top' ? '-100%' : '100%'},0px)`
|
||||
};
|
||||
}
|
||||
style.zIndex = this.uZindex;
|
||||
// 如果用户设置了borderRadius值,添加弹窗的圆角
|
||||
if (this.borderRadius) {
|
||||
switch (this.mode) {
|
||||
case 'left':
|
||||
style.borderRadius = `0 ${this.borderRadius}rpx ${this.borderRadius}rpx 0`;
|
||||
break;
|
||||
case 'top':
|
||||
style.borderRadius = `0 0 ${this.borderRadius}rpx ${this.borderRadius}rpx`;
|
||||
break;
|
||||
case 'right':
|
||||
style.borderRadius = `${this.borderRadius}rpx 0 0 ${this.borderRadius}rpx`;
|
||||
break;
|
||||
case 'bottom':
|
||||
style.borderRadius = `${this.borderRadius}rpx ${this.borderRadius}rpx 0 0`;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
// 不加可能圆角无效
|
||||
style.overflow = 'hidden';
|
||||
}
|
||||
// style['background-color'] = this.backgroundColor
|
||||
return style;
|
||||
},
|
||||
// 中部弹窗的特有样式
|
||||
centerStyle() {
|
||||
let style = {};
|
||||
style.width = this.width ? this.getUnitValue(this.width) : this.getUnitValue(this.length);
|
||||
// 中部弹出的模式,如果没有设置高度,就用auto值,由内容撑开高度
|
||||
style.height = this.height ? this.getUnitValue(this.height) : 'auto';
|
||||
style.zIndex = this.uZindex;
|
||||
style.marginTop = `-${this.addUnit(this.negativeTop)}`;
|
||||
if (this.borderRadius) {
|
||||
style.borderRadius = `${this.borderRadius}rpx`;
|
||||
// 不加可能圆角无效
|
||||
style.overflow = 'hidden';
|
||||
}else{
|
||||
style.borderRadius = `20rpx`;
|
||||
// style.overflow = 'unset !important';
|
||||
console.log("啊啊啊啊")
|
||||
}
|
||||
// 背景色
|
||||
if (this.centerBoxBgColor) {
|
||||
style['background-color'] = this.centerBoxBgColor;
|
||||
} else {
|
||||
// 背景透明
|
||||
style['background-color'] = 'transparent';
|
||||
}
|
||||
return style;
|
||||
},
|
||||
// 计算整理后的z-index值
|
||||
uZindex() {
|
||||
return this.zIndex ? this.zIndex : 10075;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (val) {
|
||||
this.open();
|
||||
} else if(!this.closeFromInner) {
|
||||
this.close();
|
||||
}
|
||||
this.closeFromInner = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 组件渲染完成时,检查value是否为true,如果是,弹出popup
|
||||
this.value && this.open();
|
||||
},
|
||||
methods: {
|
||||
// 判断传入的值,是否带有单位,如果没有,就默认用rpx单位
|
||||
getUnitValue(val) {
|
||||
if(/(%|px|rpx|auto)$/.test(val)) return val;
|
||||
else return val + 'rpx'
|
||||
},
|
||||
// 遮罩被点击
|
||||
maskClick() {
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
// 标记关闭是内部发生的,否则修改了value值,导致watch中对value检测,导致再执行一遍close
|
||||
// 造成@close事件触发两次
|
||||
this.closeFromInner = true;
|
||||
this.change('showDrawer', 'visibleSync', false);
|
||||
},
|
||||
// 中部弹出时,需要.u-drawer-content将居中内容,此元素会铺满屏幕,点击需要关闭弹窗
|
||||
// 让其只在mode=center时起作用
|
||||
modeCenterClose(mode) {
|
||||
if (mode != 'center' || !this.maskCloseAble) return;
|
||||
this.close();
|
||||
},
|
||||
open() {
|
||||
this.change('visibleSync', 'showDrawer', true);
|
||||
},
|
||||
// 此处的原理是,关闭时先通过动画隐藏弹窗和遮罩,再移除整个组件
|
||||
// 打开时,先渲染组件,延时一定时间再让遮罩和弹窗的动画起作用
|
||||
change(param1, param2, status) {
|
||||
// 如果this.popup为false,意味着为picker,actionsheet等组件调用了popup组件
|
||||
if (this.popup == true) {
|
||||
this.$emit('input', status);
|
||||
}
|
||||
this[param1] = status;
|
||||
if(status) {
|
||||
// #ifdef H5 || MP
|
||||
this.timer = setTimeout(() => {
|
||||
this[param2] = status;
|
||||
this.$emit(status ? 'open' : 'close');
|
||||
}, 50);
|
||||
// #endif
|
||||
// #ifndef H5 || MP
|
||||
this.$nextTick(() => {
|
||||
this[param2] = status;
|
||||
this.$emit(status ? 'open' : 'close');
|
||||
})
|
||||
// #endif
|
||||
} else {
|
||||
this.timer = setTimeout(() => {
|
||||
this[param2] = status;
|
||||
this.$emit(status ? 'open' : 'close');
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
// 处理样式单位
|
||||
addUnit (value = 'auto', unit = 'rpx') {
|
||||
value = String(value);
|
||||
// 用uView内置验证规则中的number判断是否为数值
|
||||
return this.testNumber(value) ? `${value}${unit}` : value;
|
||||
},
|
||||
testNumber (value) {
|
||||
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.u-drawer {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.u-drawer-content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: absolute;
|
||||
z-index: 1003;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
.u-drawer__scroll-view {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.u-drawer-left {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.u-drawer-right {
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.u-drawer-top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.u-drawer-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.u-drawer-center {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
z-index: 99999;
|
||||
}
|
||||
.u-mode-center-box {
|
||||
min-width: 100rpx;
|
||||
min-height: 100rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.u-drawer-content-visible.u-drawer-center {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
.u-animation-zoom {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
.u-drawer-content-visible {
|
||||
transform: translate3D(0px, 0px, 0px) !important;
|
||||
}
|
||||
.u-close {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
.u-close--top-left {
|
||||
top: 30rpx;
|
||||
left: 30rpx;
|
||||
}
|
||||
.u-close--top-right {
|
||||
top: 30rpx;
|
||||
right: 30rpx;
|
||||
}
|
||||
.u-close--bottom-left {
|
||||
bottom: 30rpx;
|
||||
left: 30rpx;
|
||||
}
|
||||
.u-close--bottom-right {
|
||||
right: 30rpx;
|
||||
bottom: 30rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
<!-- tabbar组件 -->
|
||||
<template>
|
||||
<view class="TabControl" :style="[tabControlStyle]">
|
||||
<scroll-view scroll-x="true" :style="'background-color:'+bgc+';top:'+top+'px;'" :class="fixed?'fxied':''" :scroll-left='scrollLeft' scroll-with-animation id="tabcard">
|
||||
<image src="@/static/signUp/ico_return@3x.png" class="goBackImg" mode="" @click="goBack" v-if="goBackImg"></image>
|
||||
<view class="tabList" :style="[tabListStyle]">
|
||||
<view
|
||||
:class="'tabItem'+(currentIndex==index?' thisOpenSelect':'')"
|
||||
:style="[tabItemStyle]"
|
||||
v-for="(item,index) in values"
|
||||
:id="'item'+index"
|
||||
:key='index'
|
||||
@click="_onClick(item, index)">
|
||||
<view class="tabItemName">
|
||||
<text :style="(currentIndex==index?'font-size:'+activeSize+'rpx;color:'+activeColor:'font-size:'+itemSize+'rpx')">{{ item.name }}</text>
|
||||
<!-- 排序箭头显示 -->
|
||||
<view
|
||||
v-show='item.sort && item.sortType !== 1'
|
||||
class="arrow"
|
||||
:style="'transform: rotate(' + (([4, 2].indexOf(item.sortType) !== -1) ? '180' : '0') + 'deg);'" />
|
||||
</view>
|
||||
<!-- 激活下划线(如果需要显示,则sort不要设置) -->
|
||||
<view v-if='activeLineShow' class="activeLine" :style="{'background-color': activeLineColor}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name:'TabControl',
|
||||
props:{
|
||||
//是否显示返回箭头
|
||||
goBackImg: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 当前激活项
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// tab 集合
|
||||
values: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// 整体背景色
|
||||
bgc:{
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
// 是否开启固定位置
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否开启tabBar滚动
|
||||
scrollFlag: {
|
||||
type:Boolean,
|
||||
default:false
|
||||
},
|
||||
// 激活下划线的宽度
|
||||
lineWidth: {
|
||||
type: Number,
|
||||
default: 48
|
||||
},
|
||||
// tab 文本正常时的字体大小
|
||||
itemSize: {
|
||||
type: Number,
|
||||
default: 28
|
||||
},
|
||||
// tab 文本激活时的字体大小
|
||||
activeSize: {
|
||||
type: Number,
|
||||
default: 28
|
||||
},
|
||||
// tab 文本正常时的字体颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#FE4066'
|
||||
},
|
||||
// 开启固定位置之后的,top设置
|
||||
top: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 每个tab项是否等宽显示
|
||||
isEqually: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 父级与tab项的高度
|
||||
tabListHeight: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
// 是否显示激活下划线
|
||||
activeLineShow: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//下划线颜色
|
||||
activeLineColor: {
|
||||
type: String,
|
||||
default: '#FE4066'
|
||||
},
|
||||
tabWrapNeedHeight: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
windowWidth:0, //设备宽度
|
||||
leftList:[], //选项距离左边的距离
|
||||
widthList:[], //选项宽度
|
||||
scrollLeft:0, //移动距离
|
||||
newScroll:0, //上一次移动距离(用来判断是左滑还是右滑)
|
||||
wornScroll:0, //上一次移动距离(用来判断是左滑还是右滑)
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
current(val) {
|
||||
if (val !== this.currentIndex) {
|
||||
this.currentIndex = val
|
||||
if(this.scrollFlag){
|
||||
this.tabListScroll(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabItemStyle: function () {
|
||||
const _style = {}
|
||||
if (this.isEqually) {
|
||||
_style['width'] = this.windowWidth/this.values.length + 'px'
|
||||
_style['margin-right'] = 0
|
||||
}
|
||||
if (!this.activeLineShow) {
|
||||
_style['height'] = '100%'
|
||||
}
|
||||
return _style
|
||||
},
|
||||
tabListStyle: function () {
|
||||
const _style = {
|
||||
height: this.tabListHeight + 'px'
|
||||
}
|
||||
if (this.isEqually) {
|
||||
_style['display'] = 'flex'
|
||||
_style['justify-content'] = 'space-between'
|
||||
_style['padding-left'] = 0
|
||||
}
|
||||
return _style
|
||||
},
|
||||
tabControlStyle: function () {
|
||||
const _style = {}
|
||||
if (this.tabWrapNeedHeight) {
|
||||
_style['height'] = this.tabListHeight + 'px'
|
||||
}
|
||||
return _style
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.currentIndex = this.current
|
||||
if(this.scrollFlag){
|
||||
setTimeout(()=>{
|
||||
this.tabListScroll(this.current)
|
||||
},300)
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
setTimeout(()=>{
|
||||
uni.createSelectorQuery().in(this).select("#tabcard").boundingClientRect((res)=>{
|
||||
this.$emit('getTabCardHeight', {height:res.height})
|
||||
}).exec()
|
||||
uni.getSystemInfo({
|
||||
success: (res)=> {
|
||||
this.windowWidth = res.windowWidth;
|
||||
// console.log(this.windowWidth);
|
||||
this.values.forEach((i,v)=>{
|
||||
let info = uni.createSelectorQuery().in(this);
|
||||
info.select("#item"+v).boundingClientRect((res)=>{
|
||||
// 获取第一个元素到左边的距离
|
||||
// if(v==0){
|
||||
// this.startLenght = res.left
|
||||
// }
|
||||
this.widthList.push(res.width)
|
||||
this.leftList.push(res.left)
|
||||
|
||||
}).exec()
|
||||
|
||||
})
|
||||
// console.log(this.leftList)
|
||||
// console.log(this.widthList)
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
goBack(){
|
||||
uni.navigateBack({
|
||||
|
||||
})
|
||||
},
|
||||
_onClick(item, index) {
|
||||
this.$emit('clickItem', { currentIndex: index, ...item })
|
||||
if (this.currentIndex !== index) {
|
||||
this.currentIndex = index
|
||||
// 开启滚动
|
||||
if(this.scrollFlag){
|
||||
this.tabListScroll(index)
|
||||
}
|
||||
}
|
||||
},
|
||||
// 选项移动
|
||||
tabListScroll(index){
|
||||
let scoll = 0;
|
||||
this.wornScroll = index;
|
||||
// this.wornScroll-this.newScroll>0 在向左滑 ←←←←←
|
||||
if(this.wornScroll-this.newScroll>0){
|
||||
for(let i = 0;i<this.leftList.length;i++){
|
||||
if(i>1&&i==this.currentIndex){
|
||||
scoll = this.leftList[i-1]
|
||||
}
|
||||
}
|
||||
// console.log('在向左滑',scoll)
|
||||
}else{
|
||||
if(index>1){
|
||||
for(let i = 0;i<this.leftList.length;i++){
|
||||
if(i<index-1){
|
||||
scoll = this.leftList[i]
|
||||
}
|
||||
}
|
||||
}else{
|
||||
scoll = 0
|
||||
}
|
||||
// console.log('在向右滑')
|
||||
}
|
||||
this.newScroll = this.wornScroll;
|
||||
this.scrollLeft = scoll;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goBackImg{
|
||||
width: 18upx;
|
||||
height: 32upx;
|
||||
position: absolute;
|
||||
left: 30upx;
|
||||
top: 30upx;
|
||||
z-index: 999;
|
||||
}
|
||||
.TabControl {
|
||||
.fxied {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
}
|
||||
.tabList {
|
||||
height: 80rpx;
|
||||
padding-left: 24rpx;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
.tabItem {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 60rpx;
|
||||
.tabItemName {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text {
|
||||
line-height: 44rpx;
|
||||
color: #564F5F;
|
||||
transition: all 0.3s ease 0s;
|
||||
}
|
||||
.arrow {
|
||||
width: 10rpx;
|
||||
height: 6rpx;
|
||||
margin-left: 6rpx;
|
||||
background: url('@/static/components/triangular_arrow.png');
|
||||
background-size: 100% 100%;
|
||||
// transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
.activeLine {
|
||||
width: 48rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 4rpx;
|
||||
// background-color: #FE4066;
|
||||
margin: 8rpx auto 0;
|
||||
opacity: 0;
|
||||
transition: all 0.5s ease 0s;
|
||||
}
|
||||
}
|
||||
.tabItem:first-child {
|
||||
// margin-left: 22rpx;
|
||||
}
|
||||
.tabItem:last-child {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.thisOpenSelect {
|
||||
text {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
.activeLine {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<!--增加audio标签支持-->
|
||||
<audio
|
||||
:id="node.attr.id"
|
||||
:class="node.classStr"
|
||||
:style="node.styleStr"
|
||||
:src="node.attr.src"
|
||||
:loop="node.attr.loop"
|
||||
:poster="node.attr.poster"
|
||||
:name="node.attr.name"
|
||||
:author="node.attr.author"
|
||||
controls></audio>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wxParseAudio',
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<image
|
||||
:mode="node.attr.mode"
|
||||
:lazy-load="node.attr.lazyLoad"
|
||||
:class="node.classStr"
|
||||
:style="newStyleStr || node.styleStr"
|
||||
:data-src="node.attr.src"
|
||||
:src="node.attr.src"
|
||||
@tap="wxParseImgTap"
|
||||
@load="wxParseImgLoad"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wxParseImg',
|
||||
data() {
|
||||
return {
|
||||
newStyleStr: '',
|
||||
preview: true,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
wxParseImgTap(e) {
|
||||
if (!this.preview) return;
|
||||
const { src } = e.currentTarget.dataset;
|
||||
if (!src) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {// TODO 遍历获取父节点执行方法
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.preview(src, e);
|
||||
},
|
||||
// 图片视觉宽高计算函数区
|
||||
wxParseImgLoad(e) {
|
||||
const { src } = e.currentTarget.dataset;
|
||||
if (!src) return;
|
||||
const { width, height } = e.mp.detail;
|
||||
const recal = this.wxAutoImageCal(width, height);
|
||||
const { imageheight, imageWidth } = recal;
|
||||
const { padding, mode } = this.node.attr;
|
||||
const { styleStr } = this.node;
|
||||
const imageHeightStyle = mode === 'widthFix' ? '' : `height: ${imageheight}px;`;
|
||||
this.newStyleStr = `${styleStr}; ${imageHeightStyle}; width: ${imageWidth}px; padding: 0 ${+padding}px;`;
|
||||
},
|
||||
// 计算视觉优先的图片宽高
|
||||
wxAutoImageCal(originalWidth, originalHeight) {
|
||||
// 获取图片的原始长宽
|
||||
const { padding } = this.node.attr;
|
||||
const windowWidth = this.node.$screen.width - (2 * padding);
|
||||
const results = {};
|
||||
|
||||
if (originalWidth < 60 || originalHeight < 60) {
|
||||
const { src } = this.node.attr;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.removeImageUrl(src);
|
||||
this.preview = false;
|
||||
}
|
||||
|
||||
// 判断按照那种方式进行缩放
|
||||
if (originalWidth > windowWidth) {
|
||||
// 在图片width大于手机屏幕width时候
|
||||
results.imageWidth = windowWidth;
|
||||
results.imageheight = windowWidth * (originalHeight / originalWidth);
|
||||
} else {
|
||||
// 否则展示原来的数据
|
||||
results.imageWidth = originalWidth;
|
||||
results.imageheight = originalHeight;
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--table类型-->
|
||||
<block v-else-if="node.tag == 'table'">
|
||||
<view :class="node.classStr" class="table" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate1';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate0',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;// TODO currentTarget才有dataset
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {// TODO 遍历获取父节点执行方法
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<view :class="(node.tag == 'li' ? node.classStr : (node.node==='text'?'text':''))">
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<!-- <view :class="node.classStr" :style="node.styleStr"> -->
|
||||
<view :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate2';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate1',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate11';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate10',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<!--button类型-->
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
{{node.text}}
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
{{node.text}}
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
{{node.text}}
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate11',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate3';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate2',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate4';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate3',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate5';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate4',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate6';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate5',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate7';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate6',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate8';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate7',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate9';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate8',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view>
|
||||
<!--判断是否是标签节点-->
|
||||
<block v-if="node.node == 'element'">
|
||||
<block v-if="node.tag == 'button'">
|
||||
<button type="default" size="mini">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</button>
|
||||
</block>
|
||||
|
||||
<!--li类型-->
|
||||
<block v-else-if="node.tag == 'li'">
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--video类型-->
|
||||
<block v-else-if="node.tag == 'video'">
|
||||
<wx-parse-video :node="node" />
|
||||
</block>
|
||||
|
||||
<!--audio类型-->
|
||||
<block v-else-if="node.tag == 'audio'">
|
||||
<wx-parse-audio :node="node" />
|
||||
</block>
|
||||
|
||||
<!--img类型-->
|
||||
<block v-else-if="node.tag == 'img'">
|
||||
<wx-parse-img :node="node" />
|
||||
</block>
|
||||
|
||||
<!--a类型-->
|
||||
<block v-else-if="node.tag == 'a'">
|
||||
<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!--br类型-->
|
||||
<block v-else-if="node.tag == 'br'">
|
||||
<text>\n</text>
|
||||
</block>
|
||||
|
||||
<!--其他标签-->
|
||||
<block v-else>
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<block v-for="(node, index) of node.nodes" :key="index">
|
||||
<wx-parse-template :node="node" />
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</block>
|
||||
|
||||
<!--判断是否是文本节点-->
|
||||
<block v-else-if="node.node == 'text'">{{node.text}}</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wxParseTemplate from './wxParseTemplate10';
|
||||
import wxParseImg from './wxParseImg';
|
||||
import wxParseVideo from './wxParseVideo';
|
||||
import wxParseAudio from './wxParseAudio';
|
||||
|
||||
export default {
|
||||
name: 'wxParseTemplate9',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
wxParseImg,
|
||||
wxParseVideo,
|
||||
wxParseAudio,
|
||||
},
|
||||
methods: {
|
||||
wxParseATap(e) {
|
||||
const {
|
||||
href
|
||||
} = e.currentTarget.dataset;
|
||||
if (!href) return;
|
||||
let parent = this.$parent;
|
||||
while(!parent.preview || typeof parent.preview !== 'function') {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
parent.navigate(href, e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<!--增加video标签支持,并循环添加-->
|
||||
<view :class="node.classStr" :style="node.styleStr">
|
||||
<video :class="node.classStr" class="video-video" :src="node.attr.src"></video>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wxParseVideo',
|
||||
props: {
|
||||
node: {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
/**
|
||||
* html2Json 改造来自: https://github.com/Jxck/html2json
|
||||
*
|
||||
*
|
||||
* author: Di (微信小程序开发工程师)
|
||||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
||||
* 垂直微信小程序开发交流社区
|
||||
*
|
||||
* github地址: https://github.com/icindy/wxParse
|
||||
*
|
||||
* for: 微信小程序富文本解析
|
||||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
||||
*/
|
||||
|
||||
import wxDiscode from './wxDiscode';
|
||||
import HTMLParser from './htmlparser';
|
||||
|
||||
function makeMap(str) {
|
||||
const obj = {};
|
||||
const items = str.split(',');
|
||||
for (let i = 0; i < items.length; i += 1) obj[items[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Block Elements - HTML 5
|
||||
const block = makeMap('br,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video');
|
||||
|
||||
// Inline Elements - HTML 5
|
||||
const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
|
||||
|
||||
// Elements that you can, intentionally, leave open
|
||||
// (and which close themselves)
|
||||
const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
|
||||
|
||||
function removeDOCTYPE(html) {
|
||||
const isDocument = /<body.*>([^]*)<\/body>/.test(html);
|
||||
return isDocument ? RegExp.$1 : html;
|
||||
}
|
||||
|
||||
function trimHtml(html) {
|
||||
return html
|
||||
.replace(/<!--.*?-->/gi, '')
|
||||
.replace(/\/\*.*?\*\//gi, '')
|
||||
.replace(/[ ]+</gi, '<')
|
||||
.replace(/<script[^]*<\/script>/gi, '')
|
||||
.replace(/<style[^]*<\/style>/gi, '');
|
||||
}
|
||||
|
||||
function getScreenInfo() {
|
||||
const screen = {};
|
||||
wx.getSystemInfo({
|
||||
success: (res) => {
|
||||
screen.width = res.windowWidth;
|
||||
screen.height = res.windowHeight;
|
||||
},
|
||||
});
|
||||
return screen;
|
||||
}
|
||||
|
||||
function html2json(html, customHandler, imageProp, host) {
|
||||
// 处理字符串
|
||||
html = removeDOCTYPE(html);
|
||||
html = trimHtml(html);
|
||||
html = wxDiscode.strDiscode(html);
|
||||
// 生成node节点
|
||||
const bufArray = [];
|
||||
const results = {
|
||||
nodes: [],
|
||||
imageUrls: [],
|
||||
};
|
||||
|
||||
const screen = getScreenInfo();
|
||||
function Node(tag) {
|
||||
this.node = 'element';
|
||||
this.tag = tag;
|
||||
|
||||
this.$screen = screen;
|
||||
}
|
||||
|
||||
HTMLParser(html, {
|
||||
start(tag, attrs, unary) {
|
||||
// node for this element
|
||||
const node = new Node(tag);
|
||||
|
||||
if (bufArray.length !== 0) {
|
||||
const parent = bufArray[0];
|
||||
if (parent.nodes === undefined) {
|
||||
parent.nodes = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (block[tag]) {
|
||||
node.tagType = 'block';
|
||||
} else if (inline[tag]) {
|
||||
node.tagType = 'inline';
|
||||
} else if (closeSelf[tag]) {
|
||||
node.tagType = 'closeSelf';
|
||||
}
|
||||
|
||||
node.attr = attrs.reduce((pre, attr) => {
|
||||
const { name } = attr;
|
||||
let { value } = attr;
|
||||
if (name === 'class') {
|
||||
node.classStr = value;
|
||||
}
|
||||
// has multi attibutes
|
||||
// make it array of attribute
|
||||
if (name === 'style') {
|
||||
node.styleStr = value;
|
||||
}
|
||||
if (value.match(/ /)) {
|
||||
value = value.split(' ');
|
||||
}
|
||||
|
||||
// if attr already exists
|
||||
// merge it
|
||||
if (pre[name]) {
|
||||
if (Array.isArray(pre[name])) {
|
||||
// already array, push to last
|
||||
pre[name].push(value);
|
||||
} else {
|
||||
// single value, make it array
|
||||
pre[name] = [pre[name], value];
|
||||
}
|
||||
} else {
|
||||
// not exist, put it
|
||||
pre[name] = value;
|
||||
}
|
||||
|
||||
return pre;
|
||||
}, {});
|
||||
|
||||
// 优化样式相关属性
|
||||
if (node.classStr) {
|
||||
node.classStr += ` ${node.tag}`;
|
||||
} else {
|
||||
node.classStr = node.tag;
|
||||
}
|
||||
if (node.tagType === 'inline') {
|
||||
node.classStr += ' inline';
|
||||
}
|
||||
|
||||
// 对img添加额外数据
|
||||
if (node.tag === 'img') {
|
||||
let imgUrl = node.attr.src;
|
||||
imgUrl = wxDiscode.urlToHttpUrl(imgUrl, imageProp.domain);
|
||||
Object.assign(node.attr, imageProp, {
|
||||
src: imgUrl || '',
|
||||
});
|
||||
if (imgUrl) {
|
||||
results.imageUrls.push(imgUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理a标签属性
|
||||
if (node.tag === 'a') {
|
||||
node.attr.href = node.attr.href || '';
|
||||
}
|
||||
|
||||
// 处理font标签样式属性
|
||||
if (node.tag === 'font') {
|
||||
const fontSize = [
|
||||
'x-small',
|
||||
'small',
|
||||
'medium',
|
||||
'large',
|
||||
'x-large',
|
||||
'xx-large',
|
||||
'-webkit-xxx-large',
|
||||
];
|
||||
const styleAttrs = {
|
||||
color: 'color',
|
||||
face: 'font-family',
|
||||
size: 'font-size',
|
||||
};
|
||||
if (!node.styleStr) node.styleStr = '';
|
||||
Object.keys(styleAttrs).forEach((key) => {
|
||||
if (node.attr[key]) {
|
||||
const value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key];
|
||||
node.styleStr += `${styleAttrs[key]}: ${value};`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 临时记录source资源
|
||||
if (node.tag === 'source') {
|
||||
results.source = node.attr.src;
|
||||
}
|
||||
|
||||
if (customHandler.start) {
|
||||
customHandler.start(node, results);
|
||||
}
|
||||
|
||||
if (unary) {
|
||||
// if this tag doesn't have end tag
|
||||
// like <img src="hoge.png"/>
|
||||
// add to parents
|
||||
const parent = bufArray[0] || results;
|
||||
if (parent.nodes === undefined) {
|
||||
parent.nodes = [];
|
||||
}
|
||||
parent.nodes.push(node);
|
||||
} else {
|
||||
bufArray.unshift(node);
|
||||
}
|
||||
},
|
||||
end(tag) {
|
||||
// merge into parent tag
|
||||
const node = bufArray.shift();
|
||||
if (node.tag !== tag) {
|
||||
console.error('invalid state: mismatch end tag');
|
||||
}
|
||||
|
||||
// 当有缓存source资源时于于video补上src资源
|
||||
if (node.tag === 'video' && results.source) {
|
||||
node.attr.src = results.source;
|
||||
delete results.source;
|
||||
}
|
||||
|
||||
if (customHandler.end) {
|
||||
customHandler.end(node, results);
|
||||
}
|
||||
|
||||
if (bufArray.length === 0) {
|
||||
results.nodes.push(node);
|
||||
} else {
|
||||
const parent = bufArray[0];
|
||||
if (!parent.nodes) {
|
||||
parent.nodes = [];
|
||||
}
|
||||
parent.nodes.push(node);
|
||||
}
|
||||
},
|
||||
chars(text) {
|
||||
if (!text.trim()) return;
|
||||
|
||||
const node = {
|
||||
node: 'text',
|
||||
text,
|
||||
};
|
||||
|
||||
if (customHandler.chars) {
|
||||
customHandler.chars(node, results);
|
||||
}
|
||||
|
||||
if (bufArray.length === 0) {
|
||||
results.nodes.push(node);
|
||||
} else {
|
||||
const parent = bufArray[0];
|
||||
if (parent.nodes === undefined) {
|
||||
parent.nodes = [];
|
||||
}
|
||||
parent.nodes.push(node);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export default html2json;
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/**
|
||||
*
|
||||
* htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
|
||||
*
|
||||
* author: Di (微信小程序开发工程师)
|
||||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
||||
* 垂直微信小程序开发交流社区
|
||||
*
|
||||
* github地址: https://github.com/icindy/wxParse
|
||||
*
|
||||
* for: 微信小程序富文本解析
|
||||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
||||
*/
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
|
||||
const startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z0-9_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
|
||||
const endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
|
||||
const attr = /([a-zA-Z0-9_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
|
||||
|
||||
function makeMap(str) {
|
||||
const obj = {};
|
||||
const items = str.split(',');
|
||||
for (let i = 0; i < items.length; i += 1) obj[items[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Empty Elements - HTML 5
|
||||
const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr');
|
||||
|
||||
// Block Elements - HTML 5
|
||||
const block = makeMap('address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video');
|
||||
|
||||
// Inline Elements - HTML 5
|
||||
const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
|
||||
|
||||
// Elements that you can, intentionally, leave open
|
||||
// (and which close themselves)
|
||||
const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
|
||||
|
||||
// Attributes that have their values filled in disabled="disabled"
|
||||
const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected');
|
||||
|
||||
function HTMLParser(html, handler) {
|
||||
let index;
|
||||
let chars;
|
||||
let match;
|
||||
let last = html;
|
||||
const stack = [];
|
||||
|
||||
stack.last = () => stack[stack.length - 1];
|
||||
|
||||
function parseEndTag(tag, tagName) {
|
||||
// If no tag name is provided, clean shop
|
||||
let pos;
|
||||
if (!tagName) {
|
||||
pos = 0;
|
||||
} else {
|
||||
// Find the closest opened tag of the same type
|
||||
tagName = tagName.toLowerCase();
|
||||
for (pos = stack.length - 1; pos >= 0; pos -= 1) {
|
||||
if (stack[pos] === tagName) break;
|
||||
}
|
||||
}
|
||||
if (pos >= 0) {
|
||||
// Close all the open elements, up the stack
|
||||
for (let i = stack.length - 1; i >= pos; i -= 1) {
|
||||
if (handler.end) handler.end(stack[i]);
|
||||
}
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
|
||||
function parseStartTag(tag, tagName, rest, unary) {
|
||||
tagName = tagName.toLowerCase();
|
||||
|
||||
if (block[tagName]) {
|
||||
while (stack.last() && inline[stack.last()]) {
|
||||
parseEndTag('', stack.last());
|
||||
}
|
||||
}
|
||||
|
||||
if (closeSelf[tagName] && stack.last() === tagName) {
|
||||
parseEndTag('', tagName);
|
||||
}
|
||||
|
||||
unary = empty[tagName] || !!unary;
|
||||
|
||||
if (!unary) stack.push(tagName);
|
||||
|
||||
if (handler.start) {
|
||||
const attrs = [];
|
||||
|
||||
rest.replace(attr, function genAttr(matches, name) {
|
||||
const value = arguments[2] || arguments[3] || arguments[4] || (fillAttrs[name] ? name : '');
|
||||
|
||||
attrs.push({
|
||||
name,
|
||||
value,
|
||||
escaped: value.replace(/(^|[^\\])"/g, '$1\\"'), // "
|
||||
});
|
||||
});
|
||||
|
||||
if (handler.start) {
|
||||
handler.start(tagName, attrs, unary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (html) {
|
||||
chars = true;
|
||||
|
||||
if (html.indexOf('</') === 0) {
|
||||
match = html.match(endTag);
|
||||
|
||||
if (match) {
|
||||
html = html.substring(match[0].length);
|
||||
match[0].replace(endTag, parseEndTag);
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if (html.indexOf('<') === 0) {
|
||||
match = html.match(startTag);
|
||||
|
||||
if (match) {
|
||||
html = html.substring(match[0].length);
|
||||
match[0].replace(startTag, parseStartTag);
|
||||
chars = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (chars) {
|
||||
index = html.indexOf('<');
|
||||
let text = '';
|
||||
while (index === 0) {
|
||||
text += '<';
|
||||
html = html.substring(1);
|
||||
index = html.indexOf('<');
|
||||
}
|
||||
text += index < 0 ? html : html.substring(0, index);
|
||||
html = index < 0 ? '' : html.substring(index);
|
||||
|
||||
if (handler.chars) handler.chars(text);
|
||||
}
|
||||
|
||||
if (html === last) throw new Error(`Parse Error: ${html}`);
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
}
|
||||
|
||||
export default HTMLParser;
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
// HTML 支持的数学符号
|
||||
function strNumDiscode(str) {
|
||||
str = str.replace(/∀/g, '∀');
|
||||
str = str.replace(/∂/g, '∂');
|
||||
str = str.replace(/∃/g, '∃');
|
||||
str = str.replace(/∅/g, '∅');
|
||||
str = str.replace(/∇/g, '∇');
|
||||
str = str.replace(/∈/g, '∈');
|
||||
str = str.replace(/∉/g, '∉');
|
||||
str = str.replace(/∋/g, '∋');
|
||||
str = str.replace(/∏/g, '∏');
|
||||
str = str.replace(/∑/g, '∑');
|
||||
str = str.replace(/−/g, '−');
|
||||
str = str.replace(/∗/g, '∗');
|
||||
str = str.replace(/√/g, '√');
|
||||
str = str.replace(/∝/g, '∝');
|
||||
str = str.replace(/∞/g, '∞');
|
||||
str = str.replace(/∠/g, '∠');
|
||||
str = str.replace(/∧/g, '∧');
|
||||
str = str.replace(/∨/g, '∨');
|
||||
str = str.replace(/∩/g, '∩');
|
||||
str = str.replace(/∪/g, '∪');
|
||||
str = str.replace(/∫/g, '∫');
|
||||
str = str.replace(/∴/g, '∴');
|
||||
str = str.replace(/∼/g, '∼');
|
||||
str = str.replace(/≅/g, '≅');
|
||||
str = str.replace(/≈/g, '≈');
|
||||
str = str.replace(/≠/g, '≠');
|
||||
str = str.replace(/≤/g, '≤');
|
||||
str = str.replace(/≥/g, '≥');
|
||||
str = str.replace(/⊂/g, '⊂');
|
||||
str = str.replace(/⊃/g, '⊃');
|
||||
str = str.replace(/⊄/g, '⊄');
|
||||
str = str.replace(/⊆/g, '⊆');
|
||||
str = str.replace(/⊇/g, '⊇');
|
||||
str = str.replace(/⊕/g, '⊕');
|
||||
str = str.replace(/⊗/g, '⊗');
|
||||
str = str.replace(/⊥/g, '⊥');
|
||||
str = str.replace(/⋅/g, '⋅');
|
||||
return str;
|
||||
}
|
||||
|
||||
// HTML 支持的希腊字母
|
||||
function strGreeceDiscode(str) {
|
||||
str = str.replace(/Α/g, 'Α');
|
||||
str = str.replace(/Β/g, 'Β');
|
||||
str = str.replace(/Γ/g, 'Γ');
|
||||
str = str.replace(/Δ/g, 'Δ');
|
||||
str = str.replace(/Ε/g, 'Ε');
|
||||
str = str.replace(/Ζ/g, 'Ζ');
|
||||
str = str.replace(/Η/g, 'Η');
|
||||
str = str.replace(/Θ/g, 'Θ');
|
||||
str = str.replace(/Ι/g, 'Ι');
|
||||
str = str.replace(/Κ/g, 'Κ');
|
||||
str = str.replace(/Λ/g, 'Λ');
|
||||
str = str.replace(/Μ/g, 'Μ');
|
||||
str = str.replace(/Ν/g, 'Ν');
|
||||
str = str.replace(/Ξ/g, 'Ν');
|
||||
str = str.replace(/Ο/g, 'Ο');
|
||||
str = str.replace(/Π/g, 'Π');
|
||||
str = str.replace(/Ρ/g, 'Ρ');
|
||||
str = str.replace(/Σ/g, 'Σ');
|
||||
str = str.replace(/Τ/g, 'Τ');
|
||||
str = str.replace(/Υ/g, 'Υ');
|
||||
str = str.replace(/Φ/g, 'Φ');
|
||||
str = str.replace(/Χ/g, 'Χ');
|
||||
str = str.replace(/Ψ/g, 'Ψ');
|
||||
str = str.replace(/Ω/g, 'Ω');
|
||||
|
||||
str = str.replace(/α/g, 'α');
|
||||
str = str.replace(/β/g, 'β');
|
||||
str = str.replace(/γ/g, 'γ');
|
||||
str = str.replace(/δ/g, 'δ');
|
||||
str = str.replace(/ε/g, 'ε');
|
||||
str = str.replace(/ζ/g, 'ζ');
|
||||
str = str.replace(/η/g, 'η');
|
||||
str = str.replace(/θ/g, 'θ');
|
||||
str = str.replace(/ι/g, 'ι');
|
||||
str = str.replace(/κ/g, 'κ');
|
||||
str = str.replace(/λ/g, 'λ');
|
||||
str = str.replace(/μ/g, 'μ');
|
||||
str = str.replace(/ν/g, 'ν');
|
||||
str = str.replace(/ξ/g, 'ξ');
|
||||
str = str.replace(/ο/g, 'ο');
|
||||
str = str.replace(/π/g, 'π');
|
||||
str = str.replace(/ρ/g, 'ρ');
|
||||
str = str.replace(/ς/g, 'ς');
|
||||
str = str.replace(/σ/g, 'σ');
|
||||
str = str.replace(/τ/g, 'τ');
|
||||
str = str.replace(/υ/g, 'υ');
|
||||
str = str.replace(/φ/g, 'φ');
|
||||
str = str.replace(/χ/g, 'χ');
|
||||
str = str.replace(/ψ/g, 'ψ');
|
||||
str = str.replace(/ω/g, 'ω');
|
||||
str = str.replace(/ϑ/g, 'ϑ');
|
||||
str = str.replace(/ϒ/g, 'ϒ');
|
||||
str = str.replace(/ϖ/g, 'ϖ');
|
||||
str = str.replace(/·/g, '·');
|
||||
return str;
|
||||
}
|
||||
|
||||
function strcharacterDiscode(str) {
|
||||
// 加入常用解析
|
||||
str = str.replace(/ /g, ' ');
|
||||
str = str.replace(/ /g, ' ');
|
||||
str = str.replace(/ /g, ' ');
|
||||
str = str.replace(/"/g, "'");
|
||||
str = str.replace(/&/g, '&');
|
||||
str = str.replace(/</g, '<');
|
||||
str = str.replace(/>/g, '>');
|
||||
str = str.replace(/•/g, '•');
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// HTML 支持的其他实体
|
||||
function strOtherDiscode(str) {
|
||||
str = str.replace(/Œ/g, 'Œ');
|
||||
str = str.replace(/œ/g, 'œ');
|
||||
str = str.replace(/Š/g, 'Š');
|
||||
str = str.replace(/š/g, 'š');
|
||||
str = str.replace(/Ÿ/g, 'Ÿ');
|
||||
str = str.replace(/ƒ/g, 'ƒ');
|
||||
str = str.replace(/ˆ/g, 'ˆ');
|
||||
str = str.replace(/˜/g, '˜');
|
||||
str = str.replace(/ /g, '');
|
||||
str = str.replace(/ /g, '');
|
||||
str = str.replace(/ /g, '');
|
||||
str = str.replace(/‌/g, '');
|
||||
str = str.replace(/‍/g, '');
|
||||
str = str.replace(/‎/g, '');
|
||||
str = str.replace(/‏/g, '');
|
||||
str = str.replace(/–/g, '–');
|
||||
str = str.replace(/—/g, '—');
|
||||
str = str.replace(/‘/g, '‘');
|
||||
str = str.replace(/’/g, '’');
|
||||
str = str.replace(/‚/g, '‚');
|
||||
str = str.replace(/“/g, '“');
|
||||
str = str.replace(/”/g, '”');
|
||||
str = str.replace(/„/g, '„');
|
||||
str = str.replace(/†/g, '†');
|
||||
str = str.replace(/‡/g, '‡');
|
||||
str = str.replace(/•/g, '•');
|
||||
str = str.replace(/…/g, '…');
|
||||
str = str.replace(/‰/g, '‰');
|
||||
str = str.replace(/′/g, '′');
|
||||
str = str.replace(/″/g, '″');
|
||||
str = str.replace(/‹/g, '‹');
|
||||
str = str.replace(/›/g, '›');
|
||||
str = str.replace(/‾/g, '‾');
|
||||
str = str.replace(/€/g, '€');
|
||||
str = str.replace(/™/g, '™');
|
||||
|
||||
str = str.replace(/←/g, '←');
|
||||
str = str.replace(/↑/g, '↑');
|
||||
str = str.replace(/→/g, '→');
|
||||
str = str.replace(/↓/g, '↓');
|
||||
str = str.replace(/↔/g, '↔');
|
||||
str = str.replace(/↵/g, '↵');
|
||||
str = str.replace(/⌈/g, '⌈');
|
||||
str = str.replace(/⌉/g, '⌉');
|
||||
|
||||
str = str.replace(/⌊/g, '⌊');
|
||||
str = str.replace(/⌋/g, '⌋');
|
||||
str = str.replace(/◊/g, '◊');
|
||||
str = str.replace(/♠/g, '♠');
|
||||
str = str.replace(/♣/g, '♣');
|
||||
str = str.replace(/♥/g, '♥');
|
||||
|
||||
str = str.replace(/♦/g, '♦');
|
||||
str = str.replace(/'/g, "'");
|
||||
return str;
|
||||
}
|
||||
|
||||
function strDiscode(str) {
|
||||
str = strNumDiscode(str);
|
||||
str = strGreeceDiscode(str);
|
||||
str = strcharacterDiscode(str);
|
||||
str = strOtherDiscode(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
function urlToHttpUrl(url, domain) {
|
||||
if (/^\/\//.test(url)) {
|
||||
return `https:${url}`;
|
||||
} else if (/^\//.test(url)) {
|
||||
return `https://${domain}${url}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export default {
|
||||
strDiscode,
|
||||
urlToHttpUrl,
|
||||
};
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
## uParse 适用于 uni-app/mpvue 的富文本解析组件
|
||||
|
||||
> 支持 Html、Markdown 解析,Fork自: [mpvue-wxParse](https://github.com/F-loat/mpvue-wxParse)
|
||||
|
||||
|
||||
## 属性
|
||||
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| -----------------|--------------- | ------------- | ---------------- |
|
||||
| loading | Boolean | false | 数据加载状态 |
|
||||
| className | String | — | 自定义 class 名称 |
|
||||
| content | String | — | 渲染内容 |
|
||||
| noData | String | 数据不能为空 | 空数据时的渲染展示 |
|
||||
| startHandler | Function | 见源码 | 自定义 parser 函数 |
|
||||
| endHandler | Function | null | 自定义 parser 函数 |
|
||||
| charsHandler | Function | null | 自定义 parser 函数 |
|
||||
| imageProp | Object | 见下文 | 图片相关参数 |
|
||||
|
||||
### 自定义 parser 函数具体介绍
|
||||
|
||||
* 传入的参数为当前节点 `node` 对象及解析结果 `results` 对象,例如 `startHandler(node, results)`
|
||||
* 无需返回值,通过对传入的参数直接操作来完成需要的改动
|
||||
* 自定义函数会在原解析函数处理之后执行
|
||||
|
||||
### imageProp 对象具体属性
|
||||
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| -----------------|--------------- | ------------- | ------------------ |
|
||||
| mode | String | 'aspectFit' | 图片裁剪、缩放的模式 |
|
||||
| padding | Number | 0 | 图片内边距 |
|
||||
| lazyLoad | Boolean | false | 图片懒加载 |
|
||||
| domain | String | '' | 图片服务域名 |
|
||||
|
||||
## 事件
|
||||
|
||||
| 名称 | 参数 | 描述 |
|
||||
| -----------------|----------------- | ---------------- |
|
||||
| preview | 图片地址,原始事件 | 预览图片时触发 |
|
||||
| navigate | 链接地址,原始事件 | 点击链接时触发 |
|
||||
|
||||
## 基本使用方法
|
||||
|
||||
|
||||
``` vue
|
||||
<template>
|
||||
<div>
|
||||
<u-parse :content="article" @preview="preview" @navigate="navigate" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uParse from '@/components/u-parse/u-parse.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
uParse
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
article: '<div>我是HTML代码</div>'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
preview(src, e) {
|
||||
// do something
|
||||
},
|
||||
navigate(href, e) {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import url("@/components/u-parse/u-parse.css");
|
||||
</style>
|
||||
```
|
||||
|
||||
|
||||
## 渲染 Markdown
|
||||
|
||||
> 先将 markdown 转换为 html 即可
|
||||
|
||||
```
|
||||
npm install marked
|
||||
```
|
||||
|
||||
``` js
|
||||
import marked from 'marked'
|
||||
import uParse from '@/components/u-parse/u-parse.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
uParse
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
article: marked(`#hello, markdown!`)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
/**
|
||||
* author: Di (微信小程序开发工程师)
|
||||
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
|
||||
* 垂直微信小程序开发交流社区
|
||||
*
|
||||
* github地址: https://github.com/icindy/wxParse
|
||||
*
|
||||
* for: 微信小程序富文本解析
|
||||
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
|
||||
*/
|
||||
|
||||
.wxParse {
|
||||
width: 100%;
|
||||
font-family: Helvetica, sans-serif;
|
||||
font-size: 30upx;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.wxParse view {
|
||||
word-break: hyphenate;
|
||||
}
|
||||
|
||||
.wxParse .inline {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wxParse .div {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wxParse .h1 .text {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
.wxParse .h2 .text {
|
||||
font-size: 1.5em;
|
||||
margin: 0.83em 0;
|
||||
}
|
||||
.wxParse .h3 .text {
|
||||
font-size: 1.17em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.wxParse .h4 .text {
|
||||
margin: 1.33em 0;
|
||||
}
|
||||
.wxParse .h5 .text {
|
||||
font-size: 0.83em;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
.wxParse .h6 .text {
|
||||
font-size: 0.67em;
|
||||
margin: 2.33em 0;
|
||||
}
|
||||
|
||||
.wxParse .h1 .text,
|
||||
.wxParse .h2 .text,
|
||||
.wxParse .h3 .text,
|
||||
.wxParse .h4 .text,
|
||||
.wxParse .h5 .text,
|
||||
.wxParse .h6 .text,
|
||||
.wxParse .b,
|
||||
.wxParse .strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
|
||||
.wxParse .p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.wxParse .i,
|
||||
.wxParse .cite,
|
||||
.wxParse .em,
|
||||
.wxParse .var,
|
||||
.wxParse .address {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.wxParse .pre,
|
||||
.wxParse .tt,
|
||||
.wxParse .code,
|
||||
.wxParse .kbd,
|
||||
.wxParse .samp {
|
||||
font-family: monospace;
|
||||
}
|
||||
.wxParse .pre {
|
||||
overflow: auto;
|
||||
background: #f5f5f5;
|
||||
/* padding: 16upx; */
|
||||
white-space: pre;
|
||||
margin: 1em 0upx;
|
||||
}
|
||||
.wxParse .code {
|
||||
display: inline;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.wxParse .big {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
|
||||
.wxParse .small,
|
||||
.wxParse .sub,
|
||||
.wxParse .sup {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
|
||||
.wxParse .sub {
|
||||
vertical-align: sub;
|
||||
}
|
||||
.wxParse .sup {
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
.wxParse .s,
|
||||
.wxParse .strike,
|
||||
.wxParse .del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.wxParse .strong,
|
||||
.wxParse .s {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.wxParse .a {
|
||||
color: deepskyblue;
|
||||
}
|
||||
|
||||
.wxParse .video {
|
||||
text-align: center;
|
||||
margin: 22upx 0;
|
||||
}
|
||||
|
||||
.wxParse .video-video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wxParse .img {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wxParse .blockquote {
|
||||
margin: 10upx 0;
|
||||
padding: 22upx 0 22upx 22upx;
|
||||
font-family: Courier, Calibri, "宋体";
|
||||
background: #f5f5f5;
|
||||
border-left: 6upx solid #dbdbdb;
|
||||
}
|
||||
.wxParse .blockquote .p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wxParse .ul, .wxParse .ol {
|
||||
display: block;
|
||||
margin: 1em 0;
|
||||
padding-left: 33upx;
|
||||
}
|
||||
.wxParse .ol {
|
||||
list-style-type: disc;
|
||||
}
|
||||
.wxParse .ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
.wxParse .ol>weixin-parse-template,.wxParse .ul>weixin-parse-template {
|
||||
display: list-item;
|
||||
align-items: baseline;
|
||||
text-align: match-parent;
|
||||
}
|
||||
|
||||
.wxParse .ol>.li,.wxParse .ul>.li {
|
||||
display: list-item;
|
||||
align-items: baseline;
|
||||
text-align: match-parent;
|
||||
}
|
||||
.wxParse .ul .ul, .wxParse .ol .ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
.wxParse .ol .ol .ul, .wxParse .ol .ul .ul, .wxParse .ul .ol .ul, .wxParse .ul .ul .ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.wxParse .u {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.wxParse .hide {
|
||||
display: none;
|
||||
}
|
||||
.wxParse .del {
|
||||
display: inline;
|
||||
}
|
||||
.wxParse .figure {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wxParse .table {
|
||||
width: 100%;
|
||||
}
|
||||
.wxParse .thead, .wxParse .tfoot, .wxParse .tr {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.wxParse .tr {
|
||||
width:100%;
|
||||
display: flex;
|
||||
border-right: 2upx solid #e0e0e0;
|
||||
border-bottom: 2upx solid #e0e0e0;
|
||||
}
|
||||
.wxParse .th,
|
||||
.wxParse .td {
|
||||
display: flex;
|
||||
width: 1276upx;
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
padding: 11upx;
|
||||
border-left: 2upx solid #e0e0e0;
|
||||
}
|
||||
.wxParse .td:last {
|
||||
border-top: 2upx solid #e0e0e0;
|
||||
}
|
||||
.wxParse .th {
|
||||
background: #f0f0f0;
|
||||
border-top: 2upx solid #e0e0e0;
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<!--**
|
||||
* forked from:https://github.com/F-loat/mpvue-wxParse
|
||||
*
|
||||
* github地址: https://github.com/dcloudio/uParse
|
||||
*
|
||||
* for: uni-app框架下 富文本解析
|
||||
*/-->
|
||||
|
||||
<template>
|
||||
<!--基础元素-->
|
||||
<div class="wxParse" :class="className" v-if="!loading">
|
||||
<block v-for="(node,index) of nodes" :key="index">
|
||||
<wxParseTemplate :node="node" />
|
||||
</block>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HtmlToJson from './libs/html2json';
|
||||
import wxParseTemplate from './components/wxParseTemplate0';
|
||||
|
||||
export default {
|
||||
name: 'wxParse',
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
noData: {
|
||||
type: String,
|
||||
default: '<div style="color: red;">数据不能为空</div>',
|
||||
},
|
||||
startHandler: {
|
||||
type: Function,
|
||||
default() {
|
||||
return (node) => {
|
||||
node.attr.class = null;
|
||||
node.attr.style = null;
|
||||
};
|
||||
},
|
||||
},
|
||||
endHandler: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
charsHandler: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
imageProp: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
mode: 'aspectFit',
|
||||
padding: 0,
|
||||
lazyLoad: false,
|
||||
domain: '',
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
wxParseTemplate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imageUrls: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
nodes() {
|
||||
const {
|
||||
content,
|
||||
noData,
|
||||
imageProp,
|
||||
startHandler,
|
||||
endHandler,
|
||||
charsHandler,
|
||||
} = this;
|
||||
const parseData = content || noData;
|
||||
const customHandler = {
|
||||
start: startHandler,
|
||||
end: endHandler,
|
||||
chars: charsHandler,
|
||||
};
|
||||
const results = HtmlToJson(parseData, customHandler, imageProp, this);
|
||||
this.imageUrls = results.imageUrls;
|
||||
console.log(results)
|
||||
return results.nodes;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
navigate(href, $event) {
|
||||
this.$emit('navigate', href, $event);
|
||||
},
|
||||
preview(src, $event) {
|
||||
if (!this.imageUrls.length) return;
|
||||
wx.previewImage({
|
||||
current: src,
|
||||
urls: this.imageUrls,
|
||||
});
|
||||
this.$emit('preview', src, $event);
|
||||
},
|
||||
removeImageUrl(src) {
|
||||
const { imageUrls } = this;
|
||||
imageUrls.splice(imageUrls.indexOf(src), 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!-- 正在开发中提示组件 -->
|
||||
<template>
|
||||
<view class="underDevelopment" :class="{ fullScreen: fullScreen }">
|
||||
<image class="tipsImg" :src="tipsImg" mode=""></image>
|
||||
<text class="tipsTitle">{{ tipsTitle }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UnderDevelopment',
|
||||
props: {
|
||||
tipsImg: {
|
||||
type: String,
|
||||
default: '/static/components/pic_comingsoon@2x.png'
|
||||
},
|
||||
tipsTitle: {
|
||||
type: String,
|
||||
default: '正在研发中,敬请期待~'
|
||||
},
|
||||
// 是否全屏显示
|
||||
fullScreen: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.underDevelopment {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&.fullScreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.tipsImg {
|
||||
|
||||
}
|
||||
.tipsTitle {
|
||||
margin-top: 40rpx;
|
||||
font-size: 40rpx;
|
||||
color: #404C69;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<view v-if="width" :style="'width:'+width+';'+(square?'height:'+width:'')" class="uni-grid-item">
|
||||
<view :class="{ 'uni-grid-item--border': showBorder, 'uni-grid-item--border-top': showBorder && index < column, 'uni-highlight': highlight }"
|
||||
:style="{ 'border-right-color': borderColor ,'border-bottom-color': borderColor ,'border-top-color': borderColor }"
|
||||
class="uni-grid-item__box" @click="_onClick">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UniGridItem',
|
||||
inject: ['grid'],
|
||||
props: {
|
||||
index:{
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
column: 0,
|
||||
showBorder: true,
|
||||
square: true,
|
||||
highlight: true,
|
||||
left: 0,
|
||||
top: 0,
|
||||
openNum: 2,
|
||||
width: 0,
|
||||
borderColor: '#e5e5e5'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.column = this.grid.column
|
||||
this.showBorder = this.grid.showBorder
|
||||
this.square = this.grid.square
|
||||
this.highlight = this.grid.highlight
|
||||
this.top = this.hor === 0 ? this.grid.hor : this.hor
|
||||
this.left = this.ver === 0 ? this.grid.ver : this.ver
|
||||
this.borderColor = this.grid.borderColor
|
||||
this.grid.children.push(this)
|
||||
// this.grid.init()
|
||||
this.width = this.grid.width
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.grid.children.forEach((item, index) => {
|
||||
if (item === this) {
|
||||
this.grid.children.splice(index, 1)
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
_onClick() {
|
||||
this.grid.change({
|
||||
detail: {
|
||||
index: this.index
|
||||
}
|
||||
})
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.uni-grid-item {
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 100%;
|
||||
display: flex;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-grid-item__box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
}
|
||||
|
||||
.uni-grid-item--border {
|
||||
position: relative;
|
||||
border-bottom-color: $uni-border-color;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-right-color: $uni-border-color;
|
||||
border-right-style: solid;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.uni-grid-item--border-top {
|
||||
border-top-color: $uni-border-color;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-highlight:active {
|
||||
background-color: $uni-bg-color-hover;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<view class="uni-grid-wrap">
|
||||
<view :id="elId" ref="uni-grid" class="uni-grid" :class="{ 'uni-grid--border': showBorder }" :style="{ 'border-left-style':'solid','border-left-color':borderColor, 'border-left-width':showBorder?'1px':0 }">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
const dom = uni.requireNativePlugin('dom');
|
||||
// #endif
|
||||
export default {
|
||||
name: 'UniGrid',
|
||||
props: {
|
||||
// 每列显示个数
|
||||
column: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
// 是否显示边框
|
||||
showBorder: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '#e5e5e5'
|
||||
},
|
||||
// 是否正方形显示,默认为 true
|
||||
square: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
highlight: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
grid: this
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
|
||||
return {
|
||||
elId,
|
||||
width: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.children = []
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
setTimeout(() => {
|
||||
this._getSize((width) => {
|
||||
this.children.forEach((item, index) => {
|
||||
item.width = width
|
||||
})
|
||||
})
|
||||
}, 50)
|
||||
},
|
||||
change(e) {
|
||||
this.$emit('change', e)
|
||||
},
|
||||
_getSize(fn) {
|
||||
// #ifndef APP-NVUE
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(`#${this.elId}`)
|
||||
.boundingClientRect()
|
||||
.exec(ret => {
|
||||
this.width = parseInt((ret[0].width-1) / this.column) + 'px'
|
||||
fn(this.width)
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
dom.getComponentRect(this.$refs['uni-grid'], (ret) => {
|
||||
this.width = parseInt((ret.size.width-1) / this.column) + 'px'
|
||||
fn(this.width)
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.uni-grid-wrap {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
/* #ifdef H5 */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-grid {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
// flex: 1;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.uni-grid--border {
|
||||
border-left-color: $uni-border-color;
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
export default {
|
||||
'contact': '\ue100',
|
||||
'person': '\ue101',
|
||||
'personadd': '\ue102',
|
||||
'contact-filled': '\ue130',
|
||||
'person-filled': '\ue131',
|
||||
'personadd-filled': '\ue132',
|
||||
'phone': '\ue200',
|
||||
'email': '\ue201',
|
||||
'chatbubble': '\ue202',
|
||||
'chatboxes': '\ue203',
|
||||
'phone-filled': '\ue230',
|
||||
'email-filled': '\ue231',
|
||||
'chatbubble-filled': '\ue232',
|
||||
'chatboxes-filled': '\ue233',
|
||||
'weibo': '\ue260',
|
||||
'weixin': '\ue261',
|
||||
'pengyouquan': '\ue262',
|
||||
'chat': '\ue263',
|
||||
'qq': '\ue264',
|
||||
'videocam': '\ue300',
|
||||
'camera': '\ue301',
|
||||
'mic': '\ue302',
|
||||
'location': '\ue303',
|
||||
'mic-filled': '\ue332',
|
||||
'speech': '\ue332',
|
||||
'location-filled': '\ue333',
|
||||
'micoff': '\ue360',
|
||||
'image': '\ue363',
|
||||
'map': '\ue364',
|
||||
'compose': '\ue400',
|
||||
'trash': '\ue401',
|
||||
'upload': '\ue402',
|
||||
'download': '\ue403',
|
||||
'close': '\ue404',
|
||||
'redo': '\ue405',
|
||||
'undo': '\ue406',
|
||||
'refresh': '\ue407',
|
||||
'star': '\ue408',
|
||||
'plus': '\ue409',
|
||||
'minus': '\ue410',
|
||||
'circle': '\ue411',
|
||||
'checkbox': '\ue411',
|
||||
'close-filled': '\ue434',
|
||||
'clear': '\ue434',
|
||||
'refresh-filled': '\ue437',
|
||||
'star-filled': '\ue438',
|
||||
'plus-filled': '\ue439',
|
||||
'minus-filled': '\ue440',
|
||||
'circle-filled': '\ue441',
|
||||
'checkbox-filled': '\ue442',
|
||||
'closeempty': '\ue460',
|
||||
'refreshempty': '\ue461',
|
||||
'reload': '\ue462',
|
||||
'starhalf': '\ue463',
|
||||
'spinner': '\ue464',
|
||||
'spinner-cycle': '\ue465',
|
||||
'search': '\ue466',
|
||||
'plusempty': '\ue468',
|
||||
'forward': '\ue470',
|
||||
'back': '\ue471',
|
||||
'left-nav': '\ue471',
|
||||
'checkmarkempty': '\ue472',
|
||||
'home': '\ue500',
|
||||
'navigate': '\ue501',
|
||||
'gear': '\ue502',
|
||||
'paperplane': '\ue503',
|
||||
'info': '\ue504',
|
||||
'help': '\ue505',
|
||||
'locked': '\ue506',
|
||||
'more': '\ue507',
|
||||
'flag': '\ue508',
|
||||
'home-filled': '\ue530',
|
||||
'gear-filled': '\ue532',
|
||||
'info-filled': '\ue534',
|
||||
'help-filled': '\ue535',
|
||||
'more-filled': '\ue537',
|
||||
'settings': '\ue560',
|
||||
'list': '\ue562',
|
||||
'bars': '\ue563',
|
||||
'loop': '\ue565',
|
||||
'paperclip': '\ue567',
|
||||
'eye': '\ue568',
|
||||
'arrowup': '\ue580',
|
||||
'arrowdown': '\ue581',
|
||||
'arrowleft': '\ue582',
|
||||
'arrowright': '\ue583',
|
||||
'arrowthinup': '\ue584',
|
||||
'arrowthindown': '\ue585',
|
||||
'arrowthinleft': '\ue586',
|
||||
'arrowthinright': '\ue587',
|
||||
'pulldown': '\ue588',
|
||||
'closefill': '\ue589',
|
||||
'sound': '\ue590',
|
||||
'scan': '\ue612'
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,237 @@
|
|||
<template>
|
||||
<view class="uni-navbar">
|
||||
<view :class="{ 'uni-navbar--fixed': fixed, 'uni-navbar--shadow': shadow, 'uni-navbar--border': border }" :style="{ 'background-color': backgroundColor }"
|
||||
class="uni-navbar__content">
|
||||
<uni-status-bar v-if="statusBar" />
|
||||
<view :style="{ color: color,backgroundColor: backgroundColor }" class="uni-navbar__header uni-navbar__content_view">
|
||||
<!-- <slot name="leftCustom"> -->
|
||||
<view @tap="onClickLeft" class="uni-navbar__header-btns uni-navbar__header-btns-left uni-navbar__content_view">
|
||||
<view class="uni-navbar__content_view" v-if="leftIcon.length">
|
||||
<uni-icons :color="color" :type="leftIcon" size="24" />
|
||||
</view>
|
||||
<view :class="{ 'uni-navbar-btn-icon-left': !leftIcon.length }" class="uni-navbar-btn-text uni-navbar__content_view"
|
||||
v-if="leftText.length">
|
||||
<text :style="{ color: color, fontSize: '14px' }">{{ leftText }}</text>
|
||||
</view>
|
||||
<slot name="left" />
|
||||
</view>
|
||||
<!-- </slot> -->
|
||||
<view class="uni-navbar__header-container uni-navbar__content_view">
|
||||
<view class="uni-navbar__header-container-inner uni-navbar__content_view" v-if="title.length">
|
||||
<text class="uni-nav-bar-text" :style="{color: color }">{{ title }}</text>
|
||||
</view>
|
||||
<!-- 标题插槽 -->
|
||||
<slot />
|
||||
</view>
|
||||
<view v-if="rightShow" :class="title.length ? 'uni-navbar__header-btns-right' : ''" @tap="onClickRight" class="uni-navbar__header-btns uni-navbar__content_view">
|
||||
<view class="uni-navbar__content_view" v-if="rightIcon.length">
|
||||
<uni-icons :color="color" :type="rightIcon" size="24" />
|
||||
</view>
|
||||
<!-- 优先显示图标 -->
|
||||
<view class="uni-navbar-btn-text uni-navbar__content_view" v-if="rightText.length && !rightIcon.length">
|
||||
<text class="uni-nav-bar-right-text" :style="{color: rightTextColor}">{{ rightText }}</text>
|
||||
</view>
|
||||
<slot name="right" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-navbar__placeholder" v-if="fixed">
|
||||
<uni-status-bar v-if="statusBar" />
|
||||
<view class="uni-navbar__placeholder-view" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniStatusBar from "../uni-status-bar/uni-status-bar.vue";
|
||||
import uniIcons from "../uni-icons/uni-icons.vue";
|
||||
|
||||
export default {
|
||||
name: "UniNavBar",
|
||||
components: {
|
||||
uniStatusBar,
|
||||
uniIcons
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
leftText: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
rightText: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
leftIcon: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
fixed: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "#000000"
|
||||
},
|
||||
rightTextColor: {
|
||||
type: String,
|
||||
default: "#000000"
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: "#FFFFFF"
|
||||
},
|
||||
statusBar: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
shadow: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
border: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
// 右侧是否需要显示
|
||||
rightShow: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(uni.report && this.title !== '') {
|
||||
uni.report('title', this.title)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClickLeft() {
|
||||
this.$emit("clickLeft");
|
||||
},
|
||||
onClickRight() {
|
||||
this.$emit("clickRight");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$nav-height: 50px;
|
||||
.uni-nav-bar-text {
|
||||
/* #ifdef APP-PLUS */
|
||||
font-size: 34rpx;
|
||||
/* #endif */
|
||||
/* #ifndef APP-PLUS */
|
||||
font-size: $uni-font-size-lg;
|
||||
/* #endif */
|
||||
font-weight: bold;
|
||||
}
|
||||
.uni-nav-bar-right-text {
|
||||
font-size: $uni-font-size-base;
|
||||
}
|
||||
|
||||
.uni-navbar {
|
||||
width: 750rpx;
|
||||
}
|
||||
|
||||
.uni-navbar__content {
|
||||
position: relative;
|
||||
width: 750rpx;
|
||||
background-color: $uni-bg-color;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uni-navbar__content_view {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
// background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.uni-navbar__header {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
width: 750rpx;
|
||||
height: $nav-height;
|
||||
line-height: $nav-height;
|
||||
font-size: 16px;
|
||||
// background-color: #ffffff;
|
||||
}
|
||||
|
||||
.uni-navbar__header-btns {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-wrap: nowrap;
|
||||
width: 120rpx;
|
||||
padding: 0 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-navbar__header-btns-left {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
width: 150rpx;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.uni-navbar__header-btns-right {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
width: 160rpx;
|
||||
padding-right: 30rpx;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.uni-navbar__header-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.uni-navbar__header-container-inner {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: $uni-font-size-base;
|
||||
}
|
||||
|
||||
|
||||
.uni-navbar__placeholder-view {
|
||||
height: $nav-height;
|
||||
}
|
||||
|
||||
.uni-navbar--fixed {
|
||||
position: fixed;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
.uni-navbar--shadow {
|
||||
/* #ifndef APP-NVUE */
|
||||
box-shadow: 0 1px 6px #ccc;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-navbar--border {
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: $uni-border-color;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<view :style="{ height: statusBarHeight }" class="uni-status-bar">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var statusBarHeight = uni.getSystemInfoSync().statusBarHeight + 'px'
|
||||
export default {
|
||||
name: 'UniStatusBar',
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: statusBarHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.uni-status-bar {
|
||||
width: 750rpx;
|
||||
height: 20px;
|
||||
// height: var(--status-bar-height);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<!-- 虚拟按钮(可设置圆角、背景,边框等) -->
|
||||
<template>
|
||||
<view class="virtualButton" :style="[btnStyle]" :class="{ 'blockBtn': block }" @click.stop="clickHandle">
|
||||
<slot>
|
||||
<text>虚拟按钮</text>
|
||||
</slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VirtualButton',
|
||||
props: {
|
||||
// 按钮高
|
||||
btnHeight: {
|
||||
type: [Number, String],
|
||||
default: 64
|
||||
},
|
||||
// 按钮类型 [ 'default', 'primary','dange' ]
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
// 是否需要边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 边框类型:[ 'solid', 'dashed' ]
|
||||
borderType: {
|
||||
type: String,
|
||||
default: 'solid'
|
||||
},
|
||||
// 边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '#564F5F'
|
||||
},
|
||||
// 是否为块级
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 形状:[ 'circle', 'round' ]
|
||||
shape: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 幽灵属性:背景是否透明
|
||||
ghost: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 按钮类型
|
||||
btnTypeSet: [ 'default', 'primary', 'dange', 'grey', 'D7E1FD', '376AF5', 'F19149', '61596C', 'F7CE68', 'FFCF00' ],
|
||||
// 形状
|
||||
btnShapeSet: [ 'circle', 'round' ],
|
||||
// 背景颜色(与按钮进行映射)
|
||||
bgColor: {
|
||||
'default': '#fff',
|
||||
'primary': '#5FC48D',
|
||||
'dange': '#FE4066',
|
||||
'grey': '#999999',
|
||||
'D7E1FD': '#D7E1FD',
|
||||
'376AF5': '#376AF5',
|
||||
'F19149': '#F19149',
|
||||
'61596C': '#61596C',
|
||||
'F7CE68': '#F7CE68',
|
||||
'FFCF00': '#FFCF00',
|
||||
},
|
||||
// 字体颜色
|
||||
fontColor: {
|
||||
'default': '#999999',
|
||||
'primary': '#FFFFFF',
|
||||
'dange': '#FFFFFF',
|
||||
'grey': '#FFFFFF',
|
||||
'D7E1FD': '#FFFFFF',
|
||||
'376AF5': '#FFFFFF',
|
||||
'F19149': '#FFFFFF',
|
||||
'61596C': '#FFFFFF',
|
||||
'F7CE68': '#FFFFFF',
|
||||
'FFCF00': '#FFFFFF',
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 计算按钮样式
|
||||
btnStyle () {
|
||||
let _style = {
|
||||
"height": this.btnHeight + 'rpx',
|
||||
"line-height": this.btnHeight + 'rpx'
|
||||
}
|
||||
// 背景色/字体颜色
|
||||
if (this.btnTypeSet.indexOf(this.type) !== -1) {
|
||||
if (this.ghost) {
|
||||
_style['background-color'] = ''
|
||||
_style['color'] = this.bgColor[this.type]
|
||||
} else {
|
||||
_style['background-color'] = this.bgColor[this.type]
|
||||
_style['color'] = this.fontColor[this.type]
|
||||
}
|
||||
}
|
||||
// 边框
|
||||
if (this.border) {
|
||||
_style['border-width'] = '2rpx'
|
||||
_style['border-style'] = this.borderType
|
||||
if (this.ghost) {
|
||||
_style['border-color'] = this.bgColor[this.type]
|
||||
} else {
|
||||
_style['border-color'] = this.borderColor
|
||||
}
|
||||
}
|
||||
// 形状
|
||||
if (this.btnShapeSet.indexOf(this.shape) !== -1) {
|
||||
if (this.shape === 'circle' && !this.block) {
|
||||
_style['min-width'] = this.btnHeight + 'rpx'
|
||||
} else {
|
||||
_style['padding'] = '0 30rpx'
|
||||
}
|
||||
_style['border-radius'] = this.btnHeight + 'rpx'
|
||||
} else {
|
||||
_style['padding'] = '0 30rpx'
|
||||
}
|
||||
return _style
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickHandle () {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.virtualButton {
|
||||
display: inline-block;
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
|
||||
&.blockBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
text {
|
||||
display: inline-block;
|
||||
height: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<view class="express">
|
||||
<view class="shop">
|
||||
<image :src="img" mode="aspectFit" class="express-img"></image>
|
||||
</view>
|
||||
<view class="express-detail">
|
||||
<view class="status">{{wlInfo.logisticsStatus == 1 ? '已签收' : '配送中'}}</view>
|
||||
<view class="name">{{name}}</view>
|
||||
<view class="no">{{wlInfo.brand}} <text>{{wlInfo.no}}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收货地址 -->
|
||||
<view class="content bgf">
|
||||
<view>
|
||||
<view class="flex list">
|
||||
<!-- <view class="time"></view> -->
|
||||
<view class="info flex1">
|
||||
<view class="title address">[收货地址]{{address}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- :class="{one: index+1 == wlInfo.item.length && wlInfo.delivery_status == 1}" -->
|
||||
<view class="flex list" v-for="(item, index) in wlInfo.items" :key="index" :class="{one: index == 0}">
|
||||
<!-- <view class="time">
|
||||
<view class="day">{{item.time.split(' ')[0]}}</view>
|
||||
<view>{{item.time.split(' ')[1]}}</view>
|
||||
</view> -->
|
||||
<view class="info flex1">
|
||||
<view class="title" :class="{active:index == 0 && logisticsStatus == 1}">{{index == 0 && logisticsStatus == 1 ? '已签收' : '配送中'}}</view>
|
||||
<view class="time-new">{{item.time.split(' ')[0]}} {{item.time.split(' ')[1]}}</view>
|
||||
<view class="text">{{item.context}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
wlInfo: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
img:{
|
||||
type:String,
|
||||
default:''
|
||||
},
|
||||
name:{
|
||||
type:String,
|
||||
default:''
|
||||
},
|
||||
address:{
|
||||
type:String,
|
||||
default:''
|
||||
},
|
||||
logisticsStatus:{
|
||||
type:Number,
|
||||
default:0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
console.log(this.wlInfo)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
max-height: 100vh;
|
||||
/* #ifdef H5 */
|
||||
max-height: calc(100vh - 44px);
|
||||
/* #endif */
|
||||
font-size: 24rpx;
|
||||
background-color: #fff;
|
||||
padding: 0 32rpx;
|
||||
// overflow-y: auto;
|
||||
}
|
||||
|
||||
/*flex 转换成flex容器*/
|
||||
.flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/*flex1 自动填充*/
|
||||
.flex1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/*ali-c 竖直居中*/
|
||||
.ali-c {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bgf {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.express{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 32rpx;
|
||||
border-bottom: 1px solid #F2F2F2;
|
||||
.shop{
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 0 24rpx 24rpx 0;
|
||||
background-image: url('@/static/new/shop-bg.png');
|
||||
background-size: contain;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.express-img{
|
||||
width: 142rpx;
|
||||
height: 132rpx;
|
||||
}
|
||||
}
|
||||
.express-detail{
|
||||
margin-left: 24rpx;
|
||||
.status{
|
||||
font-weight: bold;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
}
|
||||
.name{
|
||||
width: 500rpx;
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-top: 24rpx;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.no{
|
||||
width: 500rpx;
|
||||
margin-top: 8rpx;
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//收货地址
|
||||
.content {
|
||||
// margin: 20rpx;
|
||||
padding: 56rpx 0rpx 56rpx 0rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.list {
|
||||
&:first-child {
|
||||
.info::before {
|
||||
bottom: -20rpx;
|
||||
margin-top: 40rpx;
|
||||
border-left: 1px dashed #e5e5e5;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 56rpx;
|
||||
|
||||
&::before {
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
left: -23rpx;
|
||||
display:flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-image: url('@/static/new/mine/sh.png');
|
||||
background-size: 46rpx 46rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.info::before {
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.one {
|
||||
.info::before {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #1a1a1a;
|
||||
|
||||
&::before {
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
left: -23rpx;
|
||||
background: url('@/static/new/mine/yun.png') no-repeat center;
|
||||
background-size: 46rpx 46rpx;
|
||||
}
|
||||
}
|
||||
.active{
|
||||
color: #1a1a1a;
|
||||
|
||||
&::before {
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
left: -23rpx;
|
||||
background: url('@/static/new/mine/pai.png') no-repeat center;
|
||||
background-size: 46rpx 46rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 16rpx;
|
||||
color: #888;
|
||||
|
||||
.day {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
width: 100rpx;
|
||||
padding-right: 30rpx;
|
||||
text-align: right;
|
||||
font-size: 16rpx;
|
||||
color: #888888;
|
||||
|
||||
.day {
|
||||
margin-bottom: 4rpx;
|
||||
font-size: 22rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
position: relative;
|
||||
padding-top: 12rpx;
|
||||
color: #999;
|
||||
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 0;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
margin-bottom: 10rpx;
|
||||
padding-left: 32rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
margin: auto 0;
|
||||
border-radius: 50%;
|
||||
background-color: #cecece;
|
||||
}
|
||||
|
||||
&.address {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
padding: 0 0 44rpx 32rpx;
|
||||
}
|
||||
}
|
||||
.time-new{
|
||||
padding-left: 30rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 环境判断:开发、生产
|
||||
const envType = process.env.NODE_ENV === 'development' ? 'develop' : 'produce';
|
||||
// console.log(envType);
|
||||
// 获取当前app的版本
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
// 应用程序版本号
|
||||
// 条件编译,只在H5渲染
|
||||
const version_number = systemInfo.appVersion ? systemInfo.appVersion : '0.0.1';
|
||||
console.log(version_number,'版本号');
|
||||
const apiUrl = {
|
||||
develop: {
|
||||
// apiBaseUrl:'http://192.168.2.17:8080', // cky
|
||||
// apiBaseUrl:'http://192.168.2.130:8905', // 130
|
||||
// apiBaseUrl: 'http://192.168.2.51:9321', // 正式
|
||||
// apiBaseUrl:'http://192.168.2.130:8905', // 130
|
||||
// apiBaseUrl: 'http://192.168.2.51:9321', // 正式
|
||||
// apiBaseUrl: 'http://192.168.2.130:8905',
|
||||
apiBaseUrl: 'https://api.app.blindbox.06zk.com', // 正式
|
||||
// apiBaseUrl:'https://test.blindbox.06zk.com/api', // 101
|
||||
// apiBaseUrl:'http://192.168.2.71:8080', // 101
|
||||
|
||||
wsUrl: 'ws://xxx', // 正式WebSocket连接地址
|
||||
weChatCode: 'https://auth.wechat.06zk.com',
|
||||
apiVersion:'0.0.1',
|
||||
appVersion:version_number
|
||||
},
|
||||
produce: {
|
||||
// apiBaseUrl:'http://192.168.2.130:8905', // 130
|
||||
// apiBaseUrl: 'https://api.app.blindbox.06zk.com', // 正式
|
||||
// apiBaseUrl:'https://test.blindbox.06zk.com/api', // 101
|
||||
// apiBaseUrl: 'http://192.168.2.51:9321', // 正式
|
||||
apiBaseUrl: 'https://api.app.blindbox.06zk.com', // 正式
|
||||
// apiBaseUrl:'https://test.blindbox.06zk.com/api', // 101
|
||||
// apiBaseUrl: 'http://192.168.2.51:9321', // 正式
|
||||
wsUrl: 'ws://xxxx', // 正式WebSocket连接地址
|
||||
weChatCode: 'https://auth.wechat.06zk.com',
|
||||
apiVersion:'0.0.1',
|
||||
appVersion:version_number
|
||||
// weChatCode: 'https://auth.wechat.miyamama.cn' //正式
|
||||
}
|
||||
};
|
||||
|
||||
const ApiVersion = function() {
|
||||
return apiUrl[envType]['apiVersion']
|
||||
};
|
||||
const AppVersion = function() {
|
||||
return apiUrl[envType]['appVersion']
|
||||
};
|
||||
const baseUrl = function(nameSpace) {
|
||||
return apiUrl[envType][nameSpace]
|
||||
};
|
||||
|
||||
// 其他服务地址配置
|
||||
const otherServerUrl = {
|
||||
// gameUrl: 'http://web.wgmwdt.com/games'
|
||||
}
|
||||
|
||||
export {
|
||||
baseUrl,
|
||||
otherServerUrl,
|
||||
ApiVersion,
|
||||
AppVersion
|
||||
};
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>微信登录</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.9.0/vconsole.min.js"></script>
|
||||
<script>
|
||||
var GWC = {
|
||||
version: '1.2.0',
|
||||
urlParams: {},
|
||||
appendParams: function (url, params) {
|
||||
if (params) {
|
||||
var baseWithSearch = url.split('#')[0];
|
||||
var hash = url.split('#')[1];
|
||||
for (var key in params) {
|
||||
var attrValue = params[key];
|
||||
if (attrValue !== undefined) {
|
||||
var newParam = key + "=" + attrValue;
|
||||
if (baseWithSearch.indexOf('?') > 0) {
|
||||
var oldParamReg = new RegExp('^' + key + '=[-%.!~*\'\(\)\\w]*', 'g');
|
||||
if (oldParamReg.test(baseWithSearch)) {
|
||||
baseWithSearch = baseWithSearch.replace(oldParamReg, newParam);
|
||||
} else {
|
||||
baseWithSearch += "&" + newParam;
|
||||
}
|
||||
} else {
|
||||
baseWithSearch += "?" + newParam;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
url = baseWithSearch + '#' + hash;
|
||||
} else {
|
||||
url = baseWithSearch;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
getUrlParams: function () {
|
||||
var pairs = location.search.substring(1).split('&');
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var pos = pairs[i].indexOf('=');
|
||||
if (pos === -1) {
|
||||
continue;
|
||||
}
|
||||
GWC.urlParams[pairs[i].substring(0, pos)] = decodeURIComponent(pairs[i].substring(pos + 1));
|
||||
}
|
||||
},
|
||||
doRedirect: function () {
|
||||
var code = GWC.urlParams['code'];
|
||||
var redirect_uri = GWC.urlParams['redirect_uri'];
|
||||
// var appId = 'wx47db6cca5930ce74';
|
||||
var appId = GWC.urlParams['appid'];
|
||||
var scope = GWC.urlParams['scope'] || 'snsapi_base';
|
||||
var state = GWC.urlParams['state'];
|
||||
var isMp = GWC.urlParams['isMp']; //isMp为true时使用开放平台作授权登录,false为网页扫码登录
|
||||
var baseUrl;
|
||||
var redirectUri;
|
||||
console.log('appid',appId)
|
||||
if (!code) {
|
||||
baseUrl = "https://open.weixin.qq.com/connect/oauth2/authorize#wechat_redirect";
|
||||
if (scope == 'snsapi_login' && !isMp) {
|
||||
baseUrl = "https://open.weixin.qq.com/connect/qrconnect";
|
||||
}
|
||||
//第一步,没有拿到code,跳转至微信授权页面获取code
|
||||
redirectUri = GWC.appendParams(baseUrl, {
|
||||
'appid': appId,
|
||||
'redirect_uri': encodeURIComponent(location.href),
|
||||
'response_type': 'code',
|
||||
'scope': scope,
|
||||
'state': encodeURIComponent(state),
|
||||
});
|
||||
} else {
|
||||
//第二步,从微信授权页面跳转回来,已经获取到了code,再次跳转到实际所需页面
|
||||
redirectUri = GWC.appendParams(GWC.urlParams['redirect_uri'], {
|
||||
'code': code,
|
||||
'state': encodeURIComponent(state)
|
||||
});
|
||||
}
|
||||
|
||||
location.href = redirectUri;
|
||||
}
|
||||
};
|
||||
|
||||
GWC.getUrlParams();
|
||||
GWC.doRedirect();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "nl-precisionNum",
|
||||
"name": "防止加减乘除精度丢失",
|
||||
"version": "1.0.2",
|
||||
"description": "运算时防止数据精度丢失",
|
||||
"keywords": [
|
||||
"numAdd",
|
||||
"numSub",
|
||||
"numMulti",
|
||||
"numDiv"
|
||||
],
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"JS SDK",
|
||||
"通用 SDK"
|
||||
]
|
||||
},
|
||||
"displayName": "防止加减乘除精度丢失"
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 判断obj是否为一个整数 整数取整后还是等于自己。利用这个特性来判断是否是整数
|
||||
*/
|
||||
function isInteger(obj) {
|
||||
// 或者使用 Number.isInteger()
|
||||
return Math.floor(obj) === obj
|
||||
}
|
||||
/*
|
||||
* 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
|
||||
* @param floatNum {number} 小数
|
||||
* @return {object}
|
||||
* {times:100, num: 314}
|
||||
*/
|
||||
function toInteger(floatNum) {
|
||||
// 初始化数字与精度 times精度倍数 num转化后的整数
|
||||
var ret = {
|
||||
times: 1,
|
||||
num: 0
|
||||
}
|
||||
var isNegative = floatNum < 0 //是否是小数
|
||||
if (isInteger(floatNum)) { // 是否是整数
|
||||
ret.num = floatNum
|
||||
return ret //是整数直接返回
|
||||
}
|
||||
var strfi = floatNum + '' // 转换为字符串
|
||||
var dotPos = strfi.indexOf('.')
|
||||
var len = strfi.substr(dotPos + 1).length // 拿到小数点之后的位数
|
||||
var times = Math.pow(10, len) // 精度倍数
|
||||
/* 为什么加0.5?
|
||||
前面讲过乘法也会出现精度问题
|
||||
假设传入0.16344556此时倍数为100000000
|
||||
Math.abs(0.16344556) * 100000000=0.16344556*10000000=1634455.5999999999
|
||||
少了0.0000000001
|
||||
加上0.5 0.16344556*10000000+0.5=1634456.0999999999 parseInt之后乘法的精度问题得以矫正
|
||||
*/
|
||||
var intNum = parseInt(Math.abs(floatNum) * times + 0.5, 10)
|
||||
ret.times = times
|
||||
if (isNegative) {
|
||||
intNum = -intNum
|
||||
}
|
||||
ret.num = intNum
|
||||
return ret
|
||||
}
|
||||
|
||||
/*
|
||||
* 核心方法,实现加减乘除运算,确保不丢失精度
|
||||
* 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
|
||||
* @param a {number} 运算数1
|
||||
* @param b {number} 运算数2
|
||||
*/
|
||||
function operation(a, b, op) {
|
||||
var o1 = toInteger(a)
|
||||
var o2 = toInteger(b)
|
||||
var n1 = o1.num // 3.25+3.153
|
||||
var n2 = o2.num
|
||||
var t1 = o1.times
|
||||
var t2 = o2.times
|
||||
var max = t1 > t2 ? t1 : t2
|
||||
var result = null
|
||||
switch (op) {
|
||||
// 加减需要根据倍数关系来处理
|
||||
case 'add':
|
||||
if (t1 === t2) { // 两个小数倍数相同
|
||||
result = n1 + n2
|
||||
} else if (t1 > t2) {
|
||||
// o1 小数位 大于 o2
|
||||
result = n1 + n2 * (t1 / t2)
|
||||
} else { // o1小数位小于 o2
|
||||
result = n1 * (t2 / t1) + n2
|
||||
}
|
||||
return result / max
|
||||
case 'subtract':
|
||||
if (t1 === t2) {
|
||||
result = n1 - n2
|
||||
} else if (t1 > t2) {
|
||||
result = n1 - n2 * (t1 / t2)
|
||||
} else {
|
||||
result = n1 * (t2 / t1) - n2
|
||||
}
|
||||
return result / max
|
||||
case 'multiply':
|
||||
// 325*3153/(100*1000) 扩大100倍 ==>缩小100倍
|
||||
result = (n1 * n2) / (t1 * t2)
|
||||
return result
|
||||
case 'divide':
|
||||
// (325/3153)*(1000/100) 缩小100倍 ==>扩大100倍
|
||||
result = (n1 / n2) * (t2 / t1)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// 加减乘除的四个接口
|
||||
export function numAdd(a, b) {
|
||||
return operation(a, b, 'add')
|
||||
}
|
||||
export function numSub(a, b) {
|
||||
return operation(a, b, 'subtract')
|
||||
}
|
||||
export function numMulti(a, b) {
|
||||
return operation(a, b, 'multiply')
|
||||
}
|
||||
export function numDiv(a, b) {
|
||||
return operation(a*100, b*100, 'divide')
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Cache-Control" content="no-cache" />
|
||||
<meta http-equiv="Cache-Control" content="no-cache" />
|
||||
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
|
||||
<META HTTP-EQUIV="Cache-Control" CONTENT="no-store, must-revalidate">
|
||||
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
|
||||
<META HTTP-EQUIV="expires" CONTENT="0">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.9.0/vconsole.min.js"></script>
|
||||
<script>
|
||||
// init vConsole
|
||||
// var vConsole = new VConsole();
|
||||
// console.log('Hello VConsole');
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
function getUrlParam(name) {
|
||||
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [,
|
||||
""
|
||||
])[1].replace(/\+/g, '%20')) || null
|
||||
}
|
||||
window.onload = function() {
|
||||
var code = getUrlParam('code');
|
||||
// debugger;
|
||||
console.log("-------code:",code)
|
||||
location.href = `./index.html#/pages/login/login?code=${code}`;
|
||||
// if (!code) { // 没有登入之前
|
||||
// var redirect_uri = encodeURI(window.location.origin + window.location.pathname);
|
||||
// var data = {
|
||||
// appid: 'wx47db6cca5930ce74',
|
||||
// redirect_uri: redirect_uri,
|
||||
// response_type: 'code',
|
||||
// scope: 'snsapi_userinfo',
|
||||
// state: ''
|
||||
// };
|
||||
// location.href =
|
||||
// `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${data.appid}&redirect_uri=${data.redirect_uri}&response_type=${data.response_type}&scope=${data.scope}&state=${data.state}#wechat_redirect`;
|
||||
// } else { // 登入后
|
||||
// localStorage.setItem('code', code);
|
||||
// setTimeout(() => {
|
||||
// location.href = './index.html#/pages/login/login';
|
||||
// }, 500);
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
|
||||
import msgUtils from './utils/msgUtils.js'
|
||||
import path from './utils/skip'
|
||||
import '@/utils/filter/filterUtils.js'
|
||||
|
||||
// 注册全局组件
|
||||
import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"
|
||||
import MescrollUni from "@/components/mescroll-uni/mescroll-uni.vue"
|
||||
Vue.component('mescroll-body', MescrollBody)
|
||||
Vue.component('mescroll-uni', MescrollUni)
|
||||
|
||||
|
||||
|
||||
|
||||
// #ifdef H5
|
||||
// 提交前需要注释 本地调试使用
|
||||
// const vconsole = require('vconsole')
|
||||
// Vue.prototype.$vconsole = new vconsole() // 使用vconsole
|
||||
// #endif
|
||||
|
||||
import CustomNavbarPage from '@/pages/components/customNavbarPage.vue'
|
||||
import CropStatusbarHeight from '@/pages/components/crop-statusbar-height/crop-statusbar-height.vue'
|
||||
Vue.component('custom-navbar-page', CustomNavbarPage)
|
||||
Vue.component('crop-statusbar-height', CropStatusbarHeight)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$api = msgUtils
|
||||
Vue.prototype.$path = path
|
||||
|
||||
App.mpType = 'app'
|
||||
|
||||
const app = new Vue({
|
||||
...App,
|
||||
path
|
||||
})
|
||||
app.$mount()
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
docker build -t registry.cn-shanghai.aliyuncs.com/box-mall/box-mall-app-web:2.0 --platform=linux/amd64 .
|
||||
|
||||
docker push registry.cn-shanghai.aliyuncs.com/box-mall/box-mall-app-web:2.0
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
docker build -t registry.cn-shanghai.aliyuncs.com/box-mall/box-mall-app-web:1.0 --platform=linux/amd64 .
|
||||
|
||||
docker push registry.cn-shanghai.aliyuncs.com/box-mall/box-mall-app-web:1.0
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
docker build -t registry.cn-shanghai.aliyuncs.com/miyamama_box/box-mall-app-web:1.0 --platform=linux/amd64 .
|
||||
|
||||
docker push registry.cn-shanghai.aliyuncs.com/miyamama_box/box-mall-app-web:1.0
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
{
|
||||
"name" : "妙趣盲盒",
|
||||
"appid" : "__UNI__8AC2CA7",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : 100,
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
"safearea" : {
|
||||
"bottom" : {
|
||||
"offset" : "auto"
|
||||
}
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : false,
|
||||
"waiting" : false,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules" : {
|
||||
"Maps" : {},
|
||||
"OAuth" : {},
|
||||
"Payment" : {},
|
||||
"Share" : {}
|
||||
},
|
||||
/* 应用发布信息 */
|
||||
"distribute" : {
|
||||
/* android打包配置 */
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
],
|
||||
"autoSdkPermissions" : false
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios" : {},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs" : {
|
||||
"ad" : {},
|
||||
"maps" : {
|
||||
"amap" : {
|
||||
"appkey_ios" : "40a745f83e5c11c12aa6c7dea464dbb9",
|
||||
"appkey_android" : "40a745f83e5c11c12aa6c7dea464dbb9"
|
||||
}
|
||||
},
|
||||
"oauth" : {
|
||||
"weixin" : {
|
||||
"appid" : "wx836b447885f16f22",
|
||||
"UniversalLinks" : ""
|
||||
}
|
||||
},
|
||||
"payment" : {
|
||||
"weixin" : {
|
||||
"__platform__" : [ "ios", "android" ],
|
||||
"appid" : "wx836b447885f16f22",
|
||||
"UniversalLinks" : ""
|
||||
},
|
||||
"alipay" : {
|
||||
"__platform__" : [ "ios", "android" ]
|
||||
}
|
||||
},
|
||||
"share" : {
|
||||
"weixin" : {
|
||||
"appid" : "wx836b447885f16f22",
|
||||
"UniversalLinks" : ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"splashscreen" : {
|
||||
"androidStyle" : "common",
|
||||
"android" : {
|
||||
"hdpi" : "static/common/splash.9.png",
|
||||
"xhdpi" : "static/common/splash.9.png",
|
||||
"xxhdpi" : "static/common/splash.9.png"
|
||||
}
|
||||
},
|
||||
"icons" : {
|
||||
"android" : {
|
||||
"hdpi" : "unpackage/res/icons/72x72.png",
|
||||
"xhdpi" : "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi" : "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi" : "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios" : {
|
||||
"appstore" : "unpackage/res/icons/1024x1024.png",
|
||||
"ipad" : {
|
||||
"app" : "unpackage/res/icons/76x76.png",
|
||||
"app@2x" : "unpackage/res/icons/152x152.png",
|
||||
"notification" : "unpackage/res/icons/20x20.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x" : "unpackage/res/icons/167x167.png",
|
||||
"settings" : "unpackage/res/icons/29x29.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"spotlight" : "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone" : {
|
||||
"app@2x" : "unpackage/res/icons/120x120.png",
|
||||
"app@3x" : "unpackage/res/icons/180x180.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"notification@3x" : "unpackage/res/icons/60x60.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"settings@3x" : "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x" : "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "wx836b447885f16f22",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : true
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"permission" : {
|
||||
"scope.userLocation" : {
|
||||
"desc" : "获取与门店距离"
|
||||
}
|
||||
},
|
||||
"requiredPrivateInfos" : [ "getLocation" ]
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
"h5" : {
|
||||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "./"
|
||||
}
|
||||
},
|
||||
"uniCloud" : {
|
||||
"provider" : "aliyun",
|
||||
"spaceId" : "mp-ee8153d2-d55b-4e47-82d3-64303d9910ea",
|
||||
"clientSecret" : "c8SiFkxUtsnS/CBjDJBgbg=="
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
export const myMixins = {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
mixinsTimer:null,
|
||||
mixinsTime:0,
|
||||
btnTimer:null
|
||||
};
|
||||
},
|
||||
onShow(){
|
||||
// pages/index/index //首页
|
||||
// pages/index/detail //盲盒详情
|
||||
// pages/index/card //使用提示卡
|
||||
// pages/index/confirmOrder //盲盒确认订单
|
||||
// pages/index/confirmOrder //盲盒支付成功页面
|
||||
// pages/index/prize //开奖页面
|
||||
|
||||
// pages/product/product //商品列表
|
||||
// pages/search/index//商品搜索
|
||||
// pages/search/search-list//商品搜索列表
|
||||
// pages/product/detail //商品详情页
|
||||
// pages/order/confirm //商品确认订单
|
||||
// pages/shop/paySuccess //商品支付成功页面
|
||||
|
||||
|
||||
// pages/mine/sign//签到
|
||||
|
||||
|
||||
// pages/mine/index //个人中心
|
||||
// pages/mine/setting //设置
|
||||
// pages/mine/editName //修改昵称
|
||||
// pages/agreement/index //用户协议
|
||||
// pages/agreement/privacy //隐私政策
|
||||
// pages/mine/about //关于我们
|
||||
// pages/mine/point //积分使用记录
|
||||
// pages/mine/coupon //优惠券
|
||||
// pages/mine/moreCoupon //更多优惠券
|
||||
// pages/mine/boxRecrd //物品
|
||||
// pages/mine/boxConfirm//物品发货提交订单
|
||||
// pages/order/index//我的订单
|
||||
// pages/mine/orderStatusDetail//订单详情
|
||||
// pages/mine/boxOrder//盲盒订单
|
||||
// pages/mine/address//地址管理
|
||||
// pages/mine/addAddress //编辑新增地址
|
||||
// pages/mine/collect//收藏列表
|
||||
|
||||
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const url = currentPage.route; // 当前页面路径
|
||||
const options = currentPage.options; // 当前页面参数
|
||||
console.log(url);
|
||||
this.mixinsTimer = setInterval(() => {
|
||||
this.mixinsTime++
|
||||
},1000)
|
||||
},
|
||||
onHide(){
|
||||
clearInterval(this.mixinsTimer)
|
||||
console.log(this.mixinsTime)
|
||||
this.mixinsTimer = null
|
||||
this.mixinsTime = 0
|
||||
console.log('页面隐藏,即将返回上一页,');
|
||||
|
||||
this.btnTimer = Math.floor(Math.random() * (1 - 100000 + 1)) + 100000
|
||||
clearInterval(this.btnTimer)
|
||||
|
||||
},
|
||||
onUnload(){
|
||||
console.log('页面卸载,即将返回上一页');
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue