Init project
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
/dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
unpackage/
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
@@ -0,0 +1,16 @@
|
||||
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
|
||||
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
|
||||
"version": "0.0",
|
||||
"configurations": [{
|
||||
"default" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"mp-weixin" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"type" : "uniCloud"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<script>
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "uview-ui/index.scss";
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
/*每个页面公共css */
|
||||
@import "animate.css";
|
||||
@import "./assets/iconfont/iconfont.css";
|
||||
@import "./assets/css/base.less";
|
||||
@import "./assets/css/reset.less";
|
||||
@import "./assets/css/style.less";
|
||||
</style>
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 拼团列表
|
||||
*/
|
||||
export function getCombinationList(data) {
|
||||
return request.get("/combination/list", data, { login: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼团产品详情
|
||||
* @param {*} id
|
||||
*/
|
||||
export function getCombinationDetail(id) {
|
||||
return request.get("/combination/detail/" + id, {}, { login: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼团 开团
|
||||
* @param {*} id
|
||||
*/
|
||||
export function getCombinationPink(id) {
|
||||
return request.get("/combination/pink/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼团 取消开团
|
||||
*/
|
||||
export function getCombinationRemove(data) {
|
||||
return request.post("/combination/remove", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼团海报
|
||||
* @param {*} id
|
||||
*/
|
||||
export function getCombinationPoster(data) {
|
||||
return request.post("/combination/poster", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀列表配置
|
||||
*/
|
||||
export function getSeckillConfig() {
|
||||
return request.get("/seckill/index", {}, { login: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀列表
|
||||
*/
|
||||
export function getSeckillList(time, data) {
|
||||
return request.get("/seckill/list/" + time, data, { login: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒杀产品详情
|
||||
*/
|
||||
export function getSeckillDetail(id) {
|
||||
return request.get("/seckill/detail/" + id, {}, { login: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价列表
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainList(data) {
|
||||
return request.get("/bargain/list", data, { login: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价产品详情
|
||||
*/
|
||||
export function getBargainDetail(id) {
|
||||
return request.get("/bargain/detail/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价 观看/分享/参与次数
|
||||
*/
|
||||
export function getBargainShare(data) {
|
||||
return request.post("/bargain/share", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价开启
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainStart(data) {
|
||||
return request.post("/bargain/start", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价 帮助好友砍价
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainHelp(data) {
|
||||
return request.post("/bargain/help", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价 砍掉金额
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainHelpPrice(data) {
|
||||
return request.post("/bargain/help/price", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价 砍价帮总人数、剩余金额、进度条、已经砍掉的价格
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainHelpCount(data) {
|
||||
return request.post("/bargain/help/count", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价 开启砍价用户信息
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainStartUser(data) {
|
||||
return request.post("/bargain/start/user", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价 砍价帮
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainHelpList(data) {
|
||||
return request.post("/bargain/help/list", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价海报
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainPoster(data) {
|
||||
return request.post("/bargain/poster", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价列表(已参与)
|
||||
* @param {*} data
|
||||
*/
|
||||
export function getBargainUserList(data) {
|
||||
return request.get("/bargain/user/list", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价取消
|
||||
*/
|
||||
export function getBargainUserCancel(data) {
|
||||
return request.post("/bargain/user/cancel", data);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 统计数据
|
||||
*/
|
||||
export function getStatisticsInfo() {
|
||||
return request.get("/admin/order/statistics", {}, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单月统计
|
||||
*/
|
||||
export function getStatisticsMonth(where) {
|
||||
return request.get("/admin/order/data", where, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单月统计
|
||||
*/
|
||||
export function getAdminOrderList(where) {
|
||||
return request.get("/admin/order/list", where, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单改价
|
||||
*/
|
||||
export function setAdminOrderPrice(data) {
|
||||
return request.post("/admin/order/price", data, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
export function setAdminOrderRemark(data) {
|
||||
return request.post("/admin/order/remark", data, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单详情
|
||||
*/
|
||||
export function getAdminOrderDetail(orderId) {
|
||||
return request.get("/admin/order/detail/" + orderId, {}, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单发货信息获取
|
||||
*/
|
||||
export function getAdminOrderDelivery(orderId) {
|
||||
return request.get(
|
||||
"/admin/order/detail/" + orderId,
|
||||
{},
|
||||
{ login: true }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单发货保存
|
||||
*/
|
||||
export function setAdminOrderDelivery(data) {
|
||||
return request.post("/admin/order/delivery/keep", data, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单统计图
|
||||
*/
|
||||
export function getStatisticsTime(data) {
|
||||
return request.get("/admin/order/time", data, { login: true });
|
||||
}
|
||||
/**
|
||||
* 线下付款订单确认付款
|
||||
*/
|
||||
export function setOfflinePay(data) {
|
||||
return request.post("/admin/order/offline", data, { login: true });
|
||||
}
|
||||
/**
|
||||
* 订单确认退款
|
||||
*/
|
||||
export function setOrderRefund(data) {
|
||||
return request.post("/admin/order/refund", data, { login: true });
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// 预售商品列表
|
||||
export function getPreSaleList(page, limit) {
|
||||
return request.get(
|
||||
"/preSale/list", {
|
||||
page,
|
||||
limit
|
||||
}, {
|
||||
login: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 每日特价列表
|
||||
export function getDailyGoodsList(page, limit) {
|
||||
return request.get(
|
||||
"/dailyGoods/list", {
|
||||
page,
|
||||
limit
|
||||
}, {
|
||||
login: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 秒杀产品时间区间
|
||||
export function getYxStoreSeckillIndex() {
|
||||
return request.get("/seckill/index", {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
// 秒杀产品列表
|
||||
export function getSeckillTopList(page, limit) {
|
||||
return request.get("/seckill/limitTime", {page, limit}, {login: true})
|
||||
}
|
||||
|
||||
// 秒杀产品列表
|
||||
export function getYxStoreSeckillPageList(page, limit, timeId) {
|
||||
return request.get("/seckill/list", {
|
||||
page,
|
||||
limit,
|
||||
timeId
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 秒杀活动规则
|
||||
export function getSeckillRule() {
|
||||
return request.get("/seckill/rule", {}, {login: false})
|
||||
}
|
||||
|
||||
// 预售商品详情
|
||||
export function getPreSale(id) {
|
||||
return request.get(`/preSale/detail/${id}`, {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 每日特价详情
|
||||
export function getDailyGoods(id) {
|
||||
return request.get(`/dailyGoods/detail/${id}`, {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 秒杀产品详情
|
||||
export function getYxStoreSeckill(id) {
|
||||
return request.get(`/seckill/detail/${id}`, {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 尖货推荐横幅
|
||||
export function sharpBannar() {
|
||||
return request.get("/productRecommend/index", {}, {
|
||||
login: false
|
||||
})
|
||||
}
|
||||
|
||||
// 尖货推荐-商品列表
|
||||
export function getSharpList(keyword) {
|
||||
let param = "";
|
||||
if (keyword.trim().length > 0) {
|
||||
param = `?keyword=${keyword}`;
|
||||
}
|
||||
return request.get("/product/hot" + param, {}, {
|
||||
login: false
|
||||
})
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 客栈接口
|
||||
* */
|
||||
import request from "@/utils/request";
|
||||
|
||||
|
||||
/**
|
||||
* 客栈申请
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postHotelApply(data) {
|
||||
return request.post("/api/hotelMain/apply", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈重新申请
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postHotelReApply(data) {
|
||||
return request.post("/api/hotelMain/reApply", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈点赞/取消
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelZan(innId) {
|
||||
return request.get("/api/hotelMain/zan/" + innId, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈信息修改
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postHotelInfoEdit(data) {
|
||||
return request.post("/api/hotelMain/edit", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈详情
|
||||
* @param innId 客栈id
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelDetail(innId) {
|
||||
return request.get("/api/hotelMain/info/" + innId, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈列表
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelList(data) {
|
||||
return request.get("/api/hotelMain/list", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈商品列表
|
||||
* @param data
|
||||
*/
|
||||
export function goodsList(data) {
|
||||
return request.get("/api/hotelMain/goodsList", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页客栈列表(不需要登录)
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHomeHotelList(data) {
|
||||
return request.get("/api/hotelMain/listIndex", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页客栈详情(不需要登录)
|
||||
* @param innId 客栈id
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHomeHotelDetail(innId) {
|
||||
return request.get("/api/hotelMain/infoIndex/" + innId, {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 客栈新增资讯
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postHotelNewsAdd(data) {
|
||||
return request.post("/api/hotelNews/add", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈删除资讯
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postHotelNewsDel(data) {
|
||||
return request.post("/api/hotelNews/del", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈资讯列表
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelNewsList(data) {
|
||||
return request.get("/api/hotelNews/list", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈资讯详情
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelNewsDetail(id) {
|
||||
return request.get("/api/hotelNews/info/" + id, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 客栈资讯点赞/取消
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelNewsZan(id) {
|
||||
return request.get("/api/hotelNews/zan/" + id, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的喜欢列表
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelNewsZanList(data) {
|
||||
return request.get("/api/hotelNews/zan/list", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有客栈的入驻的城市列表
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelCityList() {
|
||||
return request.get("/api/hotelMain/listCity", {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客栈类型列表
|
||||
* @param null
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelTypeList() {
|
||||
return request.get("/api/hotelMain/listType", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 店铺详情海报
|
||||
* @param id 店铺ID int
|
||||
* */
|
||||
export function getPoster(id) {
|
||||
return request.get("/api/hotelMain/poster/" + id, {}, {login: true})
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// 商户申请人数统计
|
||||
export function getCount() {
|
||||
return request.get("/merchantApply/count", {}, {login: true})
|
||||
}
|
||||
|
||||
// 商户申请入口页图片
|
||||
export function getImages() {
|
||||
return request.get("/merchantApply/images", {}, {login: true})
|
||||
}
|
||||
|
||||
// 商户申请页面数据
|
||||
export function getShopData(partnerId) {
|
||||
let param = {};
|
||||
if (partnerId) param.partnerId = partnerId;
|
||||
return request.get("/merchantApply/index", param, {login: true})
|
||||
}
|
||||
|
||||
// 查看商户申请
|
||||
// 1-特产供应商 2-店铺 3-文玩供应商
|
||||
export function getShopInfo() {
|
||||
return request.get("/merchantApply/info", {}, {login: true})
|
||||
}
|
||||
|
||||
// 提交店铺入驻申请
|
||||
export function saveShop(hotelInfo, partnerId) {
|
||||
let param = {hotelInfo};
|
||||
if (partnerId && partnerId.length > 0) param.partnerId = partnerId;
|
||||
return request.post("/merchantApply/hotel/submit", param, {login: true})
|
||||
}
|
||||
|
||||
// 提交特产供应商入驻申请
|
||||
export function saveSupplier(hotelInfo, supplierInfo, partnerId) {
|
||||
let param = {hotelInfo, supplierInfo};
|
||||
if (partnerId && partnerId.length > 0) param.partnerId = partnerId;
|
||||
return request.post("/merchantApply/goodSupplier/submit", param, {login: true})
|
||||
}
|
||||
|
||||
// 提交文玩供应商入驻申请
|
||||
export function saveWenwanSupplier(hotelInfo, supplierInfo, partnerId) {
|
||||
let param = {hotelInfo, supplierInfo};
|
||||
if (partnerId && partnerId.length > 0) param.partnerId = partnerId;
|
||||
return request.post("/merchantApply/wenwanSupplier/submit", param, {login: true})
|
||||
}
|
||||
|
||||
// 撤销入驻申请
|
||||
export function removeApply() {
|
||||
return request.post("/merchantApply/revoke")
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* NIGHT V4 API
|
||||
*/
|
||||
|
||||
// 获取抽奖活动详情
|
||||
export function getLotteryInfo() {
|
||||
return request.get("/lottery/detail", {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 抽奖
|
||||
export function draw(id) {
|
||||
return request.post("/lottery/draw", {
|
||||
lotteryId: id
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 加入抽奖活动
|
||||
export function join(id) {
|
||||
return request.post("/lottery/join", {
|
||||
lotteryId: id
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 获取我的抽奖记录
|
||||
export function getLotteryRecordsMy(id, page) {
|
||||
return request.get("/lottery/records/my", {
|
||||
lotteryId: id,
|
||||
page
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 获取最新抽奖记录
|
||||
export function getLotteryRecordsNew(id) {
|
||||
return request.get("/lottery/records/new", {
|
||||
lotteryId: id
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 领奖
|
||||
export function takePrize(lotteryRecordId, addressId) {
|
||||
return request.post("/lottery/takePrize", {
|
||||
addressId,
|
||||
lotteryRecordId
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 订单确认
|
||||
* */
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 通过购物车 id 获取订单信息
|
||||
* @param cartId
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postOrderConfirm(cartId) {
|
||||
return request.post("/order/confirm", {
|
||||
cartId
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单金额
|
||||
* @param key
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postOrderComputed(key, data) {
|
||||
return request.post("/order/computed/" + key, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定金额可用优惠券
|
||||
* @param price
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getOrderCoupon(cartId) {
|
||||
return request.get("/coupons/order/" + cartId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单
|
||||
* @param key
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export function createOrder(key, data) {
|
||||
return request.post("/order/create/" + key, data || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单统计数据
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getOrderData() {
|
||||
return request.get("/order/data");
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单列表
|
||||
* @param data.type 0 全部,1 待付款,2 待发货,3 待发货,4 待评价,5 已完成,-1 退款中,-2 已退款,-3 退款(退款中+已退款+已驳回),-4 已驳回
|
||||
* @return _status._type 0 待付款,1 待发货,2 待收货,3 待评价,4 已完成,-1 退款中,-2 已退款,-3 已驳回
|
||||
*/
|
||||
export function getOrderList(data) {
|
||||
return request.get("/order/list", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订单
|
||||
* @returns {*}
|
||||
*/
|
||||
export function cancelOrder(id) {
|
||||
return request.post("/order/cancel", {
|
||||
id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单详情
|
||||
* @returns {*}
|
||||
*/
|
||||
export function orderDetail(id) {
|
||||
return request.get("/order/detail/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款理由
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getRefundReason() {
|
||||
return request.get("/order/refund/reason");
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交退款
|
||||
* @returns {*}
|
||||
*/
|
||||
export function postOrderRefund(data) {
|
||||
return request.post("/order/refund/verify", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认收货
|
||||
* @returns {*}
|
||||
*/
|
||||
export function takeOrder(uni) {
|
||||
return request.post("/order/take", {
|
||||
uni
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除订单
|
||||
* @returns {*}
|
||||
*/
|
||||
export function delOrder(uni) {
|
||||
return request.post("/order/del", {
|
||||
uni
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单查询物流信息
|
||||
* @returns {*}
|
||||
*/
|
||||
export function express(params) {
|
||||
return request.post("order/express", params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单支付
|
||||
* @returns {*}
|
||||
*/
|
||||
export function payOrder(uni, paytype, from) {
|
||||
return request.post("order/pay", {
|
||||
uni,
|
||||
paytype,
|
||||
from
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单支付(余额)
|
||||
* @returns {*}
|
||||
*/
|
||||
export function yuePayOrder(uni, paytype, from, passwordPay) {
|
||||
return request.post("order/pay", {
|
||||
uni,
|
||||
paytype,
|
||||
from,
|
||||
passwordPay
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单核销
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export function orderVerific(verifyCode, isConfirm) {
|
||||
return request.post("order/order_verific", {verifyCode, isConfirm});
|
||||
}
|
||||
|
||||
// 延迟收货
|
||||
export function orderDelay(uni) {
|
||||
return request.post("/order/extendedDelivery", {uni})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 康养食材首页
|
||||
* */
|
||||
export function wellnessFood() {
|
||||
return request.get("/wellnessFood/index", {}, {login: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 养生圣地列表
|
||||
* */
|
||||
export function wellnessPlaceList(param) {
|
||||
return request.get("/api/hotelMain/wellnessPlaceList", param, {login: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 定制礼品首页
|
||||
* */
|
||||
export function customizedGift() {
|
||||
return request.get("/customizedGift/index", {}, {login: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 千县名品首页
|
||||
* */
|
||||
export function countyFamous() {
|
||||
return request.get("/countyFamous/index", {}, {login: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 名品故事列表
|
||||
* */
|
||||
export function getStoryList(page) {
|
||||
return request.get("/countyFamous/content/list?page=" + page, {}, {login: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 故事详情
|
||||
* */
|
||||
export function getStoryDetail(id) {
|
||||
return request.get("/countyFamous/content/" + id, {}, {login: true})
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 首页
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHomeData() {
|
||||
return request.get("index", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
// 用户协议与隐私政策
|
||||
export function getAgree() {
|
||||
return request.get("/agreement", {}, {login: false})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频分类
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getStoreVideoType() {
|
||||
return request.get("/dict/getStoreVideoType", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客栈资讯分类
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getHotelNewsCategory() {
|
||||
return request.get("/dict/getHotelNewsCategory", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步直播数据
|
||||
* @returns {*}
|
||||
*/
|
||||
export function wxLiveSynchro() {
|
||||
return request.get("/synchro", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询直播间
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getWxLiveList(q) {
|
||||
return request.get("/yxWechatLive", q, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询直播间回放列表
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getWxLiveReplayList(id) {
|
||||
return request.get("/yxWechatLive/getLiveReplay/", id, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章 轮播列表
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getArticleBanner() {
|
||||
return request.get("/article/banner/list", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章分类列表
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getArticleCategory() {
|
||||
return request.get("/article/category/list", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章 热门列表
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getArticleHotList() {
|
||||
return request.get("/article/hot/list", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章列表
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getArticleList(q) {
|
||||
return request.get("/article/list/", q, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getShare() {
|
||||
return request.get("/share", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章详情
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getArticleDetails(id) {
|
||||
return request.get("/article/details/" + id, {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信sdk配置
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getWechatConfig() {
|
||||
// return request.get(
|
||||
// "/wechat/config",
|
||||
// { url: document.location.href },
|
||||
// { login: false }
|
||||
// );
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信sdk配置
|
||||
* @returns {*}
|
||||
*/
|
||||
export function wechatAuth(code, spread, login_type) {
|
||||
return request.get(
|
||||
"/wechat/auth", {
|
||||
code,
|
||||
spread,
|
||||
login_type
|
||||
}, {
|
||||
login: false
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取快递公司
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getLogistics() {
|
||||
return request.get("/logistics", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片base64
|
||||
* @retins {*}
|
||||
* */
|
||||
export function imageBase64(image, code) {
|
||||
return request.post(
|
||||
"/image_base64", {
|
||||
image: image,
|
||||
code: code
|
||||
}, {
|
||||
login: false
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据字典
|
||||
* @param code 编码
|
||||
*/
|
||||
export function queryByType(code) {
|
||||
return request.get("/dict/queryByType/" + code, {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户UnionId
|
||||
export function userUnionId() {
|
||||
return request.get("/userUnionId", {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 获取分享图片
|
||||
export function getShareImage() {
|
||||
return request.get("/shareImage", {}, {login: false})
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* NIGHT V4 API
|
||||
*/
|
||||
|
||||
// 店铺列表
|
||||
export function fetchShop(merchantName) {
|
||||
return request.post("/merchant/products", {
|
||||
merchantName
|
||||
}, {
|
||||
login: false
|
||||
})
|
||||
}
|
||||
|
||||
// 店铺内商品列表
|
||||
export function fetchGoods(merId, orderBy) {
|
||||
return request.post("/merchant/productList", {
|
||||
merId,
|
||||
orderBy
|
||||
}, {
|
||||
login: false
|
||||
})
|
||||
}
|
||||
|
||||
// 特产列表
|
||||
export function fetchSpecialty(page, cityName) {
|
||||
return request.get("/products/normal", {
|
||||
isGood: 1,
|
||||
page,
|
||||
cityName,
|
||||
limit: 20
|
||||
}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 获取特产商品城市列表
|
||||
export function fetchCity() {
|
||||
return request.post("/yxStoreProductGoods/list", {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 招商信息
|
||||
export function getAttract() {
|
||||
return request.get("/api/businessApply/index", {}, {
|
||||
login: false
|
||||
})
|
||||
}
|
||||
|
||||
// 提交招商信息
|
||||
export function saveAttract(data) {
|
||||
return request.post("/api/businessApply/apply", data, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
|
||||
// 招商项目入口页
|
||||
export function investment() {
|
||||
return request.get("/investment/index", {}, {login: true})
|
||||
}
|
||||
|
||||
// 招商项目列表
|
||||
export function investmentList(page, keyword) {
|
||||
return request.get("/investment/list", {page, keyword}, {login: true})
|
||||
}
|
||||
|
||||
// 招商项目详情
|
||||
export function investmentInfo(id) {
|
||||
return request.get("/investment/info/" + id, {}, {login: true})
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 商品分类
|
||||
* @param pid 35、文玩
|
||||
* @param excludePid 排除ID
|
||||
* */
|
||||
export function getCategory(pid, excludePid, cityId, isGood) {
|
||||
let param = {};
|
||||
if (pid) param.pid = pid;
|
||||
if (excludePid) param.excludePid = excludePid;
|
||||
if (cityId) param.cityId = cityId;
|
||||
if (isGood) param.isGood = 1;
|
||||
return request.get("/category", param, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 商品详情
|
||||
* */
|
||||
export function getProductDetail(id, data) {
|
||||
return request.get("/product/detail/" + id, data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 商品分销二维码
|
||||
* */
|
||||
export function getProductCode(id) {
|
||||
return request.get("/product/code/" + id, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
export function getProducts(q) {
|
||||
return request.get("/products", q, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
// 普通商品
|
||||
export function fetchNormal(q) {
|
||||
return request.get("/products/normal", q, {login: false})
|
||||
}
|
||||
|
||||
// 文玩商品
|
||||
export function fetchWenwan(q) {
|
||||
return request.get("/products/wenwan", q, {login: false})
|
||||
}
|
||||
|
||||
/*
|
||||
* 购物车数量
|
||||
* */
|
||||
export function getCartNum() {
|
||||
return request.get("/cart/count");
|
||||
}
|
||||
|
||||
/*
|
||||
* 添加收藏
|
||||
* */
|
||||
export function toCollect(id, category) {
|
||||
return request.get("/collect/add/" + id + "/" + category);
|
||||
}
|
||||
|
||||
/*
|
||||
* 为你推荐
|
||||
* */
|
||||
export function getHostProducts(page, limit) {
|
||||
return request.get(
|
||||
"/product/hot", {
|
||||
page: page,
|
||||
limit: limit
|
||||
}, {
|
||||
login: false
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* 精品、热门、首发列表
|
||||
* */
|
||||
export function getGroomList(type) {
|
||||
return request.get("/groom/list/" + type, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取商品海报
|
||||
* */
|
||||
export function getProductPoster(id) {
|
||||
return request.get("/product/poster/" + id, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 购物车 添加
|
||||
* */
|
||||
export function postCartAdd(data) {
|
||||
return request.post("/cart/add", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 购物车列表
|
||||
* */
|
||||
export function getCartList() {
|
||||
return request.get("/cart/list");
|
||||
}
|
||||
|
||||
/*
|
||||
* 购物车 删除
|
||||
* */
|
||||
export function postCartDel(ids) {
|
||||
return request.post("/cart/del", {
|
||||
ids
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 购物车 获取数量
|
||||
* */
|
||||
export function getCartCount(data) {
|
||||
return request.get("/cart/count", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 购物车 修改商品数量
|
||||
* */
|
||||
export function changeCartNum(id, number) {
|
||||
return request.post("/cart/num", {
|
||||
id,
|
||||
number
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索推荐关键字
|
||||
*/
|
||||
export function getSearchKeyword() {
|
||||
return request.get("/search/keyword", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品评论列表
|
||||
*/
|
||||
export function getReplyList(id, q) {
|
||||
return request.get("/reply/list/" + id, q, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品评价数量和好评度
|
||||
*/
|
||||
export function getReplyConfig(id) {
|
||||
return request.get("/reply/config/" + id, {}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 评价页面获取单个产品详情
|
||||
*/
|
||||
export function postOrderProduct(unique) {
|
||||
return request.post("/order/product", {
|
||||
unique
|
||||
}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交评价页面;
|
||||
*/
|
||||
export function postOrderComment(data) {
|
||||
return request.post("/order/comment", data, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
export function storeListApi(data) {
|
||||
return request.get("store_list", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
+525
@@ -0,0 +1,525 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 省市区
|
||||
*/
|
||||
export function getCity(data) {
|
||||
return request.get("/city_list", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
export function district(data) {
|
||||
// return request.get("/city_list", data, {
|
||||
return request.get("/citys", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param data object 用户账号密码
|
||||
*/
|
||||
export function login(data) {
|
||||
return request.post("/login", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户手机号登录
|
||||
* @param data object 用户手机号 也只能
|
||||
*/
|
||||
export function loginMobile(data) {
|
||||
return request.post("/login/mobile", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户发送验证码
|
||||
* @param data object 用户手机号
|
||||
*/
|
||||
export function registerVerify(data) {
|
||||
return request.post("/register/verify", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户手机号注册
|
||||
* @param data object 用户手机号 验证码 密码
|
||||
*/
|
||||
export function register(data) {
|
||||
return request.post("/register", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户手机号修改密码
|
||||
* @param data object 用户手机号 验证码 密码
|
||||
*/
|
||||
export function registerReset(data) {
|
||||
return request.post("/register/reset", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户绑定上级
|
||||
* @param nil
|
||||
*/
|
||||
export function userBindParent(data) {
|
||||
return request.post("/user/spread", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户等级信息
|
||||
* @param nil
|
||||
*/
|
||||
export function getUserLevelInfo() {
|
||||
return request.get("/user/levelInfo");
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户升级协议保存
|
||||
* @param nil
|
||||
*/
|
||||
export function saveUserProtocol(data) {
|
||||
return request.post("/user/saveGd", data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 视频回放列表
|
||||
* */
|
||||
export function getVideoList(q) {
|
||||
return request.get("/video_list", q, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 领取优惠券列表
|
||||
* */
|
||||
export function getCoupon(q) {
|
||||
return request.get("/coupons", q, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 点击领取优惠券
|
||||
* */
|
||||
export function getCouponReceive(id) {
|
||||
return request.post("/coupon/receive", {
|
||||
couponId: id
|
||||
}, {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 批量领取优惠券
|
||||
* */
|
||||
export function couponReceiveBatch(couponId) {
|
||||
return request.post("/coupon/receive/batch", {
|
||||
couponId
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 我的优惠券
|
||||
* */
|
||||
export function getCouponsUser(type) {
|
||||
return request.get("/coupons/user/" + type);
|
||||
}
|
||||
|
||||
/*
|
||||
* 个人中心
|
||||
* */
|
||||
export function getUser() {
|
||||
return request.get("/user");
|
||||
}
|
||||
|
||||
/*
|
||||
* 用户信息
|
||||
* */
|
||||
export function getUserInfo() {
|
||||
return request.get("/userinfo", {
|
||||
login: true
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 小程序检查授权
|
||||
* */
|
||||
export function wxappCheckAuth(data) {
|
||||
return request.post("/wxapp/checkAuthCode", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 小程序登陆
|
||||
* */
|
||||
export function wxappAuth(data) {
|
||||
return request.post("/wxapp/auth", data, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 个人中心(功能列表)
|
||||
* */
|
||||
export function getMenuUser() {
|
||||
return request.get("/menu/user");
|
||||
}
|
||||
|
||||
/*
|
||||
* 用户设置支付密码
|
||||
* */
|
||||
export function setUserPayPwd(data) {
|
||||
return request.post("/user/savePayPass", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 通知消息列表
|
||||
* */
|
||||
export function noticeList(data) {
|
||||
return request.get("/platNoticeSend/list", data, { login: true });
|
||||
}
|
||||
|
||||
/*
|
||||
* 通知消息详情
|
||||
* */
|
||||
export function noticeDetail(data) {
|
||||
return request.post("/platNoticeSend/read", data, { login: true });
|
||||
}
|
||||
|
||||
/*
|
||||
* 用户修改支付密码
|
||||
* */
|
||||
export function modifyUserPayPwd(data) {
|
||||
return request.post("/user/updatePayPass", data);
|
||||
}
|
||||
|
||||
export function bankList(data) {
|
||||
return request.get("/api/yxBank/bank/list", data, { login: true });
|
||||
}
|
||||
|
||||
export function bankCardList(data) {
|
||||
return request.get("/api/yxBankCard/bankCard/list", data, { login: true });
|
||||
}
|
||||
|
||||
export function addBankCard(data) {
|
||||
return request.post("/api/yxBankCard/bankCard/add", data, { login: true });
|
||||
}
|
||||
|
||||
export function deleteBankCard(data) {
|
||||
return request.post("/api/yxBankCard/bankCard/del", data, { login: true });
|
||||
}
|
||||
|
||||
/*
|
||||
* 地址列表
|
||||
* */
|
||||
export function getAddressList(data) {
|
||||
return request.get("/address/list", data || {});
|
||||
}
|
||||
|
||||
/*
|
||||
* 删除地址
|
||||
* */
|
||||
export function getAddressRemove(id) {
|
||||
return request.post("/address/del", {
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 设置默认地址
|
||||
* */
|
||||
export function getAddressDefaultSet(id) {
|
||||
return request.post("/address/default/set", {
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取默认地址
|
||||
* */
|
||||
export function getAddressDefault() {
|
||||
return request.get("/address/default");
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取单个地址
|
||||
* */
|
||||
export function getAddress(id) {
|
||||
return request.get("/address/detail/" + id);
|
||||
}
|
||||
|
||||
/*
|
||||
* 修改 添加地址
|
||||
* */
|
||||
export function postAddress(data) {
|
||||
return request.post("/address/edit", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取收藏产品
|
||||
* */
|
||||
export function getCollectUser(page, limit) {
|
||||
return request.get("/collect/user", {
|
||||
page: page,
|
||||
limit: limit
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 删除收藏产品
|
||||
* */
|
||||
export function getCollectDel(id, category) {
|
||||
return request.post("/collect/del", {
|
||||
id: id,
|
||||
category: category
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 批量收藏产品
|
||||
* */
|
||||
export function postCollectAll(data) {
|
||||
return request.post("/collect/all", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 添加收藏产品
|
||||
* */
|
||||
export function getCollectAdd(id, category) {
|
||||
return request.post("collect/add", {
|
||||
id: id,
|
||||
category: category
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 签到配置
|
||||
* */
|
||||
export function getSignConfig() {
|
||||
return request.get("/sign/config");
|
||||
}
|
||||
|
||||
/*
|
||||
* 签到里的签到列表
|
||||
* */
|
||||
export function getSignList(page, limit) {
|
||||
return request.get("/sign/list", {
|
||||
page: page,
|
||||
limit: limit
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 签到列表
|
||||
* */
|
||||
export function getSignMonth(page, limit) {
|
||||
return request.get("/sign/month", {
|
||||
page: page,
|
||||
limit: limit
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 签到用户信息
|
||||
* */
|
||||
export function postSignUser(sign) {
|
||||
return request.post("/sign/user", sign);
|
||||
}
|
||||
|
||||
/*
|
||||
* 签到
|
||||
* */
|
||||
export function postSignIntegral(sign) {
|
||||
return request.post("/sign/integral", sign);
|
||||
}
|
||||
|
||||
/*
|
||||
* 推广数据
|
||||
* */
|
||||
export function getSpreadInfo() {
|
||||
return request.get("/commission");
|
||||
}
|
||||
|
||||
/*
|
||||
* 推广人列表
|
||||
* */
|
||||
export function getSpreadUser(screen) {
|
||||
return request.post("/spread/people", screen);
|
||||
}
|
||||
|
||||
/*
|
||||
* 推广人订单
|
||||
* */
|
||||
export function getSpreadOrder(where) {
|
||||
return request.post("/spread/order", where);
|
||||
}
|
||||
|
||||
/*
|
||||
* 资金明细(types|0=全部,1=消费,2=充值,3=返佣,4=提现)
|
||||
* */
|
||||
export function getCommissionInfo(q, types) {
|
||||
return request.get("/spread/commission/" + types, q);
|
||||
}
|
||||
|
||||
/*
|
||||
* 积分记录
|
||||
* */
|
||||
export function getIntegralList(q) {
|
||||
return request.get("/integral/list", q);
|
||||
}
|
||||
|
||||
/*
|
||||
* 提现记录
|
||||
* */
|
||||
export function getWithdrawalRecordList(q) {
|
||||
return request.get("/list", q);
|
||||
}
|
||||
|
||||
/*
|
||||
* 提现银行
|
||||
* */
|
||||
export function getBank() {
|
||||
return request.get("/extract/bank");
|
||||
}
|
||||
|
||||
/*
|
||||
* 提现申请
|
||||
* */
|
||||
export function postCashInfo(cash) {
|
||||
return request.post("/extract/cash", cash);
|
||||
}
|
||||
|
||||
/*
|
||||
* 会员中心
|
||||
* */
|
||||
export function getVipInfo() {
|
||||
return request.get("/user/level/grade");
|
||||
}
|
||||
|
||||
/*
|
||||
* 会员等级任务
|
||||
* */
|
||||
export function getVipTask(id) {
|
||||
return request.get("/user/level/task/" + id);
|
||||
}
|
||||
|
||||
/*
|
||||
* 资金统计
|
||||
* */
|
||||
export function getBalance() {
|
||||
return request.get("/user/balance");
|
||||
}
|
||||
|
||||
/*
|
||||
* 活动状态
|
||||
* */
|
||||
export function getActivityStatus() {
|
||||
return request.get("/user/activity", {}, {
|
||||
login: false
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 活动状态
|
||||
* */
|
||||
export function getSpreadImg(data) {
|
||||
|
||||
return request.get("/spread/banner", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 用户修改信息
|
||||
* */
|
||||
export function postUserEdit(data) {
|
||||
return request.post("/user/edit", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 用户修改信息
|
||||
* */
|
||||
export function getChatRecord(to_uid, data) {
|
||||
return request.get("user/service/record/" + to_uid, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 用户修改信息
|
||||
* */
|
||||
export function serviceList() {
|
||||
return request.get("user/service/list");
|
||||
}
|
||||
|
||||
/*
|
||||
* 公众号充值
|
||||
* */
|
||||
export function rechargeWechat(data) {
|
||||
return request.post("/recharge/wechat", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 退出登录
|
||||
* */
|
||||
export function getLogout() {
|
||||
return request.post("/auth/logout");
|
||||
}
|
||||
|
||||
/*
|
||||
* 绑定手机号(小程序方式)
|
||||
* */
|
||||
export function bindingPhone(data) {
|
||||
return request.post("wxapp/binding", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* 绑定手机号(手机号,验证码方式)
|
||||
* */
|
||||
export function bindingPhoneByInput(data) {
|
||||
return request.post("/binding", data);
|
||||
}
|
||||
|
||||
/*
|
||||
* h5切换公众号登陆
|
||||
* */
|
||||
export function switchH5Login() {
|
||||
return request.post("switch_h5", {
|
||||
from: "wechat"
|
||||
});
|
||||
}
|
||||
/*
|
||||
* 获取推广人排行
|
||||
* */
|
||||
export function getRankList(q) {
|
||||
return request.get("rank", q);
|
||||
}
|
||||
/*
|
||||
* 获取佣金排名
|
||||
* */
|
||||
export function getBrokerageRank(q) {
|
||||
return request.get("brokerage_rank", q);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测会员等级
|
||||
*/
|
||||
export function setDetection() {
|
||||
return request.get("user/level/detection");
|
||||
}
|
||||
|
||||
|
||||
export function getRechargeApi() {
|
||||
return request.get("recharge/index");
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// 文玩店铺列表
|
||||
export function fetchStore(page, cityName, name) {
|
||||
return request.get("/api/hotelMain/wenwanList", {
|
||||
limit: 10,
|
||||
page,
|
||||
cityName,
|
||||
name
|
||||
}, {login: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品城市列表
|
||||
* @param type 类型:0、特产;1、文玩;2、七彩云上;3、定制礼品;4、康养食材;5、千县名品;6、景点门票
|
||||
* */
|
||||
export function fetchCity(type) {
|
||||
return request.get(`/wenwan/cityList/${type}`, {}, {
|
||||
login: true
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
@charset "UTF-8";
|
||||
/**
|
||||
*相关初始化
|
||||
*/
|
||||
|
||||
/*重置uni-checkbox样式开始*/
|
||||
uni-checkbox .uni-checkbox-input{
|
||||
border-radius: 50% !important;
|
||||
}
|
||||
|
||||
uni-checkbox .uni-checkbox-input:hover {
|
||||
border-color: #FE5261 !important;
|
||||
}
|
||||
|
||||
uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked{
|
||||
background-color: #FE5261 !important;
|
||||
border: 1px solid #FE5261 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
checkbox .wx-checkbox-input {
|
||||
border-radius: 50% !important;
|
||||
}
|
||||
|
||||
checkbox .wx-checkbox-input:hover {
|
||||
border-color: #FE5261 !important;
|
||||
}
|
||||
|
||||
checkbox .wx-checkbox-input.wx-checkbox-input-checked {
|
||||
background-color: #FE5261 !important;
|
||||
border: 1px solid #FE5261 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/*重置uni-checkbox样式结束*/
|
||||
.font-color-lightred{
|
||||
color:#FE5261 !important;
|
||||
}
|
||||
|
||||
.font-color-white{
|
||||
color: #fff !important;
|
||||
}
|
||||
.font-color-red {
|
||||
color: #FE5261 !important;
|
||||
}
|
||||
.font-color-green{
|
||||
color: #00c17b !important;
|
||||
}
|
||||
|
||||
.bg-color-lightred{
|
||||
background-color:#FE5261 !important;
|
||||
}
|
||||
.bg-color-green{
|
||||
background-color: #00c17b !important;
|
||||
}
|
||||
.bg-color-red {
|
||||
background-color: #FE5261 !important;
|
||||
}
|
||||
.nav-bg-color-red{
|
||||
background-color: #FE5261 !important;
|
||||
}
|
||||
.icon-color {
|
||||
color: #FE5261;
|
||||
}
|
||||
.cart-color {
|
||||
color: #FE5261 !important;
|
||||
border: 1px solid #FE5261 !important;
|
||||
}
|
||||
/* padding20 */
|
||||
.padding20 {
|
||||
padding: 0.2*100rpx;
|
||||
}
|
||||
/* pad20 */
|
||||
.pad20 {
|
||||
padding: 0 0.2*100rpx;
|
||||
}
|
||||
/* padding30 */
|
||||
.padding30 {
|
||||
padding: 0.3*100rpx;
|
||||
}
|
||||
/*pad30 */
|
||||
.pad30 {
|
||||
padding: 0 0.3*100rpx;
|
||||
}
|
||||
/* layout */
|
||||
.acea-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/* 辅助类 */
|
||||
}
|
||||
.acea-row.row-middle {
|
||||
align-items: center;
|
||||
}
|
||||
.acea-row.row-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.acea-row.row-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.acea-row.row-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.acea-row.row-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.acea-row.row-left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.acea-row.row-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.acea-row.row-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
.acea-row.row-column-around {
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.acea-row.row-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
.acea-row.row-column-between {
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
/* 上下左右垂直居中 */
|
||||
.acea-row.row-center-wrapper {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* 上下两边居中对齐 */
|
||||
.acea-row.row-between-wrapper {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 轮播图 */
|
||||
.slider-banner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
/* height:750rpx; */
|
||||
overflow: hidden;
|
||||
}
|
||||
.slider-banner{
|
||||
|
||||
}
|
||||
.slider-banner .swiper-container {
|
||||
height: 100%;
|
||||
}
|
||||
.slider-banner image{
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.start {
|
||||
width: 1.22*100rpx;
|
||||
height: 0.3*100rpx;
|
||||
background-image: url("~@/static/images/start.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.22*100rpx auto;
|
||||
}
|
||||
.start.star5 {
|
||||
background-position: 0 0.03*100rpx;
|
||||
}
|
||||
.start.star4 {
|
||||
background-position: 0 -0.3*100rpx;
|
||||
}
|
||||
.start.star3 {
|
||||
background-position: 0 -0.7*100rpx;
|
||||
}
|
||||
.start.star2 {
|
||||
background-position: 0 -1.05*100rpx;
|
||||
}
|
||||
.start.star1 {
|
||||
background-position: 0 -1.4*100rpx;
|
||||
}
|
||||
.start.star0 {
|
||||
background-position: 0 -1.75*100rpx;
|
||||
}
|
||||
/* 单选框和多选框 */
|
||||
.checkbox-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
.checkbox-wrapper input {
|
||||
display: none;
|
||||
}
|
||||
.checkbox-wrapper .icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 50%;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
.checkbox-wrapper input:checked + .icon {
|
||||
background-color: #e93323;
|
||||
border-color: #e93323;
|
||||
background-image: url("/static/images/enter.png");
|
||||
background-size: 0.21*100rpx 0.15*100rpx;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
.Loads {
|
||||
height: 0.8*100rpx;
|
||||
font-size: 0.25*100rpx;
|
||||
color: #000;
|
||||
}
|
||||
.Loads .iconfont {
|
||||
font-size: 0.3*100rpx;
|
||||
margin-right: 0.1*100rpx;
|
||||
height: 0.32*100rpx;
|
||||
line-height: 0.32*100rpx;
|
||||
}
|
||||
/*加载动画*/
|
||||
@keyframes load {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.loadingpic {
|
||||
animation: load 3s linear 1s infinite;
|
||||
}
|
||||
.loading {
|
||||
animation: load linear 1s infinite;
|
||||
}
|
||||
|
||||
.page-v4{
|
||||
background: #F5F5FA;
|
||||
}
|
||||
|
||||
.bold{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tc{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// flex布局
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.jc-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.jc-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.jc-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.jc-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.jc-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ai-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.ai-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.ai-center {
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
.change-section {
|
||||
margin: 24rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.introduce {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.subInfo {
|
||||
margin-top: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #8B8F99;
|
||||
}
|
||||
|
||||
.share {
|
||||
margin-top: 20rpx;
|
||||
|
||||
.money {
|
||||
.num {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-section {
|
||||
margin: 24rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.service-section {
|
||||
.cell {
|
||||
margin-bottom: 30rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
|
||||
.after-title {
|
||||
width: 120rpx;
|
||||
color: #ADADAD;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
position: relative;
|
||||
margin-top: 20rpx;
|
||||
color: #9D9D9D;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #9D9D9D;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #FF564A;
|
||||
}
|
||||
|
||||
.icon-time {
|
||||
position: relative;
|
||||
top: 6rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.cell:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.store-section {
|
||||
.logo {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
margin-right: 10rpx;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 500rpx;
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #666;
|
||||
font-size: 22rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.product-list {
|
||||
margin-top: 30rpx;
|
||||
|
||||
.item {
|
||||
width: 198rpx;
|
||||
height: 318rpx;
|
||||
margin-right: 30rpx;
|
||||
|
||||
.cover-box {
|
||||
position: relative;
|
||||
width: 198rpx;
|
||||
height: 198rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.tag{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 92rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.cover{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
image {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.more-t {
|
||||
margin-top: 10rpx;
|
||||
color: #333;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.price {
|
||||
margin-top: 10rpx;
|
||||
color: #DA0000;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.param-section {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
|
||||
.table {
|
||||
margin-top: 30rpx;
|
||||
border: 2rpx solid #D2D2D2;
|
||||
|
||||
.row {
|
||||
border-bottom: 2rpx solid #D2D2D2;
|
||||
|
||||
.title {
|
||||
width: 160rpx;
|
||||
padding: 16rpx;
|
||||
border-right: 2rpx solid #D2D2D2;
|
||||
color: #ADADAD;
|
||||
}
|
||||
}
|
||||
.row:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
.value {
|
||||
width: 100%;
|
||||
padding: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
.u-form {
|
||||
margin-top: 8rpx;
|
||||
|
||||
.u-form-item__body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.u-cell-group {
|
||||
margin-bottom: 16rpx;
|
||||
padding: 40rpx 0;
|
||||
background: #FFFFFF;
|
||||
|
||||
.u-cell-group__title {
|
||||
padding-top: 0;
|
||||
|
||||
.u-cell-group__title__text {
|
||||
font-weight: bold;
|
||||
font-size: 40rpx;
|
||||
line-height: 58rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-cell {
|
||||
.u-cell__body__content {
|
||||
flex: 0 !important;
|
||||
}
|
||||
|
||||
.u-cell__title {
|
||||
width: 216rpx !important;
|
||||
|
||||
.u-cell__title-text {
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.u-text__value {
|
||||
width: 398rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #EF1C1C;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
.line {
|
||||
width: 690rpx;
|
||||
}
|
||||
|
||||
.u-textarea {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.area-item {
|
||||
.uni-input {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 750rpx;
|
||||
height: 80rpx;
|
||||
background: #FF564A;
|
||||
color: #FFFFFF;
|
||||
font-size: 36rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.btn-disabled {
|
||||
background: #DDDDDD;
|
||||
}
|
||||
|
||||
.btn-remove{
|
||||
background: #FF9696;
|
||||
}
|
||||
|
||||
.btn-upload {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 176rpx;
|
||||
height: 64rpx;
|
||||
border: 2rpx solid #1F77EE;
|
||||
border-radius: 32rpx;
|
||||
background: rgba(245, 245, 245, 0.39);
|
||||
color: #1F77EE;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.box-upload {
|
||||
//margin-top: 12rpx;
|
||||
|
||||
.img-preview {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 8rpx;
|
||||
margin: 0 20rpx 20rpx 0;
|
||||
}
|
||||
|
||||
.pics-box {
|
||||
width: 690rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.u-upload__wrap{
|
||||
width: 690rpx;
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
width: 80vw;
|
||||
height: 80vh;
|
||||
padding: 30rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.content {
|
||||
height: 52vh;
|
||||
overflow-y: auto;
|
||||
margin: 30rpx 0;
|
||||
}
|
||||
|
||||
.agree-box {
|
||||
margin: 60rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.box-state {
|
||||
padding: 16rpx 32rpx 0;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
.pkg-product-list {
|
||||
image {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.swiper-box {
|
||||
width: 686rpx;
|
||||
height: 360rpx;
|
||||
margin: 0 auto;
|
||||
border-radius: 16rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 686rpx;
|
||||
height: 60rpx;
|
||||
margin: 0 auto;
|
||||
padding: 0 24rpx 0 32rpx;
|
||||
box-sizing: border-box;
|
||||
background: #F7F7F7;
|
||||
border-radius: 30rpx;
|
||||
|
||||
input {
|
||||
width: 560rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.body-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
border-top: 2rpx solid #D2D2D2;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0 !important
|
||||
}
|
||||
|
||||
.aside-box {
|
||||
position: relative;
|
||||
left: 0;
|
||||
width: 194rpx;
|
||||
box-sizing: border-box;
|
||||
border-right: 2rpx solid #D2D2D2;
|
||||
overflow-y: auto;
|
||||
|
||||
.item {
|
||||
height: 94rpx;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 2rpx solid #D2D2D2;
|
||||
color: #033333;
|
||||
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border-left: 6rpx solid #FD574B;
|
||||
color: #FD574B;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.article-box {
|
||||
position: relative;
|
||||
width: calc(100vw - 194rpx);
|
||||
overflow-y: auto;
|
||||
|
||||
.empty {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
|
||||
image {
|
||||
width: 280rpx;
|
||||
height: 280rpx;
|
||||
}
|
||||
|
||||
view {
|
||||
color: gray;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
width: 242rpx;
|
||||
height: 376rpx;
|
||||
box-sizing: border-box;
|
||||
margin: 20rpx 0 0 20rpx;
|
||||
|
||||
.cover {
|
||||
width: 242rpx;
|
||||
height: 242rpx;
|
||||
margin-bottom: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.price {
|
||||
position: absolute;
|
||||
bottom: 4rpx;
|
||||
color: #DA0000;
|
||||
font-size: 28rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
.item-store{
|
||||
height: 336rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
input{line-height: normal; box-sizing:border-box;}
|
||||
@font-face {
|
||||
font-family: 'GuildfordProBook 5';
|
||||
src: url('https://h5.yixiang.co/static/iconfont/GuildfordProBook5.otf');
|
||||
}
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
.iconfont{
|
||||
font-size: .36*100rpx;
|
||||
}
|
||||
/* 一像素边框 */
|
||||
@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5) {
|
||||
.border-1px::after {
|
||||
transform: scaleY(0.7);
|
||||
}
|
||||
.border-1px::before {
|
||||
transform: scaleY(0.7);
|
||||
}
|
||||
}
|
||||
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
|
||||
.border-1px::after {
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
.border-1px::before {
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
}
|
||||
@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
|
||||
.border-1px::after {
|
||||
transform: scaleY(0.33);
|
||||
}
|
||||
.border-1px::before {
|
||||
transform: scaleY(0.33);
|
||||
}
|
||||
}
|
||||
.line1{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width: 100%;}
|
||||
.line2{word-break:break-all;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
|
||||
.mask{position:fixed;top:0;left:0;right:0;bottom:0;z-index:55;background-color:rgba(0,0,0,0.5);}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
.fixed-share {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 200rpx;
|
||||
width: 184rpx;
|
||||
height: 62rpx;
|
||||
vertical-align: middle;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.box-share {
|
||||
.box-img {
|
||||
position: relative;
|
||||
width: 500rpx;
|
||||
height: 632rpx;
|
||||
border-radius: 40rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 22rpx;
|
||||
right: 22rpx;
|
||||
width: 32.5rpx;
|
||||
height: 32.5rpx;
|
||||
}
|
||||
|
||||
.main-img {
|
||||
width: 500rpx;
|
||||
height: 632rpx;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 500rpx;
|
||||
height: 100rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,54 @@
|
||||
export default {
|
||||
// 通用导航
|
||||
commonJump: (uniapp_url) => {
|
||||
let query = uniapp_url.indexOf("?name=");
|
||||
if (query > -1) {
|
||||
let start = query + 6;
|
||||
let name = uniapp_url.substring(start)
|
||||
uni.setStorageSync("name", name)
|
||||
uni.switchTab({
|
||||
url: uniapp_url
|
||||
})
|
||||
}
|
||||
|
||||
switch (uniapp_url) {
|
||||
case "":
|
||||
uni.showToast({
|
||||
title: "开发中,敬请期待",
|
||||
icon: 'none'
|
||||
})
|
||||
break;
|
||||
case "/pages/shop/GoodsClass/index":
|
||||
uni.switchTab({
|
||||
url: uniapp_url
|
||||
})
|
||||
break;
|
||||
case "/pages/cloud/haveFun":
|
||||
uni.switchTab({
|
||||
url: uniapp_url
|
||||
})
|
||||
break;
|
||||
default:
|
||||
uni.navigateTo({
|
||||
url: uniapp_url
|
||||
})
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
navToGoods: (id) => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/shop/GoodsCon/index?id=" + id
|
||||
})
|
||||
},
|
||||
|
||||
diffSSS: (date) => {
|
||||
if (typeof date !== "string") return 0;
|
||||
date = date.substring(0, 19);
|
||||
date = date.replace(/-/g, '/');
|
||||
let timestamp = new Date(date).getTime();
|
||||
let now = new Date().getTime();
|
||||
return timestamp - now;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="address-window" :class="value === true ? 'on' : ''">
|
||||
<view class="title">
|
||||
选择地址
|
||||
<text class="iconfont icon-guanbi" @click="closeAddress"></text>
|
||||
</view>
|
||||
<view class="list" v-if="addressList.length">
|
||||
<view
|
||||
class="item acea-row row-between-wrapper"
|
||||
:class="item.id === checked ? 'font-color-red' : ''"
|
||||
v-for="(item, addressIndex) in addressList"
|
||||
@click="tapAddress(addressIndex)"
|
||||
:key="addressIndex"
|
||||
>
|
||||
<text class="iconfont icon-ditu" :class="item.id === checked ? 'font-color-red' : ''"></text>
|
||||
<view class="addressTxt">
|
||||
<view class="name" :class="item.id === checked ? 'font-color-red' : ''">
|
||||
{{ item.realName }}
|
||||
<text class="phone">{{ item.phone }}</text>
|
||||
</view>
|
||||
<view class="line1">
|
||||
{{ item.province }}{{ item.city }}{{ item.district
|
||||
}}{{ item.detail }}
|
||||
</view>
|
||||
</view>
|
||||
<text class="iconfont icon-complete" :class="item.id === checked ? 'font-color-red' : ''"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pictrue" v-if="addressList.length < 1">
|
||||
<image src="http://admin-api.xdd618.com/file/pic/20210203154438492268.png" class="image" />
|
||||
</view>
|
||||
<view class="addressBnt bg-color-red" @click="goAddressPages">新加地址</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent :hidden="value === false" @click="closeAddress"></view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { getAddressList } from "@/api/user";
|
||||
|
||||
export default {
|
||||
name: "AddressWindow",
|
||||
props: {
|
||||
value: Boolean,
|
||||
checked: Number
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
addressList: [],
|
||||
current: 0,
|
||||
cartId: 0,
|
||||
pinkId: 0,
|
||||
couponId: 0
|
||||
};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
getAddressList: function() {
|
||||
let that = this;
|
||||
getAddressList().then(res => {
|
||||
that.addressList = res.data;
|
||||
});
|
||||
},
|
||||
closeAddress() {
|
||||
this.$emit("input", false);
|
||||
},
|
||||
goAddressPages: function() {
|
||||
this.$yrouter.push({ path: "/pages/user/address/AddAddress/index" });
|
||||
this.$emit("redirect");
|
||||
},
|
||||
tapAddress: function(index) {
|
||||
this.$emit("checked", this.addressList[index]);
|
||||
this.$emit("input", false);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<view @tap.stop="open">
|
||||
<text class="uni-input">{{ value }}</text>
|
||||
<uni-popup ref="popup" type="bottom" safe-area>
|
||||
<view class="cityselect">
|
||||
<view class="cityselect-header">
|
||||
<view class="cityselect-title">
|
||||
<text>请选择地址</text>
|
||||
</view>
|
||||
<view class="cityselect-nav">
|
||||
<view class="item" v-if="provinceActive" @tap="changeNav(0)">
|
||||
<text>{{ provinceActive.n }}</text>
|
||||
</view>
|
||||
<view class="item" v-if="cityActive" @tap="changeNav(1)">
|
||||
<text>{{ cityActive.n }}</text>
|
||||
</view>
|
||||
<view class="item" v-if="districtActive" @tap="changeNav(2)">
|
||||
<text>{{ districtActive.n }}</text>
|
||||
</view>
|
||||
<view class="item active" v-else>
|
||||
<text>请选择</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cityselect-content">
|
||||
<swiper class="swiper" disable-touch="true" touchable="false" :current="current">
|
||||
<swiper-item>
|
||||
<scroll-view scroll-y class="cityScroll">
|
||||
<view>
|
||||
<view
|
||||
class="cityselect-item"
|
||||
v-for="(item,index) in province"
|
||||
:key="index"
|
||||
@tap="selectProvince(index)"
|
||||
>
|
||||
<view class="cityselect-item-box">
|
||||
<text>{{ item.n }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
<swiper-item>
|
||||
<scroll-view scroll-y class="cityScroll">
|
||||
<view>
|
||||
<view
|
||||
class="cityselect-item"
|
||||
v-for="(item,index) in city"
|
||||
:key="index"
|
||||
@tap="selectCity(index)"
|
||||
>
|
||||
<view class="cityselect-item-box">
|
||||
<text>{{ item.n }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
<swiper-item>
|
||||
<scroll-view scroll-y class="cityScroll">
|
||||
<view>
|
||||
<view
|
||||
class="cityselect-item"
|
||||
v-for="(item,index) in district"
|
||||
:key="index"
|
||||
@tap="selectDistrict(index)"
|
||||
>
|
||||
<view class="cityselect-item-box">
|
||||
<text>{{ item.n }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script type="text/babel">
|
||||
import uniPopup from "./uni-popup/uni-popup.vue";
|
||||
import uniPopupMessage from "./uni-popup/uni-popup-message.vue";
|
||||
import uniPopupDialog from "./uni-popup/uni-popup-dialog.vue";
|
||||
|
||||
export default {
|
||||
name: "CitySelect",
|
||||
components: {
|
||||
uniPopup,
|
||||
uniPopupMessage,
|
||||
uniPopupDialog
|
||||
},
|
||||
props: ["callback", "items", "defaultValue"],
|
||||
data() {
|
||||
return {
|
||||
value: "请选择",
|
||||
show: this.value,
|
||||
province: [],
|
||||
provinceActive: null,
|
||||
city: [],
|
||||
cityActive: null,
|
||||
district: [],
|
||||
districtActive: null,
|
||||
current: 0
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
items(nextItem) {
|
||||
this.province = nextItem;
|
||||
},
|
||||
defaultValue(newValue) {
|
||||
this.value = newValue;
|
||||
// this.adjustDefaultValue();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.defaultValue) {
|
||||
this.value = this.defaultValue;
|
||||
}
|
||||
this.province = this.items;
|
||||
},
|
||||
methods: {
|
||||
adjustDefaultValue() {
|
||||
var newValue = this.value;
|
||||
if (newValue != undefined && newValue.length > 0) {
|
||||
var arr = newValue.split(' ');
|
||||
|
||||
var pIdx = this.getIndex(arr[0], this.province);
|
||||
if (pIdx > -1) {
|
||||
this.selectProvince(pIdx);
|
||||
var cIdx = this.getIndex(arr[1], this.city);
|
||||
if (cIdx > 0) {
|
||||
this.selectCity(cIdx);
|
||||
var aIdx = this.getIndex(arr[2], this.district);
|
||||
if (aIdx > -1) {
|
||||
this.selectDistrict(aIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getIndex(value, arr) {
|
||||
var idx = -1;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i].n == value) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
},
|
||||
open() {
|
||||
if (this.$refs.popup.showPopup) {
|
||||
this.$refs.popup.close();
|
||||
return;
|
||||
}
|
||||
this.province = this.items;
|
||||
this.provinceActive = null;
|
||||
this.cityActive = null;
|
||||
this.districtActive = null;
|
||||
this.city = [];
|
||||
this.district = [];
|
||||
this.current = 0;
|
||||
this.$refs.popup.open();
|
||||
},
|
||||
changeNav(index) {
|
||||
if (index == 0) {
|
||||
this.provinceActive = null;
|
||||
}
|
||||
if (index == 1) {
|
||||
this.cityActive = null;
|
||||
}
|
||||
if (index == 2) {
|
||||
this.districtActive = null;
|
||||
}
|
||||
this.current = index;
|
||||
},
|
||||
selectProvince(index) {
|
||||
this.provinceActive = this.province[index];
|
||||
this.city = this.province[index].c;
|
||||
this.current = 1;
|
||||
},
|
||||
selectCity(index) {
|
||||
this.cityActive = this.city[index];
|
||||
this.district = this.city[index].c;
|
||||
this.current = 2;
|
||||
},
|
||||
selectDistrict(index) {
|
||||
this.districtActive = this.district[index];
|
||||
this.value = `${this.provinceActive.n} ${this.cityActive.n} ${this.districtActive.n}`;
|
||||
// this.callback({
|
||||
// province: {
|
||||
// id: this.provinceActive.v,
|
||||
// name: this.provinceActive.n
|
||||
// },
|
||||
// city: {
|
||||
// id: this.cityActive.v,
|
||||
// name: this.cityActive.n
|
||||
// },
|
||||
// district: {
|
||||
// id: this.districtActive.v,
|
||||
// name: this.districtActive.n
|
||||
// }
|
||||
// });
|
||||
this.$emit("callback", {
|
||||
province: {
|
||||
id: this.provinceActive.v,
|
||||
name: this.provinceActive.n
|
||||
},
|
||||
city: {
|
||||
id: this.cityActive.v,
|
||||
name: this.cityActive.n
|
||||
},
|
||||
district: {
|
||||
id: this.districtActive.v,
|
||||
name: this.districtActive.n
|
||||
}
|
||||
});
|
||||
this.$refs.popup.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.cityselect {
|
||||
width: 100%;
|
||||
height: 75%;
|
||||
background-color: #fff;
|
||||
z-index: 1502;
|
||||
position: relative;
|
||||
height: 800rpx;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.cityScroll {
|
||||
height: 100%;
|
||||
height: calc(800rpx - 95rpx - 90rpx);
|
||||
}
|
||||
|
||||
.swiper {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.cityselect-header {
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cityselect-title {
|
||||
width: 100%;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
height: 95rpx;
|
||||
line-height: 95rpx;
|
||||
position: relative;
|
||||
|
||||
&:cityselect-title:after {
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 100%;
|
||||
background-image: linear-gradient(0deg, #ececec 50%, transparent 0);
|
||||
}
|
||||
}
|
||||
|
||||
.cityselect-nav {
|
||||
width: 100%;
|
||||
padding-left: 20rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.item {
|
||||
font-size: 26rpx;
|
||||
color: #222;
|
||||
display: block;
|
||||
height: 80rpx;
|
||||
line-height: 92rpx;
|
||||
padding: 0 16rpx;
|
||||
position: relative;
|
||||
margin-right: 30rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 40%;
|
||||
|
||||
&.active {
|
||||
color: #f23030 !important;
|
||||
border-bottom: 1rpx solid #f23030;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cityselect-content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cityselect-item {
|
||||
.cityselect-item-box {
|
||||
display: block;
|
||||
padding: 0 40rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 64rpx;
|
||||
max-height: 65rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
height: 1rpx;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-image: linear-gradient(0deg, #ececec 50%, transparent 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<view class="time">
|
||||
<text v-if="rtipText">{{ rtipText }}</text>
|
||||
<text class="styleAll" v-if="risDay === true">{{ day }}</text>
|
||||
<text class="timeTxt" v-if="rdayText">{{ rdayText }}</text>
|
||||
<text class="styleAll">{{ hour }}</text>
|
||||
<text class="timeTxt" v-if="rhourText">{{ rhourText }}</text>
|
||||
<text class="styleAll">{{ minute }}</text>
|
||||
<text class="timeTxt" v-if="rminuteText">{{ rminuteText }}</text>
|
||||
<text class="styleAll" v-if="isSecond">{{ second }}</text>
|
||||
<text class="timeTxt" v-if="rsecondText">{{ rsecondText }}</text>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "CountDown",
|
||||
props: [
|
||||
//距离开始提示文字
|
||||
'tipText',
|
||||
'dayText',
|
||||
'hourText',
|
||||
'minuteText',
|
||||
'secondText',
|
||||
'datatime',
|
||||
'isDay',
|
||||
'isSecond'
|
||||
],
|
||||
data: function() {
|
||||
return {
|
||||
day: "00",
|
||||
hour: "00",
|
||||
minute: "00",
|
||||
second: "00",
|
||||
rtipText: "倒计时",
|
||||
rdayText: "天",
|
||||
rhourText: "时",
|
||||
rminuteText: "分",
|
||||
rsecondText: "秒",
|
||||
rdatatime: 0,
|
||||
risDay: true,
|
||||
risSecond: true,
|
||||
};
|
||||
},
|
||||
mounted: function() {
|
||||
this.rtipText = this.$props.tipText;
|
||||
this.rdayText = this.$props.dayText;
|
||||
this.rhourText = this.$props.hourText;
|
||||
this.rminuteText = this.$props.minuteText;
|
||||
this.rsecondText = this.$props.secondText;
|
||||
this.rdatatime = this.$props.datatime;
|
||||
this.risDay = this.$props.isDay;
|
||||
this.risSecond = this.$props.isSecond;
|
||||
this.show_time();
|
||||
},
|
||||
methods: {
|
||||
show_time: function() {
|
||||
this.runTime();
|
||||
setInterval(this.runTime, 1000);
|
||||
},
|
||||
runTime() {
|
||||
let that = this;
|
||||
//时间函数
|
||||
let intDiff = that.datatime - Date.parse(new Date()) / 1000; //获取数据中的时间戳的时间差;
|
||||
let day = 0,
|
||||
hour = 0,
|
||||
minute = 0,
|
||||
second = 0;
|
||||
if (intDiff > 0) {
|
||||
//转换时间
|
||||
if (that.isDay === true) {
|
||||
day = Math.floor(intDiff / (60 * 60 * 24));
|
||||
} else {
|
||||
day = 0;
|
||||
}
|
||||
hour = Math.floor(intDiff / (60 * 60)) - day * 24;
|
||||
minute = Math.floor(intDiff / 60) - day * 24 * 60 - hour * 60;
|
||||
second =
|
||||
Math.floor(intDiff) -
|
||||
day * 24 * 60 * 60 -
|
||||
hour * 60 * 60 -
|
||||
minute * 60;
|
||||
if (hour <= 9) hour = "0" + hour;
|
||||
if (minute <= 9) minute = "0" + minute;
|
||||
if (second <= 9) second = "0" + second;
|
||||
that.day = day;
|
||||
that.hour = hour;
|
||||
that.minute = minute;
|
||||
that.second = second;
|
||||
} else {
|
||||
that.day = "00";
|
||||
that.hour = "00";
|
||||
that.minute = "00";
|
||||
that.second = "00";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="coupon-list-window" :class="value === true ? 'on' : ''">
|
||||
<view class="title">
|
||||
优惠券
|
||||
<text class="iconfont icon-guanbi" @click="close"></text>
|
||||
</view>
|
||||
<view v-if="couponList.length > 0">
|
||||
<view class="coupon-list">
|
||||
<div
|
||||
class="item acea-row row-center-wrapper"
|
||||
v-for="coupon in couponList"
|
||||
:key="coupon.id"
|
||||
@click="click(coupon)"
|
||||
>
|
||||
<div class="money">
|
||||
<div>
|
||||
¥<span class="num">{{ coupon.couponPrice }}</span>
|
||||
</div>
|
||||
<div class="pic-num">满{{ coupon.useMinPrice }}元可用</div>
|
||||
</div>
|
||||
<div class="text">
|
||||
<div class="condition line1">{{ coupon.couponTitle }}</div>
|
||||
<div class="data acea-row row-between-wrapper">
|
||||
<div v-if="coupon.endTime === 0">不限时</div>
|
||||
<div v-else>截止:{{ coupon.endTime }}</div>
|
||||
<div
|
||||
class="iconfont icon-xuanzhong1 font-color-red"
|
||||
v-if="checked === coupon.id"
|
||||
></div>
|
||||
<div class="iconfont icon-weixuanzhong" v-else></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</view>
|
||||
<view class="couponNo bg-color-red" @click="couponNo">不使用优惠券</view>
|
||||
</view>
|
||||
<view v-if="!couponList.length && loaded">
|
||||
<view class="pictrue">
|
||||
<image src="http://admin-api.xdd618.com/file/pic/20210203154207179472.png" class="image" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent :hidden="value === false" @click="close"></view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped lang="less">
|
||||
.coupon-list-window .iconfont {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
.couponNo {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
width: 690rpx;
|
||||
height: 86rpx;
|
||||
border-radius: 43rpx;
|
||||
text-align: center;
|
||||
line-height: 86rpx;
|
||||
margin: 60rpx auto;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { getOrderCoupon } from "@/api/order";
|
||||
import DataFormatT from "@/components/DataFormatT";
|
||||
|
||||
export default {
|
||||
name: "CouponListWindow",
|
||||
components: {
|
||||
DataFormatT
|
||||
},
|
||||
props: {
|
||||
value: Boolean,
|
||||
checked: Number,
|
||||
price: {
|
||||
type: [Number, String],
|
||||
default: undefined
|
||||
},
|
||||
cartid: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
couponList: [],
|
||||
loaded: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
price(n) {
|
||||
if (n === undefined || n == null) return;
|
||||
this.getCoupon();
|
||||
},
|
||||
cartid(n) {
|
||||
if (n === undefined || n == null) return;
|
||||
this.getCoupon();
|
||||
}
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
close: function() {
|
||||
this.$emit("input", false);
|
||||
this.$emit("close");
|
||||
},
|
||||
getCoupon() {
|
||||
getOrderCoupon(this.cartid).then(res => {
|
||||
this.couponList = res.data;
|
||||
this.loaded = true;
|
||||
});
|
||||
},
|
||||
click(coupon) {
|
||||
this.$emit("checked", coupon);
|
||||
this.$emit("input", false);
|
||||
},
|
||||
couponNo: function() {
|
||||
this.$emit("checked", null);
|
||||
this.$emit("input", false);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="coupon-list-window" :class="coupon.coupon === true ? 'on' : ''">
|
||||
<view class="title">
|
||||
优惠券
|
||||
<text class="iconfont icon-guanbi" @click="close"></text>
|
||||
</view>
|
||||
<view class="coupon-list" v-if="coupon.list.length > 0">
|
||||
<view
|
||||
class="item acea-row row-center-wrapper"
|
||||
v-for="(item, couponpopIndex) in coupon.list"
|
||||
:key="couponpopIndex"
|
||||
@click="getCouponUser(couponpopIndex, item.id)"
|
||||
>
|
||||
<view class="money">
|
||||
¥
|
||||
<text class="num">{{ item.coupon_price }}</text>
|
||||
</view>
|
||||
<view class="text">
|
||||
<view class="condition line1">购物满{{ item.use_min_price }}元可用</view>
|
||||
<view class="data acea-row row-between-wrapper">
|
||||
<view v-if="item.end_time === 0">不限时</view>
|
||||
<view v-else>{{ item.start_time }}-{{ item.end_time }}</view>
|
||||
<view
|
||||
class="bnt acea-row row-center-wrapper"
|
||||
:class="!item.is_use ? 'bg-color-red' : 'gray'"
|
||||
>{{ !item.is_use ? "立即领取" : "已领取" }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!--无优惠券-->
|
||||
<view class="pictrue" v-else>
|
||||
<image src="http://admin-api.xdd618.com/file/pic/20210203154207179472.png" class="image" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent :hidden="coupon.coupon === false" @click="close"></view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { getCouponReceive } from "@/api/user";
|
||||
export default {
|
||||
name: "CouponPop",
|
||||
props: {
|
||||
coupon: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
close: function() {
|
||||
this.$emit("changeFun", { action: "changecoupon", value: false }); //$emit():注册事件;
|
||||
},
|
||||
getCouponUser: function(index, id) {
|
||||
let that = this,
|
||||
list = that.coupon.list;
|
||||
if (list[index].is_use === true) return;
|
||||
getCouponReceive(id).then(function() {
|
||||
uni.showToast({
|
||||
title: "已领取",
|
||||
icon: "none",
|
||||
duration: 2000
|
||||
});
|
||||
that.$set(list[index], "is_use", true);
|
||||
that.$emit("changefun", { action: "currentcoupon", value: index });
|
||||
that.$emit("changeFun", { action: "changecoupon", value: false });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<view v-if="couponList.length > 0">
|
||||
<view class="coupon-window" :class="value ? 'on' : ''">
|
||||
<view class="couponWinList">
|
||||
<view class="item acea-row row-between-wrapper" v-for="(item, couponwindiwIndex) in couponList" :key="couponwindiwIndex">
|
||||
<view class="money font-color-red">
|
||||
¥<text class="num">{{ item.coupon_price }}</text>
|
||||
</view>
|
||||
<view class="text">
|
||||
<view class="name">
|
||||
购物买{{ item.use_min_price }}减{{ item.coupon_price }}
|
||||
</view>
|
||||
<view v-if="item.end_time">
|
||||
{{ item.start_time }}-{{ item.end_time }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height:120rpx"></view>
|
||||
</view>
|
||||
<view class="lid">
|
||||
<view class="bnt font-color-red" @click="checked">立即领取</view>
|
||||
<view class="iconfont icon-guanbi3" @click="close"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent :hidden="!value"></view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
mapGetters
|
||||
} from "vuex";
|
||||
import {handleLoginFailure} from "@/utils";
|
||||
import {
|
||||
couponReceiveBatch
|
||||
} from "@/api/user";
|
||||
|
||||
export default {
|
||||
name: "CouponWindow",
|
||||
props: {
|
||||
couponList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
computed: mapGetters(["isLogin"]),
|
||||
data: function() {
|
||||
return {
|
||||
value: true
|
||||
};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
checked() {
|
||||
const isLogin = this.isLogin;
|
||||
if (!isLogin) return handleLoginFailure();
|
||||
|
||||
const ids = this.couponList.reduce((initial, coupon) => {
|
||||
initial.push(coupon.id);
|
||||
return initial;
|
||||
}, []);
|
||||
couponReceiveBatch(ids)
|
||||
.then(() => {
|
||||
this.$emit("success");
|
||||
uni.showToast({
|
||||
title: '领取成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
uni.showToast({
|
||||
title: '已领取',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
});
|
||||
if (isLogin) {
|
||||
this.value = false;
|
||||
this.$emit("checked");
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
this.value = false;
|
||||
this.$emit("close");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<text>{{time}}</text>
|
||||
</template>
|
||||
<script>
|
||||
import { dataFormat } from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "DataFormat",
|
||||
props: ["date"],
|
||||
data: function() {
|
||||
return {
|
||||
time: ""
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.time = dataFormat(this.date);
|
||||
},
|
||||
watch: {
|
||||
"$props.date"(props) {
|
||||
this.time = dataFormat(this.date);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<text>{{time}}</text>
|
||||
</template>
|
||||
<script>
|
||||
import { dateFormatT } from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "DataFormatT",
|
||||
props: ["date"],
|
||||
data: function() {
|
||||
return {
|
||||
time: ""
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.time = dateFormatT(this.date);
|
||||
},
|
||||
watch: {
|
||||
"$props.date"(props) {
|
||||
this.time = dateFormatT(this.date);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<view class="goodList">
|
||||
<view @click="routerGo(item)" class="item acea-row row-between-wrapper" v-for="(item, goodlistIndex) in goodList" :key="goodlistIndex">
|
||||
<view class="pictrue">
|
||||
<image :src="item.image" class="image" />
|
||||
</view>
|
||||
<view class="underline">
|
||||
<view class="text">
|
||||
<view class="line1">{{ item.storeName }}</view>
|
||||
<view class="money font-color-red">
|
||||
¥
|
||||
<!-- <text class="num">{{ force2Decimal(item.price) }}</text> -->
|
||||
<text
|
||||
class="num"
|
||||
v-if="item.vipPrice && item.vipPrice > 0"
|
||||
>{{force2Decimal(item.vipPrice)}}</text>
|
||||
<text class="num" v-else>{{ force2Decimal(item.price) }}</text>
|
||||
</view>
|
||||
<view class="vip-money acea-row row-middle">
|
||||
<view class="vip">¥{{ item.otPrice?force2Decimal(item.otPrice) : 0.00 }}</view>
|
||||
<text class="num">已售{{ item.sales }}{{ item.unitName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="iconfont icon-gouwuche cart-color acea-row row-center-wrapper"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "GoodList",
|
||||
props: {
|
||||
goodList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
isSort: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
methods:{
|
||||
force2Decimal(value){
|
||||
return this.$force2Decimal(value);
|
||||
},
|
||||
routerGo(item) {
|
||||
this.$yrouter.push({ path: '/pages/shop/GoodsCon/index', query: { id: item.id } });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<view
|
||||
class="Loads acea-row row-center-wrapper"
|
||||
v-if="loading || !loaded"
|
||||
style="margin-top: 20rpx;"
|
||||
>
|
||||
<template v-if="loading">
|
||||
<view
|
||||
class="iconfont icon-jiazai loading acea-row row-center-wrapper"
|
||||
></view>
|
||||
正在加载中
|
||||
</template>
|
||||
<template v-else>
|
||||
上拉加载更多
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Loading",
|
||||
props: {
|
||||
loaded: Boolean,
|
||||
loading: Boolean
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<view class="orderGoods">
|
||||
<view v-if="title.length>0" class="total acea-row row-middle" >
|
||||
<view class="acea-row row-left row-middle" style="position: relative;">
|
||||
<image style="width: 32rpx;height: 32rpx;margin-right: 12rpx;" src="http://admin-api.xdd618.com/file/pic/20210807230759738342.png" mode=""></image>
|
||||
<text class="title">{{title}}</text>
|
||||
<!-- <text class="pink-dot"></text> -->
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="total">共{{ cartInfo.length }}件商品</view>
|
||||
<view class="goodWrapper">
|
||||
<view class="item acea-row row-between-wrapper" style="flex-wrap: nowrap;" v-for="cart in cartInfo" :key="cart.id">
|
||||
<view class="pictrue" style="margin-right: 20rpx;">
|
||||
<image :src="cart.productInfo.image" class="image" />
|
||||
</view>
|
||||
<view class="text">
|
||||
<view class="acea-row row-between-wrapper">
|
||||
<view class="name line1">{{ cart.productInfo.storeName }}</view>
|
||||
<!-- <view class="num">x {{ cart.cartNum }}</view> -->
|
||||
</view>
|
||||
<!-- <view
|
||||
class="attr line1"
|
||||
v-if="cart.productInfo.attrInfo"
|
||||
>{{ cart.productInfo.attrInfo.sku }}</view> -->
|
||||
<view class="acea-row row-middle row-between">
|
||||
<view
|
||||
class="attr"
|
||||
style="height: 38rpx;line-height:38rpx;background-color: #FFF3F2;border-radius: 19rpx;padding: 4rpx 20rpx;"
|
||||
v-if="cart.productInfo.attrInfo"
|
||||
>属性:{{ cart.productInfo.attrInfo.sku }}</view>
|
||||
<view v-if="evaluate == 3" class="evaluate" style="bottom: 0 !important;position: relative;height: 36rpx !important;width: 90rpx !important;line-height: 36rpx !important;font-size: 32rpx;border-radius: 18rpx;" @click="routerGo(cart)">评价</view>
|
||||
</view>
|
||||
<view class="money font-color-lightred acea-row row-between">
|
||||
<text>¥{{ cart.truePrice }}</text>
|
||||
<text class="num" style="margin-left: 10rpx;">x{{ cart.cartNum }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "OrderGoods",
|
||||
props: {
|
||||
evaluate: Number,
|
||||
cartInfo: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
title:{
|
||||
type: String,
|
||||
default:''
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
routerGo(cart) {
|
||||
this.$yrouter.push({
|
||||
path: "/pages/shop/GoodsEvaluate/index",
|
||||
query: { id: cart.unique }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.pink-dot{
|
||||
width:24rpx;
|
||||
height:24rpx;
|
||||
background:rgba(255,86,74,0.4);
|
||||
border-radius:50%;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
bottom: 24rpx;
|
||||
right: -12rpx;
|
||||
}
|
||||
|
||||
.title{
|
||||
font-size:26rpx;
|
||||
color:#080F1A;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="payment" :class="value === true ? 'on' : ''">
|
||||
<view class="title acea-row row-center-wrapper">
|
||||
选择付款方式<text class="iconfont icon-guanbi" @click="close"></text>
|
||||
</view>
|
||||
<view
|
||||
class="item acea-row row-between-wrapper"
|
||||
v-if="types.indexOf('weixin') !== -1"
|
||||
@click="checked('weixin')"
|
||||
>
|
||||
<view class="left acea-row row-between-wrapper">
|
||||
<view class="iconfont icon-weixinzhifu"></view>
|
||||
<view class="text">
|
||||
<view class="name">微信支付</view>
|
||||
<view class="info">使用微信快捷支付</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="iconfont icon-xiangyou"></view>
|
||||
</view>
|
||||
<view
|
||||
class="item acea-row row-between-wrapper"
|
||||
v-if="types.indexOf('yue') !== -1"
|
||||
@click="checked('yue')"
|
||||
>
|
||||
<view class="left acea-row row-between-wrapper">
|
||||
<view class="iconfont icon-yuezhifu"></view>
|
||||
<view class="text">
|
||||
<view class="name">余额支付</view>
|
||||
<view class="info">
|
||||
当前可用余额:<text class="money">{{ balance }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="iconfont icon-xiangyou"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mask" v-show="value" @click="close"></view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "Payment",
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
balance: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
types: {
|
||||
type: Array,
|
||||
default: () => ["weixin", "alipay", "yue", "offline"]
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
checked: function(type) {
|
||||
this.$emit("checked", type);
|
||||
this.close();
|
||||
},
|
||||
close: function() {
|
||||
this.$emit("input", false);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.payment {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-radius: 0.16*100rpx 0.16*100rpx 0 0;
|
||||
background-color: #fff;
|
||||
padding-bottom: 0.6*100rpx;
|
||||
z-index: 99;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
|
||||
-webkit-transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
|
||||
-moz-transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
|
||||
-o-transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
-moz-transform: translate3d(0, 100%, 0);
|
||||
-o-transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
.payment.on {
|
||||
transform: translate3d(0, 0, 0);
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
-moz-transform: translate3d(0, 0, 0);
|
||||
-o-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
.payment .title {
|
||||
text-align: center;
|
||||
height: 1.23*100rpx;
|
||||
font-size: 0.32*100rpx;
|
||||
color: #282828;
|
||||
font-weight: bold;
|
||||
padding-right: 0.3*100rpx;
|
||||
margin-left: 0.3*100rpx;
|
||||
position: relative;
|
||||
border-bottom: 0.01*100rpx solid #eee;
|
||||
}
|
||||
|
||||
.payment .title .iconfont {
|
||||
position: absolute;
|
||||
right: 0.3*100rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 0.43*100rpx;
|
||||
color: #8a8a8a;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.payment .item {
|
||||
border-bottom: 0.01*100rpx solid #eee;
|
||||
height: 1.3*100rpx;
|
||||
margin-left: 0.3*100rpx;
|
||||
padding-right: 0.3*100rpx;
|
||||
}
|
||||
|
||||
.payment .item .left {
|
||||
width: 6.1*100rpx;
|
||||
}
|
||||
|
||||
.payment .item .left .text {
|
||||
width: 5.4*100rpx;
|
||||
}
|
||||
|
||||
.payment .item .left .text .name {
|
||||
font-size: 0.32*100rpx;
|
||||
color: #282828;
|
||||
}
|
||||
|
||||
.payment .item .left .text .info {
|
||||
font-size: 0.24*100rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.payment .item .left .text .info .money {
|
||||
color: #ff9900;
|
||||
}
|
||||
|
||||
.payment .item .left .iconfont {
|
||||
font-size: 0.45*100rpx;
|
||||
color: #09bb07;
|
||||
}
|
||||
|
||||
.payment .item .left .iconfont.icon-zhifubao {
|
||||
color: #00aaea;
|
||||
}
|
||||
|
||||
.payment .item .left .iconfont.icon-yuezhifu {
|
||||
color: #ff9900;
|
||||
}
|
||||
|
||||
.payment .item .left .iconfont.icon-yuezhifu1 {
|
||||
color: #eb6623;
|
||||
}
|
||||
|
||||
.payment .item .iconfont {
|
||||
font-size: 0.3*100rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="priceChange" :class="change === true ? 'on' : ''">
|
||||
<view class="priceTitle">
|
||||
<text v-if="status==0">
|
||||
<text v-if="orderInfo.refundStatus == 1">立即退款</text>
|
||||
<text v-if="orderInfo.refundStatus != 1">一键改价</text>
|
||||
</text>
|
||||
<text v-if="status!=0">订单备注</text>
|
||||
<text class="iconfont icon-guanbi" @click="close"></text>
|
||||
</view>
|
||||
<view class="listChange" v-if="status == 0">
|
||||
<view class="item acea-row row-between-wrapper" v-if="orderInfo.refundStatus === 0">
|
||||
<view>商品总价(¥)</view>
|
||||
<view class="money">
|
||||
{{ orderInfo.totalPrice }}
|
||||
<text class="iconfont icon-suozi"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item acea-row row-between-wrapper" v-if="orderInfo.refundStatus === 0">
|
||||
<view>原始邮费(¥)</view>
|
||||
<view class="money">
|
||||
{{ orderInfo.payPostage }}
|
||||
<text class="iconfont icon-suozi"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item acea-row row-between-wrapper" v-if="orderInfo.refundStatus === 0">
|
||||
<view>实际支付(¥)</view>
|
||||
<view class="money">
|
||||
<input
|
||||
type="text"
|
||||
v-model="price"
|
||||
:class="focus === true ? 'on' : ''"
|
||||
@focus="priceChange"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item acea-row row-between-wrapper" v-if="orderInfo.refundStatus === 1">
|
||||
<view>实际支付(¥)</view>
|
||||
<view class="money">
|
||||
{{ orderInfo.payPrice }}
|
||||
<text class="iconfont icon-suozi"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item acea-row row-between-wrapper" v-if="orderInfo.refundStatus === 1">
|
||||
<view>退款金额(¥)</view>
|
||||
<view class="money">
|
||||
<input
|
||||
type="text"
|
||||
v-model="refund_price"
|
||||
:class="focus === true ? 'on' : ''"
|
||||
@focus="priceChange"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="listChange" v-else>
|
||||
<textarea
|
||||
:placeholder="'请填写备注信息...'"
|
||||
v-model="remark"
|
||||
></textarea>
|
||||
</view>
|
||||
<view class="modify" @click="save">{{ orderInfo.refundStatus === 0 ? "立即修改" : "确认退款" }}</view>
|
||||
<view class="modify1" @click="refuse" v-if="orderInfo.refundStatus === 1">拒绝退款</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent v-show="change === true"></view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped lang="less" >
|
||||
.priceChange .listChange textarea {
|
||||
border: 1px solid #eee;
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
margin-top: 50rpx;
|
||||
border-radius: 10rpx;
|
||||
color: #333;
|
||||
padding: 20rpx;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
name: "PriceChange",
|
||||
components: {},
|
||||
props: {
|
||||
change: Boolean,
|
||||
orderInfo: Object,
|
||||
status: String
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
focus: false,
|
||||
price: 0,
|
||||
refund_price: 0,
|
||||
remark: ""
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
orderInfo: function() {
|
||||
this.price = this.orderInfo.payPrice;
|
||||
this.refund_price = this.orderInfo.payPrice;
|
||||
this.remark = "";
|
||||
}
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
priceChange: function() {
|
||||
this.focus = true;
|
||||
},
|
||||
close: function() {
|
||||
this.price = this.orderInfo.payPrice;
|
||||
this.$emit("closechange", false);
|
||||
},
|
||||
save: function() {
|
||||
let that = this;
|
||||
that.$emit("savePrice", {
|
||||
price: that.price,
|
||||
refund_price: that.refund_price,
|
||||
type: 1,
|
||||
remark: that.remark
|
||||
});
|
||||
},
|
||||
refuse: function() {
|
||||
let that = this;
|
||||
that.$emit("savePrice", {
|
||||
price: that.price,
|
||||
refund_price: that.refund_price,
|
||||
type: 2,
|
||||
remark: that.remark
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<view class="slider-banner product-bg">
|
||||
<swiper class="swiper-wrapper" @change="handleChange" v-if="imgUrls.length > 0" :indicator-dots='true' indicator-color="rgba(255, 255, 255, .3)" indicator-active-color="#ffffff">
|
||||
<block v-for="(item, imgUrlsIndex) in imgUrls" :key="imgUrlsIndex">
|
||||
<swiper-item>
|
||||
<image v-if="!checkIfVideo(item)" :src="item" class="slide-image" />
|
||||
<video v-else class="slide-image" @play="storePlayObj" :show-fullscreen-btn="false" :src="item" :id="buildId(imgUrlsIndex)" autoplay controls></video>
|
||||
</swiper-item>
|
||||
</block>
|
||||
</swiper>
|
||||
<!-- <swiper class="swiper-wrapper" :options="ProductConSwiper" v-if="imgUrls.length > 0">
|
||||
<swiperSlide class="swiper-slide" v-for="item in imgUrls" :key="item" ref="goodSwiper">
|
||||
<image :src="item" class="slide-image" />
|
||||
</swiperSlide>
|
||||
</swiper>-->
|
||||
<!-- <view class="pages">{{ currents || 1 }}/{{ imgUrls.length || 1 }}</view> -->
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
// import { swiper, swiperSlide } from "vue-awesome-swiper";
|
||||
|
||||
export default {
|
||||
name: "ProductConSwiper",
|
||||
components: {
|
||||
// swiper,
|
||||
// swiperSlide
|
||||
},
|
||||
props: {
|
||||
imgUrls: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
let that = this;
|
||||
return {
|
||||
currents: 1,
|
||||
currentVideo:null,
|
||||
ProductConSwiper: {
|
||||
autoplay: {
|
||||
disableOnInteraction: false,
|
||||
delay: 2000
|
||||
},
|
||||
loop: true,
|
||||
speed: 1000,
|
||||
observer: true,
|
||||
observeParents: true,
|
||||
on: {
|
||||
slideChangeTransitionStart: function() {
|
||||
that.currents = this.realIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
buildId(idx){
|
||||
return 'myVideo'+idx;
|
||||
},
|
||||
checkIfVideo(url){
|
||||
if(url.indexOf('.') != -1){
|
||||
var ext = url.substring(url.lastIndexOf('.')+1);
|
||||
if (['mp4','webm','mpeg4','ogg'].indexOf(ext) != -1) {
|
||||
return true;
|
||||
} else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
storePlayObj(event){
|
||||
console.log(event);
|
||||
this.currentVideo = uni.createVideoContext(event.currentTarget.id,this);
|
||||
},
|
||||
handleChange(event) {
|
||||
this.currents = event.mp.detail.current + 1;
|
||||
|
||||
//切换时停止播放
|
||||
if (this.currentVideo) {
|
||||
this.currentVideo.pause();
|
||||
}
|
||||
// var itemName = 'myVideo'+event.mp.detail.current;
|
||||
// uni.createVideoContext(itemName,this).stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="product-window" :class="attr.cartAttr === true ? 'on' : ''">
|
||||
<view class="textpic acea-row row-between-wrapper">
|
||||
<view class="pictrue" @click="previewImg(attr.productSelect.image)">
|
||||
<image :src="attr.productSelect.image" class="image" />
|
||||
</view>
|
||||
<view class="text">
|
||||
<view class="line1">{{ attr.productSelect.store_name }}</view>
|
||||
<view class="money font-color-lightred">
|
||||
¥
|
||||
<text class="num">{{ attr.productSelect.price }}</text>
|
||||
<text class="stock">库存: {{ attr.productSelect.stock }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="iconfont icon-guanbi" @click="closeAttr"></view>
|
||||
</view>
|
||||
<view class="productWinList">
|
||||
<view class="item" v-for="(item, indexw) in attr.productAttr" :key="indexw">
|
||||
<view class="title">{{ item.attrName }} 438</view>
|
||||
<view class="listn acea-row row-middle">
|
||||
<view
|
||||
class="itemn"
|
||||
:class="item.index == indexn ? 'on' : ''"
|
||||
v-for="(itemn, indexn) in item.attrValue"
|
||||
@click="tapAttr(indexw, indexn)"
|
||||
:key="indexn"
|
||||
>{{ itemn.attr }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cart">
|
||||
<view class="title">数量</view>
|
||||
<view class="carnum acea-row row-left">
|
||||
<view class="item reduce" :class="cartNum <= 1 ? 'on' : ''" @click="CartNumDes">-</view>
|
||||
<view class="item num">{{ cartNum }}</view>
|
||||
<view
|
||||
class="item plus"
|
||||
:class="
|
||||
cartNum >= attr.productSelect.stock
|
||||
? 'on'
|
||||
: ''
|
||||
"
|
||||
@click="CartNumAdd"
|
||||
>+</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent :hidden="attr.cartAttr === false" @click="closeAttr"></view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "ProductWindow",
|
||||
props: {
|
||||
attr: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
cartNum: {
|
||||
type: Number,
|
||||
default: () => 1
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
mounted: function() {
|
||||
console.log(this);
|
||||
},
|
||||
methods: {
|
||||
previewImg:function(imgUrl){
|
||||
uni.previewImage({
|
||||
urls:[imgUrl]
|
||||
})
|
||||
},
|
||||
closeAttr: function() {
|
||||
this.$emit("changeFun", { action: "changeattr", value: false });
|
||||
},
|
||||
CartNumDes: function() {
|
||||
this.$emit("changeFun", { action: "ChangeCartNum", value: false });
|
||||
},
|
||||
CartNumAdd: function() {
|
||||
this.$emit("changeFun", { action: "ChangeCartNum", value: 1 });
|
||||
},
|
||||
tapAttr: function(indexw, indexn) {
|
||||
// 修改商品规格不生效的原因:
|
||||
// H5端下面写法,attr更新,但是除H5外其他端不支持,
|
||||
// 尽量避免下面的骚写法,不要在子组件内更新props
|
||||
// 这里修改是为了能获取到被选中的属性
|
||||
this.attr.productAttr[indexw].index = indexn;
|
||||
let that = this;
|
||||
let value = that
|
||||
.getCheckedValue()
|
||||
.sort()
|
||||
.join(",");
|
||||
that.$emit("changeFun", {
|
||||
action: "ChangeAttr",
|
||||
value: {
|
||||
value,
|
||||
indexw,
|
||||
indexn
|
||||
}
|
||||
});
|
||||
},
|
||||
//获取被选中属性;
|
||||
getCheckedValue: function() {
|
||||
let productAttr = this.attr.productAttr;
|
||||
let value = [];
|
||||
// console.log(productAttr)
|
||||
for (let i = 0; i < productAttr.length; i++) {
|
||||
for (let j = 0; j < productAttr[i].attrValueArr.length; j++) {
|
||||
if (productAttr[i].index === j) {
|
||||
value.push(productAttr[i].attrValueArr[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<view class="promotionGood" v-if="benefit.length > 0">
|
||||
<view class="item acea-row row-between-wrapper" v-for="(item, promotionGoodIndex) in benefit" :key="promotionGoodIndex" @click="routerGo(item)">
|
||||
<view class="pictrue"><image :src="item.image" class="image" /></view>
|
||||
<view class="text">
|
||||
<view class="name line1">{{ item.storeName }}</view>
|
||||
<view class="sp-money acea-row">
|
||||
<view class="moneyCon">
|
||||
促销价: ¥
|
||||
<text class="num">{{ item.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="acea-row row-between-wrapper">
|
||||
<view class="money">日常价:¥{{ item.otPrice }}</view>
|
||||
<view>仅剩:{{ item.stock }}{{ item.unitName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'PromotionGood',
|
||||
props: ['benefit'],
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
routerGo(item) {
|
||||
this.$yrouter.push({ path: '/pages/shop/GoodsCon/index', query: { id: item.id } });
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<view class="recommend" ref="container">
|
||||
<view class="title acea-row row-center-wrapper">
|
||||
<text class="iconfont icon-zhuangshixian"></text>
|
||||
<text class="name">为你推荐</text>
|
||||
<text class="iconfont icon-zhuangshixian lefticon"></text>
|
||||
</view>
|
||||
<view class="recommendList acea-row row-between-wrapper">
|
||||
<view
|
||||
@click="routerGo(item)"
|
||||
class="item"
|
||||
v-for="(item, recommendIndex) in hostProduct"
|
||||
:key="recommendIndex"
|
||||
>
|
||||
<view class="pictrue"><image :src="item.image" class="image" /></view>
|
||||
<view class="name line1">{{ item.storeName }}</view>
|
||||
<view class="money font-color-red">
|
||||
¥
|
||||
<text class="num" v-if="item.vipPrice && item.vipPrice>0">{{ force2Decimal(item.vipPrice) }}</text>
|
||||
<text class="num" v-else>{{ force2Decimal(item.price) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<Loading :loaded="loadend" :loading="loading"></Loading>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { getHostProducts } from '@/api/store';
|
||||
import Loading from '@/components/Loading';
|
||||
import { mapGetters } from "vuex";
|
||||
import NP from "number-precision";
|
||||
|
||||
export default {
|
||||
name: 'Recommend',
|
||||
props: {},
|
||||
components: {
|
||||
Loading
|
||||
},
|
||||
computed: mapGetters(["userInfo"]),
|
||||
data: function() {
|
||||
return {
|
||||
hostProduct: [],
|
||||
page: 1,
|
||||
limit: 20,
|
||||
loadTitle: '',
|
||||
loading: false,
|
||||
loadend: false
|
||||
};
|
||||
},
|
||||
mounted: function() {
|
||||
this.hostProducts();
|
||||
},
|
||||
methods: {
|
||||
force2Decimal(v){
|
||||
return this.$force2Decimal(v);
|
||||
},
|
||||
routerGo(item) {
|
||||
this.$yrouter.push({ path: '/pages/shop/GoodsCon/index', query: { id: item.id } });
|
||||
},
|
||||
hostProducts: function() {
|
||||
let that = this;
|
||||
if (that.loading) return; //阻止下次请求(false可以进行请求);
|
||||
if (that.loadend) return; //阻止结束当前请求(false可以进行请求);
|
||||
that.loading = true;
|
||||
getHostProducts(that.page, that.limit).then(res => {
|
||||
that.loading = false;
|
||||
//apply();js将一个数组插入另一个数组;
|
||||
//that.hostProduct.push.apply(that.hostProduct, res.data);
|
||||
|
||||
that.hostProduct.push.apply(that.hostProduct, res.data.map((item)=>{
|
||||
|
||||
//如果已经登录获取用户信息
|
||||
if(that.$store.getters.token && that.userInfo){
|
||||
var p = NP.times(item.price, NP.divide(parseFloat(that.userInfo.discount), 100))
|
||||
item.vipPrice = that.$force2Decimal(p);
|
||||
}
|
||||
return item;
|
||||
}));
|
||||
|
||||
that.loadend = res.data.length < that.limit; //判断所有数据是否加载完成;
|
||||
that.page = that.page + 1;
|
||||
});
|
||||
}
|
||||
},
|
||||
onReachBottom() {
|
||||
!this.loading && this.hostProducts();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<view v-if="shareInfoStatus" class="poster-first">
|
||||
<view class="mask-share">
|
||||
<image :src="webUrl+'/20230304092752891046.png'" @click="shareInfoClose"/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped lang="less">
|
||||
.poster-first {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.mask-share {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.mask-share image {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
name: "ShareInfo",
|
||||
props: {
|
||||
shareInfoStatus: Boolean
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
webUrl: this.$VUE_APP_RESOURCES_URL
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
},
|
||||
methods: {
|
||||
shareInfoClose: function () {
|
||||
this.$emit("setShareInfoStatus");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<view v-if="posterImageStatus" class="poster-first">
|
||||
<div class="posterCanvasWarp">
|
||||
<canvas class="posterCanvas" canvas-id="myCanvas"></canvas>
|
||||
</div>
|
||||
<view class="poster-pop" v-show="canvasStatus">
|
||||
<img
|
||||
src="@/static/images/poster-close.png"
|
||||
class="close"
|
||||
@click="posterImageClose"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<image
|
||||
:src="posterImage"
|
||||
alt="tp"
|
||||
class="poster-image"
|
||||
show-menu-by-longpress
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="keep">长按图片可以保存到手机</view>
|
||||
</view>
|
||||
<view class="mask"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import html2canvas from "html2canvas";
|
||||
import { PosterCanvas } from "@/utils";
|
||||
import { getProductPoster } from "@/api/store";
|
||||
|
||||
export default {
|
||||
name: "StorePoster",
|
||||
props: {
|
||||
posterImageStatus: Boolean,
|
||||
posterData: Object,
|
||||
goodId: String
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
canvasStatus: false,
|
||||
posterImage: ""
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
posterImageStatus: function() {
|
||||
var that = this;
|
||||
if (that.posterImageStatus === true) {
|
||||
that.$nextTick(function() {
|
||||
that.savePosterPath();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
posterImageClose: function() {
|
||||
this.posterImageStatus = false;
|
||||
this.canvasStatus = false;
|
||||
this.$emit("setPosterImageStatus");
|
||||
},
|
||||
savePosterPath: function() {
|
||||
const that = this;
|
||||
|
||||
uni.showLoading({ title: "海报生成中", mask: true });
|
||||
getProductPoster(this.goodId)
|
||||
.then(res => {
|
||||
this.canvasStatus = true;
|
||||
this.posterImage = res.data;
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less" lang="less">
|
||||
.poster-first {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
.poster-pop {
|
||||
width: 4.5 * 100rpx;
|
||||
height: 8 * 100rpx;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 99;
|
||||
top: 50%;
|
||||
margin-top: -4.6 * 100rpx;
|
||||
}
|
||||
.poster-pop .canvas {
|
||||
background-color: #ffffff;
|
||||
height: 8 * 100rpx;
|
||||
}
|
||||
.poster-pop .poster-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.poster-pop .canvas .image {
|
||||
width: 4.5 * 100rpx;
|
||||
height: 4.5 * 100rpx;
|
||||
display: block;
|
||||
}
|
||||
.poster-pop .canvas .text {
|
||||
text-align: center;
|
||||
color: #000000;
|
||||
margin-top: 0.32 * 100rpx;
|
||||
}
|
||||
.poster-pop .canvas .text.black {
|
||||
height: 0.68 * 100rpx;
|
||||
}
|
||||
.poster-pop .canvas .text.rad {
|
||||
color: #ff0000;
|
||||
}
|
||||
.poster-pop .canvas .code {
|
||||
height: 1.4 * 100rpx;
|
||||
display: flex;
|
||||
}
|
||||
.poster-pop .canvas .code .code-img {
|
||||
width: 33%;
|
||||
padding: 0.06 * 100rpx;
|
||||
}
|
||||
.poster-pop .canvas .code .code-img image {
|
||||
width: 100%;
|
||||
}
|
||||
.poster-pop .canvas .code .code-text {
|
||||
width: 60%;
|
||||
font-size: 0.12 * 100rpx;
|
||||
line-height: 1.64 * 100rpx;
|
||||
}
|
||||
.poster-pop .close {
|
||||
width: 0.46 * 100rpx;
|
||||
height: 0.75 * 100rpx;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: -0.73 * 100rpx;
|
||||
display: block;
|
||||
}
|
||||
.poster-pop .save-poster {
|
||||
background-color: #df2d0a;
|
||||
font-size: 0.22 * 100rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
height: 0.76 * 100rpx;
|
||||
line-height: 0.76 * 100rpx;
|
||||
width: 100%;
|
||||
margin-top: -0.04 * 100rpx;
|
||||
}
|
||||
.poster-pop .keep {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 0.25 * 100rpx;
|
||||
margin-top: 0.1 * 100rpx;
|
||||
}
|
||||
.mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
z-index: 9;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<view class="evaluateWtapper">
|
||||
<view
|
||||
class="evaluateItem"
|
||||
v-for="(item, evaluateWtapperIndex) in reply"
|
||||
:key="evaluateWtapperIndex"
|
||||
>
|
||||
<view class="pic-text acea-row row-middle">
|
||||
<view class="pictrue">
|
||||
<image :src="item.avatar" class="image" />
|
||||
</view>
|
||||
<view class="acea-row row-middle">
|
||||
<view class="name line1">{{ item.nickname }}</view>
|
||||
<view class="start" :class="'star' + item.star"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="time">{{ item.createTime }} {{ item.sku||'' }}</view>
|
||||
<view class="evaluate-infor">{{ item.comment }}</view>
|
||||
<view class="imgList acea-row">
|
||||
<view class="pictrue" v-for="(itemn, eq) in item.picturesArr" :key="eq">
|
||||
<image :src="itemn" class="image" @click="previewImage(item.picturesArr,eq)" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="reply" v-if="item.merchantReplyContent">
|
||||
<span class="font-color-red">店小二</span>
|
||||
:{{item.merchantReplyContent}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { dataFormat } from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "UserEvaluation",
|
||||
props: {
|
||||
reply: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
dataFormat,
|
||||
previewImage(imgs,index){
|
||||
uni.previewImage({
|
||||
current:imgs[index],
|
||||
urls:imgs
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<view v-show="iShidden === false">
|
||||
<view class="WriteOff">
|
||||
<view class="pictrue"><image :src="orderInfo.image" /></view>
|
||||
<view class="num acea-row row-center-wrapper">
|
||||
{{ orderInfo.order_id }}
|
||||
</view>
|
||||
<view class="tip">确定要核销此订单吗?</view>
|
||||
<view class="sure" @click="confirm">确定核销</view>
|
||||
<view class="sure cancel" @click="cancel">取消</view>
|
||||
</view>
|
||||
<view class="mask" @touchmove.prevent></view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped lang="less">
|
||||
.WriteOff {
|
||||
width: 5.6*100rpx;
|
||||
height: 8*100rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 0.2*100rpx;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -4*100rpx;
|
||||
margin-left: -2.8*100rpx;
|
||||
z-index: 99;
|
||||
padding-top: 0.55*100rpx;
|
||||
}
|
||||
.WriteOff .pictrue {
|
||||
width: 3.4*100rpx;
|
||||
height: 3.4*100rpx;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.WriteOff .pictrue image{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border-radius: 0.1*100rpx;
|
||||
}
|
||||
.WriteOff .num {
|
||||
font-size: 0.3*100rpx;
|
||||
color: #666;
|
||||
margin: 0.28*100rpx 0 0.3*100rpx 0;
|
||||
}
|
||||
.WriteOff .num .see {
|
||||
font-size: 0.16*100rpx;
|
||||
color: #fff;
|
||||
border-radius: 0.04*100rpx;
|
||||
background-color: #c68937;
|
||||
padding-left: 0.05*100rpx;
|
||||
margin-left: 0.12*100rpx;
|
||||
}
|
||||
.WriteOff .num .see .iconfont {
|
||||
font-size: 0.15*100rpx;
|
||||
}
|
||||
.WriteOff .tip {
|
||||
font-size: 0.36*100rpx;
|
||||
color: #282828;
|
||||
text-align: center;
|
||||
border-top: 1px dashed #ccc;
|
||||
padding-top: 0.4*100rpx;
|
||||
position: relative;
|
||||
}
|
||||
.WriteOff .tip:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0.25*100rpx;
|
||||
height: 0.25*100rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #7f7f7f;
|
||||
right: -0.125*100rpx;
|
||||
top: -0.125*100rpx;
|
||||
}
|
||||
.WriteOff .tip:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0.25*100rpx;
|
||||
height: 0.25*100rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #7f7f7f;
|
||||
left: -0.125*100rpx;
|
||||
top: -0.125*100rpx;
|
||||
}
|
||||
.WriteOff .sure {
|
||||
font-size: 0.32*100rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 0.82*100rpx;
|
||||
height: 0.82*100rpx;
|
||||
width: 4.6*100rpx;
|
||||
border-radius: 0.41*100rpx;
|
||||
margin: 0.4*100rpx auto 0 auto;
|
||||
background-image: linear-gradient(to right, #f67a38 0%, #f11b09 100%);
|
||||
background-image: -webkit-linear-gradient(to right, #f67a38 0%, #f11b09 100%);
|
||||
background-image: -moz-linear-gradient(to right, #f67a38 0%, #f11b09 100%);
|
||||
}
|
||||
.WriteOff .sure.cancel {
|
||||
background-image: none;
|
||||
color: #999;
|
||||
margin-top: 0.1*100rpx;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
name: "WriteOff",
|
||||
props: {
|
||||
iShidden: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
orderInfo: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
mounted: function() {},
|
||||
methods: {
|
||||
cancel: function() {
|
||||
this.$emit("cancel", true);
|
||||
},
|
||||
confirm: function() {
|
||||
this.$emit("confirm", true);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,435 @@
|
||||
<template name="blPaymentPasswordInput">
|
||||
<view class="box" :style="{ 'padding-bottom': keyboardHeight + 'px' }">
|
||||
<view class="box__header" :style="hideTopBorder?'border:none;':''">
|
||||
<slot name='header'></slot>
|
||||
</view>
|
||||
<view class="box__body">
|
||||
<view class="password-box">
|
||||
<view class="password-item" @tap="show">
|
||||
<view class="password-item-char" v-for="i in items" :key="i">
|
||||
<view
|
||||
class="password-item-char__dot"
|
||||
v-if="password[i] || password[i] === 0"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="">
|
||||
<slot name='middle'></slot>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view :class="'keyboard ' + pattern">
|
||||
<ul class="number">
|
||||
<li class="button uni-flex-cc" @tap="input(item)" v-for="(item,index) in keys" :key="index">
|
||||
{{ item }}
|
||||
</li>
|
||||
<!-- 暂时去掉dot -->
|
||||
<!-- <li class="button dot">.</li> -->
|
||||
<li class="button dot"></li>
|
||||
<!-- <li class="button down" @tap="hide"></li> -->
|
||||
<li @tap="del" class="left-delete">
|
||||
<view class="huishan"></view>
|
||||
<li/>
|
||||
</ul>
|
||||
<!-- <view class="action">
|
||||
<view class="delete" @tap="del"></view>
|
||||
<view class="ok" @tap="done"></view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const keys = () => {
|
||||
let natural = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
return natural.sort(function() {
|
||||
return Math.random() > 0.5 ? -1 : 1; //用Math.random()函数生成0~1之间的随机数与0.5比较,返回-1或1
|
||||
});
|
||||
};
|
||||
|
||||
// import uniIcon from '@/components/ku3gitxdx-icon/ku3gitxdx-icon.vue'
|
||||
|
||||
export default {
|
||||
name: 'blPaymentPasswordInput',
|
||||
props: {
|
||||
defaultPassword: String,
|
||||
hideTopBorder:{
|
||||
type:Boolean,
|
||||
default:false,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
// uniIcon
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [0, 1, 2, 3, 4, 5],
|
||||
password: [],
|
||||
keys: keys(),
|
||||
pattern: 'hidden',
|
||||
keyboardHeight: uni.upx2px(480),
|
||||
// boxHeaderStyle:this.hideTopBoder?'border:none;':''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
let args = {
|
||||
cancel: false
|
||||
};
|
||||
this.$emit('shown', args);
|
||||
if (!args.cancel) {
|
||||
this.pattern = 'slideup';
|
||||
}
|
||||
this.keyboardHeight = uni.upx2px(480);
|
||||
},
|
||||
hide() {
|
||||
let args = {
|
||||
cancel: false
|
||||
};
|
||||
this.$emit('hidden', args);
|
||||
if (!args.cancel) {
|
||||
this.pattern = 'slidedown';
|
||||
}
|
||||
this.keyboardHeight = 0;
|
||||
},
|
||||
input(item) {
|
||||
let args = {
|
||||
// target: this,
|
||||
data: item,
|
||||
cancel: false,
|
||||
callback: undefined
|
||||
};
|
||||
// console.log('s');
|
||||
this.$emit('input', args);
|
||||
if (!args.cancel) {
|
||||
|
||||
if(this.password.length<this.items.length){
|
||||
this.password.push(item);
|
||||
}
|
||||
|
||||
if (this.password.length === this.items.length) {
|
||||
//长度达6位,自动验证
|
||||
|
||||
// console.log(JSON.stringify(item))
|
||||
// console.log('verify')
|
||||
this.$emit('confirm', {
|
||||
target: this,
|
||||
value: this.password.join('')
|
||||
});
|
||||
|
||||
return;
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (args.callback && typeof args.callback === 'function') {
|
||||
args.callback.call(this);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
del() {
|
||||
let args = {
|
||||
cancel: false
|
||||
};
|
||||
this.$emit('delete', args);
|
||||
if (!args.cancel) {
|
||||
if (this.password.length > 0) {
|
||||
this.password = this.password.slice(0, this.password.length - 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
done() {
|
||||
|
||||
if (this.password.length !== this.items.length) {
|
||||
uni.showToast({
|
||||
title:'请输入6位支付密码',
|
||||
icon:'none',
|
||||
duration:2000
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('confirm', {
|
||||
target: this,
|
||||
value: this.password.join('')
|
||||
});
|
||||
},
|
||||
clear(){
|
||||
this.password = [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultPassword: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val && val.length === 6) {
|
||||
this.password = val.split('').map(Number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('https://cdn.bootcss.com/font-awesome/4.7.0/fonts/fontawesome-webfont.eot?v=4.7.0');
|
||||
src: url('https://cdn.bootcss.com/font-awesome/4.7.0/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0')
|
||||
format('embedded-opentype'),
|
||||
url('https://cdn.bootcss.com/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0')
|
||||
format('woff2'),
|
||||
url('https://cdn.bootcss.com/font-awesome/4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0')
|
||||
format('woff'),
|
||||
url('https://cdn.bootcss.com/font-awesome/4.7.0/fonts/fontawesome-webfont.ttf?v=4.7.0')
|
||||
format('truetype'),
|
||||
url('https://cdn.bootcss.com/font-awesome/4.7.0/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular')
|
||||
format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.dot{
|
||||
background: #e5e5e5;
|
||||
order: 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.box {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
&__header {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
// height: 170upx;
|
||||
border-radius: 24upx 24upx 0upx 0upx;
|
||||
background: #fff;
|
||||
border-top: 1px solid #ddd;
|
||||
font-size: $uni-font-size-lg;
|
||||
}
|
||||
&__body {
|
||||
padding-top: 65upx;
|
||||
box-sizing: border-box;
|
||||
height: 200upx;
|
||||
background: #ffffff;
|
||||
.password-box {
|
||||
height: 90upx;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
.password-item {
|
||||
height: 100%;
|
||||
border: 1px solid #ddd;//#00c456
|
||||
border-radius: 4upx;
|
||||
width: 602upx;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 0 3px rgba(221,221,221,0.6);//rgba(0, 196, 86, 0.6)
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&-char {
|
||||
width: 16.66666666%;
|
||||
height: 90upx;
|
||||
border-right: 1px solid #ddd;
|
||||
line-height: 90upx;
|
||||
text-align: center;
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
&__dot {
|
||||
height: 90upx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
&:after {
|
||||
content: '●';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.keyboard {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
height: 480rpx;
|
||||
width: 100%;
|
||||
border-top: 1px solid #ddd;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.number {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;//cg
|
||||
list-style: none;
|
||||
padding: 0 0 0 1px;
|
||||
margin: 0;
|
||||
border-right: 1px solid #ddd;
|
||||
.button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 33%;
|
||||
font-size: 40rpx;
|
||||
height: 120rpx;
|
||||
text-align: center;
|
||||
line-height: 100rpx;
|
||||
border-right: 0.5px solid #ddd;
|
||||
border-bottom: 0.5px solid #ddd;
|
||||
|
||||
&:nth-child(3),
|
||||
&:nth-child(6),
|
||||
&:nth-child(9),
|
||||
&:nth-child(12) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&:nth-child(10),
|
||||
&:nth-child(11),
|
||||
&:nth-child(12) {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:nth-child(11) {
|
||||
font-size: $uni-font-size-lg;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// add
|
||||
&:nth-child(10){
|
||||
order:11
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #ddd;
|
||||
}
|
||||
}
|
||||
.down:after {
|
||||
content: '\f11c';
|
||||
font-family: 'FontAwesome';
|
||||
font-size: 30px;
|
||||
}
|
||||
.left-delete{
|
||||
order:12;
|
||||
border-top: 1px solid #ddd;
|
||||
background: #e5e5e5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 33%;
|
||||
height: 120upx;
|
||||
text-align: center;
|
||||
line-height: 100upx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// &:after{
|
||||
// content: '\f00d';
|
||||
// font-family: 'FontAwesome';
|
||||
// font-size: 30px;
|
||||
// };
|
||||
&:active {
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
.huishan{
|
||||
width:60upx;
|
||||
height:60upx;
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAlCAMAAADyQNAxAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAATtQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeYXp2gAAAGh0Uk5TABsiIQo50vy9Q/nHqrDb/oIu8HtF3WEd5JUo++cEENMDvzUHLQgYk0oBotwWMbji4SaMTeslC7kJH9/BDGv0GfdLwrVAzcW25eByJ+aL3pGJvAKtIIi6j0cT2Ogk+Noz8zJWSciEUhEWhz0LAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAASAAAAEgARslrPgAAAURJREFUOMvtktdawkAQhceACCJGsQuiEWJFKRZAUcSCItixa7Cgnvd/AkkCYbObR+DczO75/kxmdoaoKyf1SKxcbiem1wO7+rw+AfL1wz/AKjAIeYiDhoMY4T8cHcO4zZiYxJRYxHQoPMNcI7MIzDnUqmC+c4kq8JsdxVTTcS0YYRFLFrS8gtW4cVoLrhuYlEim9JjGhkWlsbllnrYVZLJEuR3s5jlqD/vW3w2sCemsjSochIqdEvOH8CZwpBJHHeOEbStfAk6jxFNxWy5KnQFll0DROVMXVaq4UHCZEyi6snrUM12T+wZJSaBu79rvlapCH3ATK9d4irKl1tvHPfem8ZCJ6PERT0zJzy+tObY3TzWnGkCF7V96ddqJt3fNbtQ/xP361PDFWd8hNOy7qsn4EdL/hrm9lxtFclCtzir2R11x+gc++EswLKyTYwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wMy0wM1QxMTo1NDowMSswODowMAVqOMQAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDMtMDNUMTE6NTQ6MDErMDg6MDB0N4B4AAAASnRFWHRzdmc6YmFzZS11cmkAZmlsZTovLy9ob21lL2FkbWluL2ljb24tZm9udC90bXAvaWNvbl9yZzE4azhvN2pnci9odWlzaGFuLnN2Z8V/tSMAAAAASUVORK5CYII=') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
// .left-delete:after{
|
||||
// content: '\f00d';
|
||||
// font-family: 'FontAwesome';
|
||||
// font-size: 30px;
|
||||
// }
|
||||
}
|
||||
.action {
|
||||
width: 25%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.delete {
|
||||
height: 50%;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #ddd;
|
||||
background: #ddd;
|
||||
&:after {
|
||||
content: '\f00d';
|
||||
font-family: 'FontAwesome';
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
font-size: 30px;
|
||||
display: block;
|
||||
line-height: 240upx;
|
||||
}
|
||||
&:active {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.ok {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
&:after {
|
||||
content: '\f00c';
|
||||
font-family: 'FontAwesome';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
display: block;
|
||||
line-height: 240upx;
|
||||
}
|
||||
&:active {
|
||||
background: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*动画*/
|
||||
@keyframes slidedown {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
.slidedown {
|
||||
animation: slidedown 0.3s linear;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
.slideup {
|
||||
animation: slideup 0.3s linear;
|
||||
transform: translateY(0);
|
||||
}
|
||||
@keyframes slideup {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<view class="chose-city">
|
||||
<view class="header">
|
||||
<view class="search-box flex jc-between ai-center">
|
||||
<input type="text" v-model="keyword" placeholder="搜索城市" placeholder-style="color:#999" @confirm="search">
|
||||
<image class="icon" :src="webUrl+'/20220903142935869059.png'" mode="scaleToFill" @click="search"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="aside">
|
||||
<view class="item flex jc-center ai-center" :class="index === navActive ? 'on' : ''"
|
||||
v-for="(item, index) in listGroup" :key="index" @click="asideTap(index)">
|
||||
<view class="name">{{ item.label }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<mescroll-body ref="mescrollRef" top="16rpx" bottom="0" class='cotent-scroll' :up="upOption" :down="downOption"
|
||||
@up="upCallback" @down="downCallback" @init="mescrollInit" @emptyclick="emptyClick">
|
||||
|
||||
<view class="fixed-box">
|
||||
<view class="line-location flex ai-center" v-if="location">
|
||||
<image :src="webUrl+'/20220903142929759087.png'" mode="scaleToFill"></image>
|
||||
<text class="bold" @click="select(location)">当前定位:{{ location }}</text>
|
||||
</view>
|
||||
|
||||
<image class="banner" :src="banner.icon" mode="scaleToFill" v-if="banner"
|
||||
@click="$global.commonJump(banner.url)"/>
|
||||
|
||||
<view class="top-box" v-if="listTop.length">
|
||||
<view class="tips">特别推荐</view>
|
||||
<view class="top-list custom-list flex ai-center">
|
||||
<view class="item" v-for="(item,index) in listTop" :key="index" @click="select(item)">
|
||||
<view class="has-goods" v-if="type===0 && item.hasData===0">暂无特产</view>
|
||||
<view class="has-goods" v-if="type>0 && type<6&& item.hasShop===0">暂无店铺</view>
|
||||
<image :src="item.icon" mode="scaleToFill"></image>
|
||||
<view class="one-t">{{ item.cityName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="province-name">{{ provinceName }}</view>
|
||||
<view class="group-list custom-list flex flex-wrap">
|
||||
<view class="item" v-for="(item, index) in listCity" :key="index" @click="select(item)">
|
||||
<view class="has-goods" v-if="type===0 && item.hasData===0">暂无特产</view>
|
||||
<view class="has-goods" v-if="type>0 && type<6&& item.hasShop===0">暂无店铺</view>
|
||||
<view class="has-goods" v-if="type===6&& item.hasData===0">暂无景点</view>
|
||||
<image :src="item.icon" mode="scaleToFill"></image>
|
||||
<view class="one-t">{{ item.cityName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollMixins from "@/mixins/mescroll-mixins.js";
|
||||
import {fetchCity} from "@/api/wenwan";
|
||||
import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue";
|
||||
import {getCurAddress, getLocation} from "@/utils/common.js";
|
||||
|
||||
export default {
|
||||
name: "choseCity",
|
||||
components: {
|
||||
MescrollBody
|
||||
},
|
||||
props: {},
|
||||
mixins: [
|
||||
MescrollMixins({
|
||||
getPageData(mescroll) {
|
||||
// 此时mescroll会携带page的参数:
|
||||
// let pageNum = mescroll.num; // 页码, 默认从1开始
|
||||
// let pageSize = mescroll.size; // 页长, 默认每页10条
|
||||
|
||||
mescroll.endSuccess(10);
|
||||
}
|
||||
})
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
upOption: {
|
||||
noMoreSize: 10, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
|
||||
auto: false,
|
||||
empty: {
|
||||
tip: '暂无城市' // 提示
|
||||
// btnText:'点击刷新'
|
||||
}
|
||||
},
|
||||
downOption: {
|
||||
auto: true
|
||||
},
|
||||
|
||||
webUrl: this.$VUE_APP_RESOURCES_URL,
|
||||
listGroup: [],
|
||||
listTop: [],
|
||||
provinceName: "",
|
||||
listCity: [],
|
||||
navActive: 0,
|
||||
keyword: "",
|
||||
type: null,
|
||||
location: "",
|
||||
banner: {}
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.type = Number(options.type);
|
||||
this.location = options.city;
|
||||
if (this.location === undefined) {
|
||||
this.getMapData();
|
||||
}
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
async getMapData() {
|
||||
const map = await getLocation();
|
||||
const {latitude, longitude} = map[1];
|
||||
const address = await getCurAddress(latitude, longitude);
|
||||
this.location = address[1].data.result.ad_info.city;
|
||||
},
|
||||
|
||||
init() {
|
||||
fetchCity(this.type).then(res => {
|
||||
if (res.success) {
|
||||
this.listGroup = res.data.group;
|
||||
// this.listTop = res.data.tops;
|
||||
if (this.listGroup.length > 0) {
|
||||
this.listTop = this.listGroup[0].top;
|
||||
this.listCity = this.listGroup[0].city;
|
||||
this.provinceName = this.listGroup[0].label;
|
||||
this.banner = {
|
||||
icon: this.listGroup[0].icon,
|
||||
url: this.listGroup[0].uniappUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
asideTap(index) {
|
||||
this.navActive = index;
|
||||
this.listTop = this.listGroup[index].top;
|
||||
this.listCity = this.listGroup[index].city;
|
||||
this.provinceName = this.listGroup[index].label;
|
||||
this.banner = {
|
||||
icon: this.listGroup[index].icon,
|
||||
url: this.listGroup[index].uniappUrl
|
||||
}
|
||||
//触发加载请求
|
||||
this.mescroll.scrollTo(0, 0);
|
||||
this.mescroll && this.mescroll.resetUpScroll(false);
|
||||
},
|
||||
emptyClick: function () {
|
||||
//触发加载请求
|
||||
this.mescroll && this.mescroll.triggerDownScroll()
|
||||
},
|
||||
search() {
|
||||
this.listGroup.findIndex((group, index) => {
|
||||
let result;
|
||||
group.city.forEach(item => {
|
||||
result = item.cityName.indexOf(this.keyword, 0);
|
||||
if (result !== -1) {
|
||||
this.asideTap(index);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
if (result !== -1) {
|
||||
return result
|
||||
}
|
||||
})
|
||||
},
|
||||
select(item) {
|
||||
if (this.type === 0 && item.hasData === 0) return;
|
||||
uni.setStorageSync("cityName", item.cityName)
|
||||
|
||||
switch (this.type) {
|
||||
case 0:
|
||||
uni.navigateTo({
|
||||
url: `/pkg_product/views/nativeList?cityId=${item.cityId}`
|
||||
})
|
||||
break;
|
||||
case 1:
|
||||
uni.navigateTo({
|
||||
url: `/wenwan/views/storeList?cityId=${item.cityId}`
|
||||
})
|
||||
break;
|
||||
case 6:
|
||||
uni.navigateTo({
|
||||
url: `/pkg_product/views/scenicSpot?cityId=${item.cityId}`
|
||||
})
|
||||
break;
|
||||
default:
|
||||
uni.navigateBack()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.chose-city {
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
color: #033333;
|
||||
|
||||
.banner {
|
||||
width: 520rpx;
|
||||
height: 200rpx;
|
||||
vertical-align: middle;
|
||||
margin: 16rpx auto 0;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.custom-list {
|
||||
.item {
|
||||
width: 160rpx;
|
||||
margin-right: 20rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 8rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.one-t {
|
||||
margin-top: 14rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 16rpx 32rpx 28rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
border-bottom: 2rpx solid #E9E9E9;
|
||||
z-index: 9;
|
||||
|
||||
.search-box {
|
||||
width: 686rpx;
|
||||
height: 64rpx;
|
||||
padding: 0 24rpx 0 32rpx;
|
||||
box-sizing: border-box;
|
||||
background: #F7F7F7;
|
||||
border-radius: 32rpx;
|
||||
|
||||
input {
|
||||
width: 560rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aside {
|
||||
position: fixed;
|
||||
width: 161rpx;
|
||||
left: 0;
|
||||
top: 110rpx;
|
||||
bottom: 0 !important;
|
||||
box-sizing: border-box;
|
||||
border-right: 2rpx solid #E9E9E9;
|
||||
background: #fff;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-scrolling: touch;
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
border-bottom: 2rpx solid #E9E9E9;
|
||||
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.item.on {
|
||||
border-left: 6rpx solid #FD574B;
|
||||
|
||||
.name {
|
||||
color: #FD574B;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aside::-webkit-scrollbar {
|
||||
width: 0 !important
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: 161rpx;
|
||||
margin-top: 110rpx;
|
||||
|
||||
.fixed-box {
|
||||
padding: 32rpx 0 44rpx 38rpx;
|
||||
|
||||
.line-location {
|
||||
font-size: 28rpx;
|
||||
|
||||
image {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.top-box {
|
||||
.tips {
|
||||
margin: 36rpx 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 40px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.top-list {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.top-list::-webkit-scrollbar {
|
||||
width: 0 !important
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.province-name {
|
||||
margin: 0 38rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.group-list {
|
||||
padding-left: 38rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.has-goods {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 7rpx 8rpx;
|
||||
border-radius: 0 8px 0 8px;
|
||||
background: #f66;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
id="_drag_button"
|
||||
class="drag"
|
||||
:style="'left: ' + left + 'px; top:' + top + 'px;'"
|
||||
@touchstart="touchstart"
|
||||
@touchmove.stop.prevent="touchmove"
|
||||
@touchend="touchend"
|
||||
@click.stop.prevent="click"
|
||||
:class="{transition: isDock && !isMove }"
|
||||
>
|
||||
<image style="width: 100%;height: 100%;" mode="scaleToFill" src="http://admin-api.xdd618.com/file/pic/20210809112551575811.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'drag-button',
|
||||
props: {
|
||||
isDock:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
existTabBar:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
top:0,
|
||||
left:0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
offsetWidth: 0,
|
||||
offsetHeight: 0,
|
||||
windowWidth: 0,
|
||||
windowHeight: 0,
|
||||
isMove: true,
|
||||
edge: 10,
|
||||
text: '按钮'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const sys = uni.getSystemInfoSync();
|
||||
|
||||
this.windowWidth = sys.windowWidth;
|
||||
this.windowHeight = sys.windowHeight;
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
this.existTabBar && (this.windowHeight -= 50);
|
||||
// #endif
|
||||
if (sys.windowTop) {
|
||||
this.windowHeight += sys.windowTop;
|
||||
}
|
||||
console.log(sys)
|
||||
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('#_drag_button').boundingClientRect(data => {
|
||||
this.width = data.width;
|
||||
this.height = data.height;
|
||||
this.offsetWidth = data.width / 2;
|
||||
this.offsetHeight = data.height / 2;
|
||||
this.left = this.windowWidth - this.width - this.edge;
|
||||
this.top = this.windowHeight - this.height - this.edge;
|
||||
}).exec();
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.$emit('btnClick');
|
||||
},
|
||||
touchstart(e) {
|
||||
this.$emit('btnTouchstart');
|
||||
},
|
||||
touchmove(e) {
|
||||
// 单指触摸
|
||||
if (e.touches.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.isMove = true;
|
||||
|
||||
this.left = e.touches[0].clientX - this.offsetWidth;
|
||||
|
||||
let clientY = e.touches[0].clientY - this.offsetHeight;
|
||||
// #ifdef H5
|
||||
clientY += this.height;
|
||||
// #endif
|
||||
let edgeBottom = this.windowHeight - this.height - this.edge;
|
||||
|
||||
// 上下触及边界
|
||||
if (clientY < this.edge) {
|
||||
this.top = this.edge;
|
||||
} else if (clientY > edgeBottom) {
|
||||
this.top = edgeBottom;
|
||||
} else {
|
||||
this.top = clientY
|
||||
}
|
||||
|
||||
},
|
||||
touchend(e) {
|
||||
if (this.isDock) {
|
||||
let edgeRigth = this.windowWidth - this.width - this.edge;
|
||||
|
||||
if (this.left < this.windowWidth / 2 - this.offsetWidth) {
|
||||
this.left = this.edge;
|
||||
} else {
|
||||
this.left = edgeRigth;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.isMove = false;
|
||||
|
||||
this.$emit('btnTouchend');
|
||||
},
|
||||
}}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.drag {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
//background-color: rgba(0, 0, 0, 0.5);
|
||||
//box-shadow: 0 0 6upx rgba(0, 0, 0, 0.4);
|
||||
color: $uni-text-color-inverse;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
font-size: $uni-font-size-sm;
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
|
||||
&.transition {
|
||||
transition: left .3s ease,top .3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<view v-if="showDd" class="dropdown">
|
||||
<view class="dropdown-mask"
|
||||
@click="handlerMask"
|
||||
></view>
|
||||
<view class="ul">
|
||||
<view
|
||||
class="li"
|
||||
v-for="(item,index) in list"
|
||||
:key="index"
|
||||
@click="handlerItem(item.name)"
|
||||
>
|
||||
{{item.name}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props:{
|
||||
list:{
|
||||
type:Array,
|
||||
default:() => ([])
|
||||
},
|
||||
showDd: {
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
|
||||
methods:{
|
||||
handlerItem(value) {
|
||||
this.$emit('select',value)
|
||||
},
|
||||
handlerMask() {
|
||||
|
||||
this.$emit('close')
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
// .dropdown {
|
||||
// position: absolute;
|
||||
// z-index 100
|
||||
// .ul {
|
||||
// position relative
|
||||
// z-index 101
|
||||
// list-style none
|
||||
// background-color #fff
|
||||
// border-radius 4rpx
|
||||
// padding-left 0
|
||||
// box-shadow 10rpx 10rpx 20rpx rgba(224, 32, 32, 0.2)
|
||||
// .li{
|
||||
// color #000
|
||||
// padding 20rpx
|
||||
// border-bottom 1px solid #E6EAEB
|
||||
// font-size 24rpx
|
||||
// }
|
||||
// li:last-child{
|
||||
// border-bottom none
|
||||
// }
|
||||
// }
|
||||
// .dropdown-mask {
|
||||
// top 0
|
||||
// left 0
|
||||
// position fixed
|
||||
// width 100vw
|
||||
// height 100vh
|
||||
// z-index 99
|
||||
// touch-action none
|
||||
// }
|
||||
// }
|
||||
|
||||
.dropdown{
|
||||
// position: absolute;
|
||||
z-index:100;
|
||||
}
|
||||
|
||||
.dropdown .ul {
|
||||
position:relative;
|
||||
z-index:101;
|
||||
list-style:none ;
|
||||
background-color:#fff;
|
||||
border-radius:4rpx;
|
||||
padding-left:0;
|
||||
box-shadow:10rpx 10rpx 20rpx rgba(224, 32, 32, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.dropdown .ul .li{
|
||||
color:#000;
|
||||
padding:20rpx;
|
||||
border-bottom:1px solid #E6EAEB;
|
||||
font-size:24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dropdown .ul li:last-child{
|
||||
border-bottom:none;
|
||||
}
|
||||
|
||||
.dropdown .dropdown-mask {
|
||||
top:0;
|
||||
left:0;
|
||||
position:fixed;
|
||||
width:100vw;
|
||||
height:100vh;
|
||||
z-index:99;
|
||||
touch-action:none;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,343 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="upload">
|
||||
<block v-for="(upload,index) in uploads" :key="index">
|
||||
<view class="uplode-file">
|
||||
<image v-if="types == 'image'" class="uploade-img" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'" :src="upload" :data-src="upload" @tap="previewImage"></image>
|
||||
<image v-if="types == 'image'" class="clear-one-icon" :src="clearIcon" @tap="delImage(index)"></image>
|
||||
<video v-if="types == 'video'" class="uploade-img" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'" :src="upload" controls>
|
||||
<cover-image v-if="types == 'video'" class="clear-one-icon" :src="clearIcon" @tap="delImage(index)"></cover-image>
|
||||
</video>
|
||||
</view>
|
||||
</block>
|
||||
<view v-if="uploads.length < uploadCount" :class="uploadIcon ? 'uploader-icon' : 'uploader-input-box'" :style="'width:' + upload_img_wh + 'rpx; height:' + upload_img_wh + 'rpx;'" >
|
||||
<view v-if="!uploadIcon" class="uploader-input" @tap="chooseUploads"></view>
|
||||
<image v-else class="image-cion" :src="uploadIcon" @tap="chooseUploads"></image>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <button type="primary" v-if="types == 'image' && !autoUpload" @tap="unifiedUpload">上传</button> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default{
|
||||
props: {
|
||||
header:{
|
||||
type:Object,
|
||||
default:{}
|
||||
},
|
||||
types: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
},
|
||||
dataList: {
|
||||
type: Array,
|
||||
default: function() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
clearIcon: {
|
||||
type: String,
|
||||
default: 'http://img1.imgtn.bdimg.com/it/u=451604666,2295832001&fm=26&gp=0.jpg'
|
||||
},
|
||||
uploadIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
uploadUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
deleteUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
uploadCount: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
//上传图片大小 默认3M
|
||||
upload_max: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
//上传视频大小 默认30M
|
||||
upload_video_max: {
|
||||
type: Number,
|
||||
default: 50
|
||||
},
|
||||
// 图片/选择宽高
|
||||
upload_img_wh: {
|
||||
type: Number,
|
||||
default: 210
|
||||
},
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
//上传的图片地址
|
||||
uploadImages: [],
|
||||
//展示的图片地址
|
||||
uploads: [],
|
||||
// 超出限制数组
|
||||
exceeded_list: [],
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
dataList:{
|
||||
handler(val){
|
||||
this.uploads = val;
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
previewImage (e) {
|
||||
var current = e.target.dataset.src
|
||||
uni.previewImage({
|
||||
current: current,
|
||||
urls: this.dataList
|
||||
})
|
||||
},
|
||||
chooseUploads(){
|
||||
switch (this.types){
|
||||
case 'image':
|
||||
uni.chooseImage({
|
||||
count: this.uploadCount - this.uploads.length, //默认9
|
||||
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album', 'camera'], //从相册选择
|
||||
success: (res) => {
|
||||
for(let i = 0; i< res.tempFiles.length; i++){
|
||||
if(Math.ceil(res.tempFiles[i].size / 1024) < this.upload_max * 1024){
|
||||
this.uploads.push(res.tempFiles[i].path)
|
||||
console.log(this.uploads);
|
||||
if(this.autoUpload){
|
||||
this.uploadFile(res.tempFiles[i].path)
|
||||
}else{
|
||||
this.uploadImages.push(res.tempFiles[i].path);
|
||||
}
|
||||
}else {
|
||||
this.exceeded_list.push(i === 0 ? 1 : i + 1);
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `第${[...new Set(this.exceeded_list)].join(',')}张图片超出限制${this.upload_max}MB,已过滤`
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showModal({
|
||||
content: JSON.stringify(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'video' :
|
||||
uni.chooseVideo({
|
||||
sourceType: ['camera', 'album'],
|
||||
success: (res) => {
|
||||
if(Math.ceil(res.size / 1024) < this.upload_video_max * 1024){
|
||||
this.uploads.push(res.tempFilePath)
|
||||
uni.uploadFile({
|
||||
url: this.uploadUrl, //仅为示例,非真实的接口地址
|
||||
filePath: res.tempFilePath,
|
||||
name: 'file',
|
||||
//请求参数
|
||||
formData: {
|
||||
'user': 'test'
|
||||
},
|
||||
success: (uploadFileRes) => {
|
||||
this.$emit('successVideo',uploadFileRes)
|
||||
}
|
||||
});
|
||||
}else {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `第${[...new Set(this.exceeded_list)].join(',')}张视频超出限制${this.upload_max}MB,已过滤`
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showModal({
|
||||
content: JSON.stringify(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
delImage(index){
|
||||
|
||||
this.uploads.splice(index,1)
|
||||
//如果不自动上传
|
||||
if(!this.autoUpload){
|
||||
this.uploadImages.splice(index,1)
|
||||
}
|
||||
|
||||
// //第一个是判断app或者h5的 第二个是判断小程序的
|
||||
// if(this.uploads[index].substring(0,4) !== 'http' || this.uploads[index].substring(0,11) == 'http://tmp/'){
|
||||
// this.uploads.splice(index,1)
|
||||
// //如果不自动上传
|
||||
// if(!this.autoUpload){
|
||||
// this.uploadImages.splice(index,1)
|
||||
// }
|
||||
// return;
|
||||
// };
|
||||
|
||||
// if(!this.deleteUrl) {
|
||||
// uni.showModal({
|
||||
// content: '请填写删除接口'
|
||||
// });
|
||||
// return;
|
||||
// };
|
||||
// uni.request({
|
||||
// url: this.deleteUrl,
|
||||
// method: 'DELETE',
|
||||
// data: {
|
||||
// image: this.dataList[index]
|
||||
// },
|
||||
// success: res => {
|
||||
// if(res.data.status == 1) {
|
||||
// uni.showToast({
|
||||
// title: '删除成功'
|
||||
// })
|
||||
// this.uploads.splice(index,1)
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
},
|
||||
uploadFile(path){
|
||||
// uni.uploadFile({
|
||||
// url: this.uploadUrl, //仅为示例,非真实的接口地址
|
||||
// filePath: path,
|
||||
// name: 'file',
|
||||
// //自定义请求参数
|
||||
// formData: {
|
||||
// 'user': 'test'
|
||||
// },
|
||||
// success: (uploadFileRes) => {
|
||||
// this.$emit('successImage',uploadFileRes)
|
||||
// }
|
||||
// });
|
||||
|
||||
uni.uploadFile({
|
||||
url: this.uploadUrl,
|
||||
filePath: path,
|
||||
header: this.header,
|
||||
name: "file",
|
||||
success: res => {
|
||||
console.log(res);
|
||||
|
||||
this.$emit('successImage',uploadFileRes)
|
||||
},
|
||||
fail: err => {
|
||||
|
||||
console.log(err);
|
||||
|
||||
},
|
||||
complete: res => {
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
unifiedUpload(){
|
||||
if(!this.uploadUrl) {
|
||||
uni.showModal({
|
||||
content: '请填写上传接口'
|
||||
});
|
||||
return;
|
||||
};
|
||||
for (let i of this.uploadImages) {
|
||||
this.uploadFile(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.uplode-file {
|
||||
margin: 10rpx;
|
||||
/* width: 210upx;
|
||||
height: 210upx; */
|
||||
position: relative;
|
||||
}
|
||||
.uploade-img {
|
||||
display: block;
|
||||
/* width: 210upx;
|
||||
height: 210upx; */
|
||||
}
|
||||
.clear-one{
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: 0;
|
||||
}
|
||||
.clear-one-icon{
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
.uploader-input-box {
|
||||
position: relative;
|
||||
margin:10upx;
|
||||
/* width: 208upx;
|
||||
height: 208upx; */
|
||||
border: 2upx solid #D9D9D9;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.uploader-input-box:before,
|
||||
.uploader-input-box:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #D9D9D9;
|
||||
}
|
||||
.uploader-input-box:before {
|
||||
width: 4upx;
|
||||
height: 79upx;
|
||||
}
|
||||
.uploader-input-box:after {
|
||||
width: 79upx;
|
||||
height: 4upx;
|
||||
}
|
||||
.uploader-input-box:active {
|
||||
border-color: #999999;
|
||||
}
|
||||
.uploader-input-box:active:before,
|
||||
.uploader-input-box:active:after {
|
||||
background-color: #999999;
|
||||
}
|
||||
.uploader-input {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
.uploader-icon{
|
||||
position: relative;
|
||||
margin:10upx;
|
||||
/* width: 208upx;
|
||||
height: 208upx; */
|
||||
}
|
||||
.uploader-icon .image-cion{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
### easy-upload 组件
|
||||
|
||||
使用方法
|
||||
```js
|
||||
<easy-upload
|
||||
:dataList="dataList"
|
||||
uploadUrl="http://localhost:3000/upload"
|
||||
deleteUrl='http://localhost:3000/upload'
|
||||
:types="types"
|
||||
@successImage="successImage"
|
||||
@successVideo="successvideo"
|
||||
/>
|
||||
|
||||
//使用 hbuilderX (easycom) 可以直接使用 或
|
||||
import easyUpload from '@/components/easy-upload/easy-upload.vue'
|
||||
export default {
|
||||
//用easycom 则省略注册组件
|
||||
components:{
|
||||
easyUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataList: [],
|
||||
types: 'image'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 是否必填 | 参数描述
|
||||
| ---- | ---- | ---- | ----
|
||||
| types | String | 否 | 上传类型 image/video
|
||||
| autoUpload | Boolean | 否 | 自动上传 默认false
|
||||
| dataList | Array | 否 | 图片/视频数据展示
|
||||
| clearIcon | String | 否 | 删除图标(可以换成自己的图片)
|
||||
| uploadIcon | String | 否 | 上传图标(可以换成自己的图片)
|
||||
| uploadUrl | String | 否 | 上传的接口
|
||||
| deleteUrl | String | 否 | 删除的接口
|
||||
| uploadCount | Number | 否 | 上传图片最大个数(默认为一张)
|
||||
| upload_max | Number | 否 | 上传大小(默认为3M)
|
||||
| upload_max | Number | 否 | 上传大小(默认为3M)
|
||||
| upload_max | Number | 否 | 上传大小(默认为3M)
|
||||
|
||||
| 事件 | 是否必填 | 参数描述
|
||||
| ---- | ---- | ----
|
||||
| successImage | 否 | 上传图片成功事件
|
||||
| successVideo | 否 | 上传视频成功回调
|
||||
|
||||
示例项目中有简单的服务端代码 /server (node.js)
|
||||
如果本地测试可以先运行一下服务端的代码
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# hx-navbar 适用于 uni-app 项目的头部导航组件
|
||||
|
||||
导航栏组件,主要用于头部导航,组件名:hx-navbar
|
||||
|
||||
本组件目前兼容微信小程序、H5、5+APP。
|
||||
|
||||
### 本组件支持模式:
|
||||
1. 普通固定顶部导航
|
||||
2. 透明导航
|
||||
3. 透明固定顶部导航
|
||||
4. 不固定普通导航
|
||||
5. 背景颜色线性渐变
|
||||
6. 滑动显示背景
|
||||
7. 左、中、右3个插槽;可关闭左右插槽使中间插槽铺满导航,实现高度自定义的导航需求
|
||||
|
||||
### 使用前提
|
||||
|
||||
需要先安装·uniapp·官方的```uni-icons``` 图标组件,```uni-icons```官方组件下载地址:[https://ext.dcloud.net.cn/plugin?id=28](https://ext.dcloud.net.cn/plugin?id=28)
|
||||
|
||||
### 使用方式
|
||||
页面使用需在 ``` script ``` 中引用组件
|
||||
``` javascript
|
||||
import hxNavbar from "@/components/hx-navbar/hx-navbar.vue"
|
||||
export default {
|
||||
components: {hxNavbar}
|
||||
}
|
||||
```
|
||||
|
||||
全局使用需在 ``` main.js ``` 中注册组件
|
||||
``` javascript
|
||||
import hxNavbar from "./components/hx-navbar/hx-navbar.vue"
|
||||
Vue.component('hx-navbar',hxNavbar)
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 属性
|
||||
#### 基本属性
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| ----------------------------|--------------- | ---------------------- | ---------------------------------------------------|
|
||||
| back | Boolean | true | 返回上一页,(设置后,```leftIcon```属性,和```click-left```事件将失效|
|
||||
| height | String | 44px | 导航栏高度(不包含状态栏高度)|
|
||||
| barPlaceholder | String | auto | 导航栏占位符 显示(show),隐藏(hidden),自动(auto:如果头部为固定fixed ,则显示占位符) |
|
||||
| title | String | - | 导航标题(当设置了标题,中间插槽将失效) |
|
||||
| fixed | Boolean | false | 固定头部 |
|
||||
| color | String | #000000 | 导航文字颜色(如果需要屏幕滑动后变色,参数则为数组,例子:`['#000000','#ffffff']`) |
|
||||
| backgroundColor | Array | [255, 255, 255] | 导航背景颜色为RGB 编号(单色背景数组为```[255,255,255]```,线性渐变背景```[[236, 0, 140],[103, 57, 182],...]```) |
|
||||
| pageScroll | Object | {} | 屏幕滑动距离顶部的对象```滑动渐变必要参数``` |
|
||||
| backgroundColorLinearDeg | String | 45 | 导航背景线性渐变角度 |
|
||||
| backgroundImg | String | - | 导航背景图片(背景图片优先级高于背景颜色) |
|
||||
| transparent | String | show | 背景透明(show 不透明,hidden 透明,auto 自动:滑动逐渐显示背景颜色,当头部固定时生效) 兼容性:头条小程序必须在页面上加 onPageScroll(e){} ,才能滑动显示背景,可参考dome7|
|
||||
| shadow | Boolean | false | 导航栏阴影 |
|
||||
| border | Boolean | false | 导航栏边框 |
|
||||
|
||||
#### 关于状态栏的属性
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| ----------------------------|--------------- | ---------------------- | ---------------------------------------------------|
|
||||
| statusBar | Boolean | true | 包含状态栏 |
|
||||
| statusBarFontColor | Array,String | #000000 | 状态栏字体颜色,只支持```#000000 ```和```#FFFFFF```(如果需要屏幕滑动变色,参数则为数组,例子:```['#000000','#ffffff']```)|
|
||||
| statusBarBackground | String | - | 状态栏背景颜色,如果你想单独设置状态栏颜色,该属性是个不错的选择
|
||||
|
||||
#### 关于插槽的属性
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| ----------------------------|--------------- | ---------------------- | ---------------------------------------------------|
|
||||
| leftIcon | String | - | 左插槽图标,必须为 ```uni-icons``` 图标 |
|
||||
| rightIcon | String | - | 右插槽图标,必须为 ```uni-icons``` 图标 |
|
||||
| leftSlot | Boolean | true | 开启左插槽 |
|
||||
| rightSlot | Boolean | true | 开启右插槽 |
|
||||
| leftSlidiSwitch | Boolean | false | 屏幕滑动后 `left`插槽切换为`leftAfter`插槽 |
|
||||
| centerSlidiSwitch | Boolean | false | 屏幕滑动后 `default`插槽切换为`centerAfter`插槽 |
|
||||
| rightSlidiSwitch | Boolean | false | 屏幕滑动后 `right`插槽切换为`rightAfter`插槽 |
|
||||
|
||||
|
||||
#### 返回上一页为空时的处理属性
|
||||
| 名称 | 类型 | 默认值 | 描述 |
|
||||
| ----------------------------|--------------- | ---------------------- | ---------------------------------------------------|
|
||||
| backTabbarUrl | String | /pages/index/index | 返回至指定的tabber页面(返回首页),当上一页为空时生效;全局使用推荐进组件修改`backTabbarUrl`的默认值|
|
||||
| defaultBackUrl | String | - | 返回至指定的普通页面,当上一页为空时生效;`defaultBackUrl`优先级高于`backTabbarUrl`;主要应用在返回失效时|
|
||||
|
||||
``` html
|
||||
<!-- 使用场景:假如刷新了当前页面,那么返回事件将失效。
|
||||
这时用上 `defaultBackUrl` 或 `backTabbarUrl` 则能返回至指定页面-->
|
||||
<hx-navbar left-text="关于" defaultBackUrl="/pages/user/setting/setting" />
|
||||
```
|
||||
|
||||
|
||||
### 插槽
|
||||
| 名称 | 描述 |
|
||||
| ----------------------|-------------------------------------------------------------------|
|
||||
| left | 左插槽 (可关闭该插槽 ```leftSlot``` 属性) |
|
||||
| default | 中间插槽(当设置了标题,中间插槽将失效) |
|
||||
| right | 右插槽 (可关闭该插槽 ```rightSlot``` 属性) |
|
||||
| leftAfter | 屏幕滑动后的左插槽 (需要开启`leftSlidiSwitch`属性才生效) |
|
||||
| centerAfter | 屏幕滑动后的中插槽 (需要开启`centerSlidiSwitch`属性才生效) |
|
||||
| rightAfter | 屏幕滑动后的右插槽 (需要开启`rightSlidiSwitch`属性才生效) |
|
||||
|
||||
|
||||
``` html
|
||||
<hx-navbar>
|
||||
<view>标题栏(中间插槽)</view>
|
||||
<view slot="left">left(左插槽)</view>
|
||||
<view slot="right">right(右插槽)</view>
|
||||
</hx-navbar>
|
||||
```
|
||||
|
||||
|
||||
### 事件
|
||||
| 名称 | 参数 | 描述 |
|
||||
| -----------------|------------------| --------------------------|
|
||||
| click-left | - | 左侧按钮点击时触发,此事件将覆盖 `返回` |
|
||||
| click-right | - | 右侧按钮点击时触发 |
|
||||
| scroll | - | 监听滚动条,回调参数为滚动距离;固定头部时生效;应用场景:如滚动到多少时触发某些事件 |
|
||||
|
||||
## 使用例子
|
||||
|
||||
### 简单使用
|
||||
``` html
|
||||
<hx-navbar title="我爱新疆" left-text="返回" />
|
||||
```
|
||||
|
||||
### 背景颜色线性渐变、头部固定
|
||||
``` html
|
||||
<hx-navbar
|
||||
title="颜色渐变"
|
||||
:back="false"
|
||||
:fixed="true"
|
||||
color="#ffffff"
|
||||
:background-color="[[28, 187, 180],[141, 198, 63]]">
|
||||
</hx-navbar>
|
||||
```
|
||||
|
||||
### 滑动显示背景
|
||||
``` html
|
||||
<!-- 该例子取消了导航占位符 -->
|
||||
<hx-navbar
|
||||
title="颜色渐变"
|
||||
:back="false"
|
||||
:fixed="true"
|
||||
color="#ffffff"
|
||||
barPlaceholder="hidden"
|
||||
transparent="auto"
|
||||
:background-color="[[28, 187, 180],[141, 198, 63]]"
|
||||
:pageScroll.sync="scrollData">
|
||||
</hx-navbar>
|
||||
|
||||
```
|
||||
``` javascript
|
||||
data() {
|
||||
return {
|
||||
scrollData: {},
|
||||
}
|
||||
},
|
||||
//必须在页面加 onPageScroll(e){} ,才能滑动显示背景
|
||||
onPageScroll(e){
|
||||
this.scrollData = e;
|
||||
},
|
||||
```
|
||||
|
||||
### 左中插槽演示
|
||||
``` html
|
||||
<hx-navbar
|
||||
:back="false"
|
||||
:fixed="true"
|
||||
right-icon="scan">
|
||||
<block slot="left">
|
||||
<view class="city">
|
||||
<view>新疆</view>
|
||||
<uni-icons type="arrowdown" color="#333333" size="22" />
|
||||
</view>
|
||||
</block>
|
||||
<view class="input-view">
|
||||
<uni-icons type="search" size="22" color="#666666" />
|
||||
<input confirm-type="search" class="input" type="text" placeholder="输入搜索关键词" @confirm="confirm">
|
||||
</view>
|
||||
</hx-navbar>
|
||||
|
||||
|
||||
/*css 用于演示插槽自定义样式*/
|
||||
<style>
|
||||
.city{
|
||||
display: flex;flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin-left: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 关闭左右插槽演示
|
||||
``` html
|
||||
<hx-navbar :back="false" :fixed="true" :leftSlot='false' :rightSlot='false'>
|
||||
<view style="display: flex;">
|
||||
<view class="city">
|
||||
<view>新疆</view>
|
||||
<uni-icons type="arrowdown" size="22" />
|
||||
</view>
|
||||
<view class="input-view" style="width: 100%;">
|
||||
<uni-icons type="search" size="22" color="#666666" />
|
||||
<input confirm-type="search" class="input" type="text" placeholder="输入搜索关键词" @confirm="confirm">
|
||||
</view>
|
||||
<uni-icons type="scan" size="22" style="line-height: 44px;padding-left: 8px;"/>
|
||||
</view>
|
||||
</hx-navbar>
|
||||
/*css 用于演示插槽自定义样式*/
|
||||
<style>
|
||||
.city{
|
||||
display: flex;flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin-left: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 屏幕滑动切换显示插槽
|
||||
``` html
|
||||
<!-- 该例子演示中间插槽、右插槽屏幕滑动后的变换 -->
|
||||
<hx-navbar
|
||||
:border="true"
|
||||
:centerSlidiSwitch="true"
|
||||
:rightSlidiSwitch="true"
|
||||
:fixed="true">
|
||||
<view style="text-align: center;width: 100%;">
|
||||
<text>帮助反馈</text>
|
||||
</view>
|
||||
<view slot="centerAfter" style="text-align: center;width: 100%;">
|
||||
<text>咨询</text>
|
||||
</view>
|
||||
<block slot="right">
|
||||
<uni-icons type="qq" size="30" ></uni-icons>
|
||||
</block>
|
||||
<block slot="rightAfter">
|
||||
<uni-icons type="chat" size="30" ></uni-icons>
|
||||
</block>
|
||||
</hx-navbar>
|
||||
```
|
||||
@@ -0,0 +1,650 @@
|
||||
<template>
|
||||
|
||||
<view class="hx-navbar" >
|
||||
|
||||
<view
|
||||
:class="{'hx-navbar--fixed': fixed,'hx-navbar--shadow':shadow,'hx-navbar--border':border}"
|
||||
:style="{'background': backgroundColorRgba}"
|
||||
class="hx-navbar__content">
|
||||
<block v-if="backgroundImg">
|
||||
<image class="navbgimg" :style="bgImgHeight?`height:${bgImgHeight}`:''" :src="backgroundImg" :mode="bgImgHeight=='auto'?'widthFix':''"></image>
|
||||
</block>
|
||||
|
||||
<view :style="{ height: statusBarHeight ,'background': statusBarBackground}" class="hx-status-bar" v-if="statusBar" ></view>
|
||||
<view :style="{color:colorInfo,height: height,'line-height':height}" class="hd hx-navbar__header hx-navbar__content_view">
|
||||
<view class="hx-navbar__header-btns hx-navbar__content_view" @tap="onClickLeft" v-if="leftSlot" :style="{'color': colorInfo}">
|
||||
<block v-if="leftText.length || leftIcon.length || back">
|
||||
<view
|
||||
v-if="leftIcon.length || back"
|
||||
:class="back ? 'left_back' : ''"
|
||||
class="hx-navbar__content_view" >
|
||||
<uni-icons :type="back ? 'arrowleft' : leftIcon" :color="colorInfo" size="28"/>
|
||||
</view>
|
||||
<view
|
||||
v-if="leftText.length"
|
||||
:class="{'hx-navbar-btn-icon-left':!leftIcon.length}"
|
||||
class="hx-navbar-btn-text hx-navbar__content_view">{{ leftText }}</view>
|
||||
|
||||
</block>
|
||||
<block v-else>
|
||||
<slot name="leftAfter" v-if="leftSlidiSwitch && slotSlidiSwitch == 1" />
|
||||
<slot name="left" v-else/>
|
||||
|
||||
</block>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="hx-navbar__header-container hx-navbar__content_view">
|
||||
<view
|
||||
v-if="title.length"
|
||||
class="hx-navbar__header-container-inner hx-navbar__content_view" style="position: relative;">{{ title }}
|
||||
</view>
|
||||
<!-- 标题插槽 -->
|
||||
|
||||
<block v-else>
|
||||
<slot name="centerAfter" v-if="centerSlidiSwitch && slotSlidiSwitch == 1"/>
|
||||
<slot v-else/>
|
||||
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<view :class="title.length?'hx-navbar__header-btns-right':''"
|
||||
class="hx-navbar__header-btns hx-navbar__content_view"
|
||||
@tap="onClickRight"
|
||||
v-if="rightSlot">
|
||||
<!-- 优先显示图标 -->
|
||||
<block v-if="rightIcon.length || rightText.length">
|
||||
<view class="hx-navbar__content_view" v-if="rightIcon.length">
|
||||
<uni-icons :type="rightIcon" :color="colorInfo" size="28"/>
|
||||
</view>
|
||||
<view v-if="rightText.length" class="hx-navbar-btn-text hx-navbar__content_view">{{ rightText }}</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<slot name="rightAfter" v-if="rightSlidiSwitch && slotSlidiSwitch == 1"/>
|
||||
<slot name="right" v-else/>
|
||||
</block>
|
||||
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="placeholder"
|
||||
class="hx-navbar__placeholder">
|
||||
<view :style="{ height: statusBarHeight}" class="hx-status-bar" v-if="statusBar" ></view>
|
||||
|
||||
<view :style="{ height: height}" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniIcons from "@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue"
|
||||
//获取系统状态栏高度
|
||||
var statusBarHeight = uni.getSystemInfoSync().statusBarHeight + 'px';
|
||||
export default {
|
||||
name: "hx-navbar",
|
||||
components: {
|
||||
uniIcons
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: statusBarHeight,
|
||||
transparentValue: 0,
|
||||
navTransparentFixedFontColor: '#fff',
|
||||
|
||||
statusBarFontColorInfo: [],
|
||||
backgroundColorRgba: 'rgba(255,255,255,1)',
|
||||
backgroundColorRgb: 'rgb(222,222,222)',
|
||||
colorInfo: '#000000',
|
||||
placeholder: false,
|
||||
colorContainer: null,
|
||||
slotSlidiSwitch: 0
|
||||
|
||||
};
|
||||
},
|
||||
props:{
|
||||
height:{
|
||||
type: String,
|
||||
default: "44px"
|
||||
},
|
||||
//导航栏占位符 显示(show),隐藏(hidden),自动(auto:如果头部为固定fiexd ,则显示占位符)
|
||||
barPlaceholder:{
|
||||
type: String,
|
||||
default: "auto"
|
||||
},
|
||||
//返回上一页
|
||||
back:{
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
//标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//是否开启左插槽
|
||||
leftSlot:{
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
//是否开启右插槽
|
||||
rightSlot:{
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
|
||||
//左边文字
|
||||
leftText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//右插槽文字
|
||||
rightText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//左插槽图标
|
||||
leftIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//右插槽图标
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//是否固定头
|
||||
fixed: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
//文字颜色
|
||||
color: {
|
||||
type: [Array,String],
|
||||
default: "#000000"
|
||||
},
|
||||
//导航栏背景颜色
|
||||
backgroundColor: {
|
||||
type: Array,
|
||||
default: function(){
|
||||
return new Array([255,255,255],[255,255,255]);
|
||||
}
|
||||
},
|
||||
//线性渐变角度
|
||||
backgroundColorLinearDeg: {
|
||||
type: String,
|
||||
default: '45'
|
||||
},
|
||||
//背景图片
|
||||
backgroundImg: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
bgImgHeight:{
|
||||
type:String,
|
||||
default:'auto'
|
||||
},
|
||||
//背景透明(show,hidden,auto)
|
||||
transparent: {
|
||||
type: String,
|
||||
default: 'show'
|
||||
},
|
||||
//状态栏字体颜色,只支持黑(#000000)和白(#FFFFFF)两种颜色。(,)
|
||||
statusBarFontColor:{
|
||||
type: [Array,String],
|
||||
default:function(){
|
||||
return new Array("#000000","#000000");
|
||||
}
|
||||
},
|
||||
//是否包含状态栏
|
||||
statusBar: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
//状态栏背景颜色
|
||||
statusBarBackground:{
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//导航栏阴影
|
||||
shadow: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
//导航栏边框
|
||||
border: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
//跳至普通页面
|
||||
defaultBackUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//跳至tabber页面
|
||||
backTabbarUrl: {
|
||||
type: String,
|
||||
default: '/pages/home/index'
|
||||
},
|
||||
//滑动后切换左插槽
|
||||
leftSlidiSwitch:{
|
||||
type: [Boolean,String],
|
||||
default: false,
|
||||
},
|
||||
//滑动后切换中间插槽
|
||||
centerSlidiSwitch:{
|
||||
type: [Boolean,String],
|
||||
default: false
|
||||
},
|
||||
//滑动后切换右插槽
|
||||
rightSlidiSwitch:{
|
||||
type: [Boolean,String],
|
||||
default: false
|
||||
},
|
||||
//页面的onPageScroll
|
||||
pageScroll:{
|
||||
type: Object,
|
||||
default:function(){
|
||||
return {}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
created(){
|
||||
var that = this;
|
||||
//是否添加占位符
|
||||
switch (that.barPlaceholder){
|
||||
case 'show':
|
||||
that.placeholder = true;
|
||||
break;
|
||||
case 'hidden':
|
||||
that.placeholder = false;
|
||||
break;
|
||||
case 'auto':
|
||||
if(that.fixed){
|
||||
that.placeholder = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//设置状态栏文字颜色
|
||||
that.setStatusBarFontColor();
|
||||
|
||||
//文字颜色
|
||||
that.colorContainer = typeof that.color == 'object' ? that.color : [that.color,that.color];
|
||||
that.colorInfo = that.colorContainer[0];
|
||||
//导航栏透明设置 及监听滚动
|
||||
switch (that.transparent){
|
||||
case 'show':
|
||||
that.transparentValue = 1;
|
||||
break;
|
||||
case 'hidden':
|
||||
that.transparentValue = 0;
|
||||
break;
|
||||
case 'auto':
|
||||
this.setTVAuto(that.pageScroll)
|
||||
break;
|
||||
}
|
||||
that.setBgColor();
|
||||
|
||||
//滑动切换
|
||||
if(that.fixed && (that.leftSlidiSwitch || that.centerSlidiSwitch || that.rightSlidiSwitch)){
|
||||
that.doScroll(that.pageScroll);
|
||||
}
|
||||
|
||||
},
|
||||
watch:{
|
||||
pageScroll(val,oldVal){
|
||||
var that = this;
|
||||
//导航栏透明设置 及监听滚动
|
||||
switch (that.transparent){
|
||||
case 'show':
|
||||
that.transparentValue = 1;
|
||||
break;
|
||||
case 'hidden':
|
||||
that.transparentValue = 0;
|
||||
break;
|
||||
case 'auto':
|
||||
this.setTVAuto(val)
|
||||
break;
|
||||
}
|
||||
//滑动切换
|
||||
if(that.fixed && (that.leftSlidiSwitch || that.centerSlidiSwitch || that.rightSlidiSwitch)){
|
||||
that.doScroll(val);
|
||||
}
|
||||
},
|
||||
//监控透明度变化
|
||||
transparentValue(val,oldVal) {
|
||||
var that = this;
|
||||
//this.settingColor();
|
||||
|
||||
//头条小程序不支持setNavigationBarColor方法
|
||||
// #ifndef MP-TOUTIAO || H5
|
||||
if(oldVal > 0.8){
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: that.statusBarFontColorInfo[1],
|
||||
backgroundColor: that.backgroundColorRgb
|
||||
});
|
||||
}else if(oldVal < 0.2){
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: that.statusBarFontColorInfo[0],
|
||||
backgroundColor: that.backgroundColorRgb
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-TOUTIAO
|
||||
if (tt.setNavigationBarColor) {
|
||||
if(oldVal > 0.8){
|
||||
tt.setNavigationBarColor({
|
||||
frontColor: that.statusBarFontColorInfo[1],
|
||||
backgroundColor: that.backgroundColorRgb,
|
||||
success(res) {},
|
||||
fail(res) {}
|
||||
});
|
||||
}else if(oldVal < 0.2){
|
||||
tt.setNavigationBarColor({
|
||||
frontColor: that.statusBarFontColorInfo[0],
|
||||
backgroundColor: that.backgroundColorRgb,
|
||||
success(res) {},
|
||||
fail(res) {}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("hx-navbar 提示:当前客户端版本过低,无法使用状态栏颜色修改功能,请升级(基础库1.40+)。")
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
//监听背景颜色
|
||||
backgroundColor(val,old){
|
||||
var that = this;
|
||||
that.setBgColor()
|
||||
},
|
||||
|
||||
color(val,old){
|
||||
var that = this;
|
||||
//文字颜色
|
||||
/* that.colorContainer = typeof val == 'object' ? val : [val,val];
|
||||
that.colorInfo = that.colorContainer[0]; */
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
onClickLeft () {
|
||||
if(this.back){
|
||||
if(getCurrentPages().length>1){
|
||||
uni.navigateBack();
|
||||
}else{
|
||||
// #ifdef H5
|
||||
history.back()
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
if(this.defaultBackUrl){
|
||||
uni.redirectTo({
|
||||
url:this.defaultBackUrl
|
||||
})
|
||||
}else{
|
||||
if(this.backTabbarUrl){
|
||||
uni.reLaunch({
|
||||
url: this.backTabbarUrl
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
}else{
|
||||
this.$emit('click-left')
|
||||
}
|
||||
},
|
||||
onClickRight () {
|
||||
this.$emit('click-right')
|
||||
},
|
||||
|
||||
//监听滚动后的操作
|
||||
doScroll(e){
|
||||
let that = this;
|
||||
that.$emit('scroll', e);
|
||||
if (e.scrollTop > 100) {
|
||||
that.slotSlidiSwitch = 1;
|
||||
} else {
|
||||
that.slotSlidiSwitch = 0
|
||||
}
|
||||
},
|
||||
//滑动渐变
|
||||
setTVAuto(e){
|
||||
let that = this;
|
||||
that.$emit('scroll', e);
|
||||
if (e.scrollTop > 100) {
|
||||
that.transparentValue = 1;
|
||||
that.colorInfo = that.colorContainer[1];
|
||||
} else {
|
||||
that.transparentValue = e.scrollTop / 100;
|
||||
that.colorInfo = that.colorContainer[0];
|
||||
}
|
||||
that.setBgColor();
|
||||
},
|
||||
//背景颜色
|
||||
setBgColor(){
|
||||
|
||||
var that = this;
|
||||
//如果存在背景图片则背景颜色失效
|
||||
// if(that.backgroundImg){
|
||||
// that.backgroundColorRgba = "url(" + that.backgroundImg + ")";
|
||||
// return;
|
||||
// }
|
||||
|
||||
//背景颜色
|
||||
if(typeof that.backgroundColor[0] == 'object'){
|
||||
let l = that.backgroundColor.length;
|
||||
if( l >= 2){
|
||||
let rgbStr = "linear-gradient("+ that.backgroundColorLinearDeg +"deg,";
|
||||
let c = null;
|
||||
for(var i in that.backgroundColor){
|
||||
c = that.backgroundColor[i];
|
||||
rgbStr += "rgba("+ c[0] + "," + c[1] + "," + c[2] +"," + that.transparentValue+")";
|
||||
|
||||
if(l != (i*1)+1){
|
||||
rgbStr += ",";
|
||||
}
|
||||
}
|
||||
rgbStr += ")";
|
||||
that.backgroundColorRgba = rgbStr;
|
||||
}
|
||||
|
||||
}else{
|
||||
let rgbStr = that.backgroundColor[0] + ','+ that.backgroundColor[1] + ','+ that.backgroundColor[2];
|
||||
that.backgroundColorRgb= 'rgb('+ rgbStr + ')';
|
||||
that.backgroundColorRgba = 'rgba('+ rgbStr +',' + that.transparentValue+')';
|
||||
}
|
||||
},
|
||||
setStatusBarFontColor(){
|
||||
var that = this;
|
||||
if(typeof that.statusBarFontColor == 'string'){
|
||||
that.statusBarFontColorInfo = [that.statusBarFontColor,that.statusBarFontColor];
|
||||
}else if(typeof that.statusBarFontColor == 'object'){
|
||||
if (that.statusBarFontColor.length==1){
|
||||
that.statusBarFontColorInfo = [that.statusBarFontColor[0],that.statusBarFontColor[0]];
|
||||
}else if(that.statusBarFontColor.length>=2){
|
||||
that.statusBarFontColorInfo = [that.statusBarFontColor[0],that.statusBarFontColor[1]];
|
||||
}
|
||||
}
|
||||
// #ifndef MP-TOUTIAO || H5
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: that.statusBarFontColorInfo[0],
|
||||
backgroundColor: that.backgroundColorRgb
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-TOUTIAO
|
||||
if (tt.setNavigationBarColor) {
|
||||
tt.setNavigationBarColor({
|
||||
frontColor: that.statusBarFontColorInfo[0],
|
||||
backgroundColor: that.backgroundColorRgb
|
||||
});
|
||||
} else {
|
||||
console.log("hx-navbar 提示:当前客户端版本过低,无法使用状态栏颜色修改功能,请升级(基础库1.40+)。")
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
},
|
||||
destroyed(){
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$nav-height: 44px;
|
||||
|
||||
.hd{
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
//防止其他ui影响
|
||||
// .hx-navbar uni-view,
|
||||
// .hx-navbar uni-scroll-view,
|
||||
// .hx-navbar uni-swiper,
|
||||
// .hx-navbar uni-button,
|
||||
// .hx-navbar uni-input,
|
||||
// .hx-navbar uni-textarea,
|
||||
// .hx-navbar uni-label,
|
||||
// .hx-navbar uni-navigator,
|
||||
// .hx-navbar uni-image {
|
||||
// box-sizing: unset;
|
||||
// }
|
||||
.hx-navbar {
|
||||
position: relative;
|
||||
padding-top: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&__content {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
/*background-color: $uni-bg-color*/;
|
||||
overflow: hidden;
|
||||
.navbgimg{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hx-navbar__content_view {
|
||||
// line-height: $nav-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
.hx-status-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
height: var(--status-bar-height);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: $nav-height;
|
||||
line-height: $nav-height;
|
||||
font-size: 36upx;
|
||||
transition: color 0.5s ease 0s;
|
||||
&-btns {
|
||||
display: inline-flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-shrink: 0;
|
||||
min-width: 30px;
|
||||
padding: 0 6px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
min-width: 54px;
|
||||
}
|
||||
|
||||
&-right:last-child{
|
||||
width: 120upx;
|
||||
text-align: right;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
width: 100%;
|
||||
margin: 0 10upx;
|
||||
|
||||
&-inner {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 36upx;
|
||||
|
||||
// padding-right: 60upx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__placeholder {
|
||||
&-view {
|
||||
height: $nav-height;
|
||||
}
|
||||
}
|
||||
|
||||
&--fixed {
|
||||
position: fixed;
|
||||
top:0;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
&--shadow {
|
||||
box-shadow: 0 2upx 12upx #ccc;
|
||||
}
|
||||
|
||||
&--border:after {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
content: '';
|
||||
-webkit-transform: scaleY(.5);
|
||||
transform: scaleY(.5);
|
||||
background-color: #efefef;
|
||||
}
|
||||
}
|
||||
.left_back{
|
||||
padding-left: 12upx;
|
||||
padding-right: 12upx;
|
||||
}
|
||||
.pink-dot{
|
||||
width:24rpx;
|
||||
height:24px;
|
||||
background:rgba(255,86,74,0.4);
|
||||
border-radius:50%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<view class="myimgDV">
|
||||
<view v-if="imgPicList.length>0" class='receiveimage flex'>
|
||||
<block v-for="(item,ind) in imgPicList" :key="ind">
|
||||
<image class="imgitem" :style="{width:imgwidth,height:imgheight,borderRadius:imgRadius+'rpx'}" :class="imgboxtype==0?'onepic':imgboxtype==1?'doublepic':imgboxtype==2?'triplepic':''"
|
||||
:src="item" :mode="imgboxtype==0?onepicMode:imgboxtype==1?'aspectFill':imgboxtype==2?'aspectFill':''" @click.stop="previewpic(item,imgPicList)"></image>
|
||||
<!-- {width:(imgboxtype==0?'':imgwidth),height:(imgboxtype==0?'':imgwidth),padding:(imgboxtype==0?'':imgpad)} -->
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
imgPicList: [],
|
||||
imgboxtype: 0,
|
||||
imgwidth: 0,
|
||||
imgpad: 0,
|
||||
imgheight: ''
|
||||
}
|
||||
},
|
||||
props: {
|
||||
imgList: {
|
||||
type: Array
|
||||
},
|
||||
num: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
imgRadius:{
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
pad: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
imgH: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
onepicMode:{
|
||||
type:String,
|
||||
default:'widthFix'
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
imgList:{
|
||||
handler(v){
|
||||
this.imgPicList = v;
|
||||
if (this.num == 1) {
|
||||
this.imgboxtype = 0;
|
||||
this.imgwidth = 100 + '%';
|
||||
} else if (this.num == 2 || this.num == 4) {
|
||||
this.imgboxtype = 1;
|
||||
this.imgwidth = 49 + '%';
|
||||
} else if (this.num == 3 || this.num > 4) {
|
||||
this.imgboxtype = 2;
|
||||
this.imgwidth = 32 + '%';
|
||||
}
|
||||
this.$nextTick(function() {
|
||||
this.getheight();
|
||||
})
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 图片预览
|
||||
previewpic(cind, clist) {
|
||||
uni.previewImage({
|
||||
urls: clist,
|
||||
current: cind,
|
||||
indicator: 'default'
|
||||
});
|
||||
},
|
||||
getheight() {
|
||||
let that = this;
|
||||
const query = uni.createSelectorQuery().in(that)
|
||||
query.select('.imgitem').boundingClientRect()
|
||||
query.exec(function(res) {
|
||||
if (that.num == 1) {
|
||||
if (that.imgH>0) {
|
||||
that.imgheight = that.imgH+'rpx';
|
||||
} else{
|
||||
that.imgheight = '100%';
|
||||
}
|
||||
|
||||
} else if (that.num == 2 || that.num == 4) {
|
||||
that.imgheight = (res[0].width).toFixed(2) + 'px';
|
||||
} else {
|
||||
that.imgheight = (res[0].width).toFixed(2) + 'px';
|
||||
}
|
||||
})
|
||||
|
||||
// const query = uni.createSelectorQuery().in(this).select('.imgitem');
|
||||
// uni.createSelectorQuery().select('.imgitem').boundingClientRect(res => {
|
||||
|
||||
// if (this.num == 1) {
|
||||
// this.imgheight = '100%';
|
||||
// } else if (this.num == 2 || this.num == 4) {
|
||||
// this.imgheight = (res.width).toFixed(2) + 'px';
|
||||
// } else {
|
||||
// this.imgheight = (res.width).toFixed(2) + 'px';
|
||||
// }
|
||||
// }).exec()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// let this = this;
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.myimgDV {
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.justify {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.receiveimage {
|
||||
margin-top: 28rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
.onepic {
|
||||
width: 100%;
|
||||
// height: 188rpx;
|
||||
// height: auto;
|
||||
margin-bottom: 28rpx;
|
||||
&:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.doublepic {
|
||||
// width: 340rpx;
|
||||
// height: 340rpx;
|
||||
margin-right: 1%;
|
||||
margin-bottom: 2%;
|
||||
&:nth-child(2n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.triplepic {
|
||||
// width: 222rpx;
|
||||
// height: 222rpx;
|
||||
margin-right: 2%;
|
||||
margin-bottom: 2%;
|
||||
&:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
# 轮播组件
|
||||
|
||||
# 基础式
|
||||
<ls-swiper :list="base_lsit" imgKey="imgUrl" :loop="true" :dots='true' :autoplay='true' @clickItem="clickItem()" />
|
||||
|
||||
# 链式
|
||||
<ls-swiper :list="list" imgKey="imgUrl" imgWidth="300" previousMargin="20" nextMargin='20'/>
|
||||
|
||||
# 卡片式
|
||||
<ls-swiper :list="base_lsit" imgKey="imgUrl" :crown="true" :loop="true" :shadow='true' height='130' previousMargin="120" nextMargin='120' imgRadius="5" />
|
||||
|
||||
# props
|
||||
参数名 | 说明 | 类型 | 默认值
|
||||
-----------------|-------------------------------------------------- -|-------------|------------
|
||||
list | 轮播数据 | Array | 必输
|
||||
imgKey | 轮播数据key(图片url属性) | String | 必输 (自定义模式,非必输)
|
||||
autoplay | 是否自动播放 | Boolean | false
|
||||
loop | 是否循环 | Boolean | false
|
||||
autoplay | 是否自动播放 | Boolean | false
|
||||
dots | 是否显示轮播点 | Boolean | false
|
||||
crown | 卡片特效 (中间突出,两边缩放特效) | Boolean | false
|
||||
interval | 播放时间间隔 | Number | 2000
|
||||
duration | 滑动速度 | Number | 1500
|
||||
bottom | 轮播点下边距 | Number | 10 (单位:px,设计图建议以375px为准)
|
||||
height | 高度 | Number | 200 (单位:px,设计图建议以375px为准)
|
||||
previousMargin | 图片前边距 | Number | 0 (单位:px,设计图建议以375px为准)
|
||||
nextMargin | 图片后间距 | Number | 0 (单位:px,设计图建议以375px为准)
|
||||
imgRadius | 图片圆角 | Number | 0 (单位:px,设计图建议以375px为准)
|
||||
imgWidth | 图片宽度 | String | 100%
|
||||
@clickItem | 点击事件 | Function |
|
||||
@change | 切换事件 | Function |
|
||||
|
||||
# 自定义
|
||||
<ls-swiper :list="list">
|
||||
<template v-slot="{data}">
|
||||
xxx
|
||||
</template>
|
||||
</ls-swiper>
|
||||
|
||||
如有其他需求,可在插槽内编写自定义内容, 自动覆盖原图片样式。
|
||||
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<view class="ls-wrap">
|
||||
<swiper class="ls-swiper" :style="{height: height *2 + 'rpx'}" :autoplay="autoplay" :interval="interval" :duration="duration"
|
||||
:circular='loop' @change='change' :previous-margin='previousMargin + "rpx"' :next-margin='nextMargin + "rpx"'>
|
||||
<swiper-item v-for="(item,index) in list" :key='index' @click="$emit('clickItem',item)">
|
||||
<view v-if="list && list.length>0" class="item" :class="[!crown ? '' : current==index ? 'crown-active':'crown']">
|
||||
<image v-if="!slotsMode" class="item-img" :class="[imgShadow?'imgShadow':'']" :src="item[imgKey]" :style="{ borderRadius: imgRadius + 'px',width:imgWidth}"
|
||||
mode=""></image>
|
||||
<slot v-else :data='item'></slot>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="dots flex" :style="{bottom: bottom * 2 + 'rpx'}" v-if="dots">
|
||||
<view class="dot" :class="[current == i ? 'curr-dot' : '']" v-for="(d,i) in list" :key='i'>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 轮播图片key
|
||||
imgKey: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 高度
|
||||
height: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
// 图片圆角
|
||||
imgRadius: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 图片阴影
|
||||
imgShadow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 前边距
|
||||
previousMargin: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 后边距
|
||||
nextMargin: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 图片宽度
|
||||
imgWidth: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
// 是否循环
|
||||
loop: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 自动播放
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 播放时间间隔
|
||||
interval: {
|
||||
type: Number,
|
||||
default: 2000
|
||||
},
|
||||
// 滑动速度
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 1200
|
||||
},
|
||||
// 显示指示点
|
||||
dots: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 轮播点下边距
|
||||
bottom: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
// 卡片特效
|
||||
crown: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
slotsMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
slots: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 判断异步数据源,是否使用插槽自定义样式
|
||||
list: {
|
||||
handler(val) {
|
||||
if (val.length > 0 && this.$slots.default) {
|
||||
this.slots = true
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change(event) {
|
||||
let current = event.detail.current
|
||||
this.current = current
|
||||
this.$emit('change', this.list[current])
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ls-wrap {
|
||||
position: relative;
|
||||
|
||||
.crown {
|
||||
transform: scale(0.93, 0.85);
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 100%;
|
||||
transition: 1.2s;
|
||||
}
|
||||
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.imgShadow {
|
||||
height: calc(100% - 10px);
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 6px 6px rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
.crown-active {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.dots {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.dot {
|
||||
width: 6rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #D6D6D6;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.curr-dot {
|
||||
height: 6rpx;
|
||||
width: 22rpx;
|
||||
border-radius: 6rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<view class="waterfalls-box" :style="{ height: height + 'px' }">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view
|
||||
v-for="(item, index) of list"
|
||||
class="waterfalls-list"
|
||||
:key="item[idKey]"
|
||||
:id="'waterfalls-list-id-' + item[idKey]"
|
||||
:ref="'waterfalls-list-id-' + item[idKey]"
|
||||
:style="{
|
||||
'--offset': offset + 'px',
|
||||
'--cols': cols,
|
||||
top: allPositionArr[index].top || 0,
|
||||
left: allPositionArr[index].left || 0,
|
||||
}"
|
||||
@click="$emit('wapper-lick', item)"
|
||||
>
|
||||
<image
|
||||
class="waterfalls-list-image"
|
||||
mode="widthFix"
|
||||
:class="{ single }"
|
||||
:style="imageStyle"
|
||||
:src="item[imageSrcKey] || ' '"
|
||||
@load="imageLoadHandle(index)"
|
||||
@error="imageLoadHandle(index)"
|
||||
@click="$emit('image-click', item)"
|
||||
/>
|
||||
<slot name="slot{{index}}" />
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<view
|
||||
v-for="(item, index) of list"
|
||||
class="waterfalls-list"
|
||||
:key="item[idKey]"
|
||||
:id="'waterfalls-list-id-' + item[idKey]"
|
||||
:ref="'waterfalls-list-id-' + item[idKey]"
|
||||
:style="{
|
||||
'--offset': offset + 'px',
|
||||
'--cols': cols,
|
||||
...listStyle,
|
||||
...(allPositionArr[index] || {}),
|
||||
}"
|
||||
@click="$emit('wapper-lick', item)"
|
||||
>
|
||||
<image
|
||||
class="waterfalls-list-image"
|
||||
:class="{ single }"
|
||||
mode="widthFix"
|
||||
:style="imageStyle"
|
||||
:src="item[imageSrcKey] || ' '"
|
||||
@load="imageLoadHandle(index)"
|
||||
@error="imageLoadHandle(index)"
|
||||
@click="$emit('image-click', item)"
|
||||
/>
|
||||
<slot v-bind="item" />
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
list: { type: Array, required: true },
|
||||
// offset 间距,单位为 px
|
||||
offset: { type: Number, default: 10 },
|
||||
// 列表渲染的 key 的键名,值必须唯一,默认为 id
|
||||
idKey: { type: String, default: "id" },
|
||||
// 图片 src 的键名
|
||||
imageSrcKey: { type: String, default: "image_url" },
|
||||
// 列数
|
||||
cols: { type: Number, default: 2, validator: (num) => num >= 2 },
|
||||
imageStyle: { type: Object },
|
||||
|
||||
// 是否是单独的渲染图片的样子,只控制图片圆角而已
|
||||
single: { type: Boolean, default: false },
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
listStyle: { type: Object },
|
||||
// #endif
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
topArr: [], // left, right 多个时依次表示第几列的数据
|
||||
allPositionArr: [], // 保存所有的位置信息
|
||||
allHeightArr: [], // 保存所有的 height 信息
|
||||
height: 0, // 外层包裹高度
|
||||
oldNum: 0,
|
||||
num: 0,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.refresh();
|
||||
},
|
||||
methods: {
|
||||
imageLoadHandle(index) {
|
||||
const id = "waterfalls-list-id-" + this.list[index][this.idKey],
|
||||
query = uni.createSelectorQuery().in(this);
|
||||
query
|
||||
.select("#" + id)
|
||||
.fields({ size: true }, (data) => {
|
||||
this.num++;
|
||||
this.$set(this.allHeightArr, index, data.height);
|
||||
if (this.num === this.list.length) {
|
||||
for (let i = this.oldNum; i < this.num; i++) {
|
||||
const getTopArrMsg = () => {
|
||||
let arrtmp = [...this.topArr].sort((a, b) => a - b);
|
||||
return {
|
||||
shorterIndex: this.topArr.indexOf(arrtmp[0]),
|
||||
shorterValue: arrtmp[0],
|
||||
longerIndex: this.topArr.indexOf(arrtmp[this.cols - 1]),
|
||||
longerValue: arrtmp[this.cols - 1],
|
||||
};
|
||||
};
|
||||
|
||||
const { shorterIndex, shorterValue } = getTopArrMsg();
|
||||
const position = {
|
||||
top: shorterValue + "px",
|
||||
left: (data.width + this.offset) * shorterIndex + "px",
|
||||
};
|
||||
this.$set(this.allPositionArr, i, position);
|
||||
this.topArr[shorterIndex] =
|
||||
shorterValue + this.allHeightArr[i] + this.offset;
|
||||
this.height = getTopArrMsg().longerValue - this.offset;
|
||||
}
|
||||
this.oldNum = this.num;
|
||||
// 完成渲染 emit `image-load` 事件
|
||||
this.$emit("image-load");
|
||||
}
|
||||
})
|
||||
.exec();
|
||||
},
|
||||
refresh() {
|
||||
let arr = [];
|
||||
for (let i = 0; i < this.cols; i++) {
|
||||
arr.push(0);
|
||||
}
|
||||
this.topArr = arr;
|
||||
this.num = 0;
|
||||
this.oldNum = 0;
|
||||
this.height = 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// 这里可以自行配置
|
||||
$border-radius: 6px;
|
||||
|
||||
.waterfalls-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.waterfalls-list {
|
||||
width: calc((100% - var(--offset) * (var(--cols) - 1)) / var(--cols));
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
border-radius: $border-radius;
|
||||
// 防止刚开始渲染时堆叠在第一幅图的地方
|
||||
left: calc(-50% - var(--offset));
|
||||
.waterfalls-list-image {
|
||||
width: 100%;
|
||||
will-change: transform;
|
||||
border-radius: $border-radius $border-radius 0 0;
|
||||
display: block;
|
||||
&.single {
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.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,91 @@
|
||||
<!--空布局
|
||||
|
||||
可作为独立的组件, 不使用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 }">
|
||||
<image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" />
|
||||
<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');
|
||||
console.log('empty');
|
||||
}
|
||||
}
|
||||
};
|
||||
</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: 1rpx solid #e04b28;
|
||||
border-radius: 60rpx;
|
||||
color: #e04b28;
|
||||
}
|
||||
|
||||
.mescroll-empty .empty-btn:active {
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<!-- 回到顶部的按钮 -->
|
||||
<template>
|
||||
<image
|
||||
v-if="mOption.src"
|
||||
class="mescroll-totop"
|
||||
:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-safe-bottom': 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 */
|
||||
.mescroll-safe-bottom{
|
||||
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,46 @@
|
||||
/* 上拉加载区域 */
|
||||
.mescroll-upwarp {
|
||||
min-height: 60rpx;
|
||||
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':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
|
||||
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
|
||||
<view v-show="isUpLoading">
|
||||
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.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,10 @@
|
||||
page {
|
||||
-webkit-overflow-scrolling: touch; /* 使iOS滚动流畅 */
|
||||
}
|
||||
|
||||
.mescroll-body {
|
||||
position: relative; /* 下拉刷新区域相对自身定位 */
|
||||
height: auto; /* 不可固定高度,否则overflow: hidden, 可通过设置最小高度使列表不满屏仍可下拉*/
|
||||
overflow: hidden; /* 遮住顶部下拉刷新区域 */
|
||||
box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<template>
|
||||
<view class="mescroll-body" :style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom, 'padding-bottom': padBottomConstant, 'padding-bottom': padBottomEnv }" @touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent" @touchcancel="touchendEvent" >
|
||||
<view class="mescroll-body-content" :style="{ transform: translateY, transition: transition }">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view class="downwarp-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" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view v-if="mescroll.optUp.use && !isDownLoading" class="mescroll-upwarp" :style="{'background-color':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>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<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';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollEmpty,
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: {optDown:{},optUp:{}}, // mescroll实例
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
|
||||
downLoadType: 4, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
statusBarHeight: 0, // 状态栏高度
|
||||
isSafearea: false // 支持安全区
|
||||
};
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: Boolean, // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可自动加上状态栏高度的偏移量)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
height: [String, Number] // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
},
|
||||
computed: {
|
||||
// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
|
||||
minHeight(){
|
||||
return this.toPx(this.height || '100%') + 'px'
|
||||
},
|
||||
// 下拉布局往下偏移的距离 (px)
|
||||
numTop() {
|
||||
return this.toPx(this.top) + (this.topbar ? this.statusBarHeight : 0);
|
||||
},
|
||||
padTop() {
|
||||
return this.numTop + 'px';
|
||||
},
|
||||
// 上拉布局往上偏移 (px)
|
||||
numBottom() {
|
||||
return this.toPx(this.bottom);
|
||||
},
|
||||
padBottom() {
|
||||
return this.numBottom + 'px';
|
||||
},
|
||||
padBottomConstant() {
|
||||
return this.isSafearea ? 'calc(' + this.padBottom + ' + constant(safe-area-inset-bottom))' : this.padBottom;
|
||||
},
|
||||
padBottomEnv() {
|
||||
return this.isSafearea ? 'calc(' + this.padBottom + ' + env(safe-area-inset-bottom))' : this.padBottom;
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
isDownReset() {
|
||||
return this.downLoadType === 3 || this.downLoadType === 4;
|
||||
},
|
||||
// 过渡
|
||||
transition() {
|
||||
return this.isDownReset ? 'transform 300ms' : this.downTransition;
|
||||
},
|
||||
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(){
|
||||
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;
|
||||
},
|
||||
//注册列表touchstart事件,用于下拉刷新
|
||||
touchstartEvent(e) {
|
||||
this.mescroll.touchstartEvent(e);
|
||||
},
|
||||
//注册列表touchmove事件,用于下拉刷新
|
||||
touchmoveEvent(e) {
|
||||
this.mescroll.touchmoveEvent(e);
|
||||
},
|
||||
//注册列表touchend事件,用于下拉刷新
|
||||
touchendEvent(e) {
|
||||
this.mescroll.touchendEvent(e);
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
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(mescroll) {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset(mescroll) {
|
||||
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(mescroll) {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll);
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll() {
|
||||
vm.upLoadType = 0;
|
||||
},
|
||||
// 空布局
|
||||
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.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
// mescroll-body在Android小程序下拉会卡顿,无法像mescroll-uni那样通过设置"disableScroll":true解决,只能用动画过渡缓解
|
||||
// #ifdef MP
|
||||
if(sys.platform == "android") vm.downTransition = 'transform 200ms'
|
||||
// #endif
|
||||
|
||||
// 因为使用的是page的scroll,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
uni.pageScrollTo({
|
||||
scrollTop: y,
|
||||
duration: t
|
||||
})
|
||||
});
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
if(sys.platform == "ios"){
|
||||
vm.isSafearea = vm.safearea;
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
}else{
|
||||
vm.isSafearea = false
|
||||
vm.mescroll.optUp.toTop.safearea = false
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "./mescroll-body.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
@import './components/mescroll-up.css';
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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
|
||||
}
|
||||
},
|
||||
// 下拉刷新的回调
|
||||
downCallback() {
|
||||
// mixin默认resetUpScroll
|
||||
this.mescroll.resetUpScroll()
|
||||
},
|
||||
// 上拉加载的回调
|
||||
upCallback() {
|
||||
// mixin默认延时500自动结束加载
|
||||
setTimeout(()=>{
|
||||
this.mescroll.endErr();
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default MescrollMixin;
|
||||
@@ -0,0 +1,36 @@
|
||||
// 全局配置
|
||||
// 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: '-- 没有更多了 --', // 没有更多数据的提示文本
|
||||
offset: 80, // 距底部多远时,触发upCallback
|
||||
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
|
||||
toTop: {
|
||||
// 回到顶部按钮,需配置src才显示
|
||||
// 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
|
||||
src: "https://www.xdd618.com/api/file/pic/20230304101324538100.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, // 是否显示空布局
|
||||
// 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
|
||||
icon: "/static/images/img_nodata.png",
|
||||
tip: '~ 暂无相关数据 ~' // 提示
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GlobalOption
|
||||
@@ -0,0 +1,29 @@
|
||||
page {
|
||||
height: 100%;
|
||||
box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
|
||||
}
|
||||
|
||||
.mescroll-uni-warp{
|
||||
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生效 */
|
||||
}
|
||||
@@ -0,0 +1,862 @@
|
||||
/* mescroll
|
||||
* version 1.2.5
|
||||
* 2020-03-15 wenju
|
||||
* http://www.mescroll.com
|
||||
*/
|
||||
|
||||
export default function MeScroll(options, isScrollBody) {
|
||||
let me = this;
|
||||
me.version = '1.2.5'; // 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.auto && hasDownCallback) {
|
||||
if (me.optDown.autoShowLoading) {
|
||||
me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
|
||||
} else {
|
||||
me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
|
||||
}
|
||||
}
|
||||
// 自动触发上拉加载
|
||||
setTimeout(function(){ // 延时确保先执行down的callback,再执行up的callback,因为部分小程序emit是异步,会导致isUpAutoLoad判断有误
|
||||
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, 此值用于控制最大的误差
|
||||
fps: 80, // 下拉节流 (值越大每秒刷新频率越高)
|
||||
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, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
|
||||
endDownScroll: null, // 结束下拉刷新的回调
|
||||
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,两者可同时触发;
|
||||
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
|
||||
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.lastPoint = this.startPoint; // 重置上次move的点
|
||||
this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
|
||||
this.inTouchend = false; // 标记不是touchend
|
||||
}
|
||||
|
||||
/* 列表touchmove事件 */
|
||||
MeScroll.prototype.touchmoveEvent = function(e) {
|
||||
// #ifdef H5
|
||||
window.isPreventDefault = false // 标记不需要阻止window事件
|
||||
// #endif
|
||||
|
||||
if (!this.optDown.use) return;
|
||||
if (!this.startPoint) return;
|
||||
let me = this;
|
||||
|
||||
// 节流
|
||||
let t = new Date().getTime();
|
||||
if (me.moveTime && t - me.moveTime < me.moveTimeDiff) { // 小于节流时间,则不处理
|
||||
return;
|
||||
} else {
|
||||
me.moveTime = t
|
||||
if(!me.moveTimeDiff) me.moveTimeDiff = 1000 / me.optDown.fps
|
||||
}
|
||||
|
||||
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; 在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))) {
|
||||
|
||||
// 下拉的角度是否在配置的范围内
|
||||
let angle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
|
||||
if (angle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
|
||||
|
||||
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
|
||||
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
|
||||
me.inTouchend = true; // 标记执行touchend
|
||||
me.touchendEvent(); // 提前触发touchend
|
||||
return;
|
||||
}
|
||||
|
||||
// #ifdef H5
|
||||
window.isPreventDefault = true // 标记阻止window事件
|
||||
// #endif
|
||||
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 += Math.round(diff * me.optDown.outOffsetRate); // 越往下,高度变化越小
|
||||
} else { // 向上收
|
||||
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
|
||||
}
|
||||
}
|
||||
|
||||
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.optDown.endDownScroll && this.optDown.endDownScroll(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.callback && this.optDown.callback(this); // 执行回调,联网加载数据
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示下拉进度布局 */
|
||||
MeScroll.prototype.showDownScroll = function() {
|
||||
this.isDownScrolling = true; // 标记下拉中
|
||||
if (this.optDown.native) {
|
||||
uni.startPullDownRefresh(); // 系统自带的下拉刷新
|
||||
this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到
|
||||
} else{
|
||||
this.downHight = this.optDown.offset; // 更新下拉区域高度
|
||||
this.optDown.showLoading && this.optDown.showLoading(this, this.downHight); // 下拉刷新中...
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示系统自带的下拉刷新时需要处理的业务 */
|
||||
MeScroll.prototype.onPullDownRefresh = function() {
|
||||
this.isDownScrolling = true; // 标记下拉中
|
||||
this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到
|
||||
this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
|
||||
}
|
||||
|
||||
/* 结束下拉刷新 */
|
||||
MeScroll.prototype.endDownScroll = function() {
|
||||
if (this.optDown.native) { // 结束原生下拉刷新
|
||||
this.isDownScrolling = false;
|
||||
this.optDown.endDownScroll && this.optDown.endDownScroll(this);
|
||||
uni.stopPullDownRefresh();
|
||||
return
|
||||
}
|
||||
let me = this;
|
||||
// 结束下拉刷新的方法
|
||||
let endScroll = function() {
|
||||
me.downHight = 0;
|
||||
me.isDownScrolling = false;
|
||||
me.optDown.endDownScroll && me.optDown.endDownScroll(me);
|
||||
!me.isScrollBody && me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
|
||||
}
|
||||
// 结束下拉刷新时的回调
|
||||
let delay = 0;
|
||||
if (me.optDown.afterLoading) delay = me.optDown.afterLoading(me); // 结束下拉刷新的延时,单位ms
|
||||
if (typeof delay === 'number' && delay > 0) {
|
||||
setTimeout(endScroll, delay);
|
||||
} else {
|
||||
endScroll();
|
||||
}
|
||||
}
|
||||
|
||||
/* 锁定下拉刷新: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.isBounce) me.setBounce(false); // 不允许bounce时,需禁止window的touchmove事件
|
||||
|
||||
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
|
||||
// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止
|
||||
// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
|
||||
if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
|
||||
}
|
||||
|
||||
/* 是否允许下拉回弹(橡皮筋效果); true或null为允许; false禁止bounce */
|
||||
MeScroll.prototype.setBounce = function(isBounce) {
|
||||
// #ifdef H5
|
||||
if (isBounce === false) {
|
||||
this.optUp.isBounce = false; // 禁止
|
||||
// 标记当前页使用了mescroll (需延时,确保page已切换)
|
||||
setTimeout(function() {
|
||||
let uniPageDom = document.getElementsByTagName('uni-page')[0];
|
||||
uniPageDom && uniPageDom.setAttribute('use_mescroll', true)
|
||||
}, 30);
|
||||
// 避免重复添加事件
|
||||
if (window.isSetBounce) return;
|
||||
window.isSetBounce = true;
|
||||
// 需禁止window的touchmove事件才能有效的阻止bounce
|
||||
window.bounceTouchmove = function(e) {
|
||||
if(!window.isPreventDefault) return; // 根据标记判断是否阻止
|
||||
|
||||
let el = e.target;
|
||||
// 当前touch的元素及父元素是否要拦截touchmove事件
|
||||
let isPrevent = true;
|
||||
while (el !== document.body && el !== document) {
|
||||
if (el.tagName === 'UNI-PAGE') { // 只扫描当前页
|
||||
if (!el.getAttribute('use_mescroll')) {
|
||||
isPrevent = false; // 如果当前页没有使用mescroll,则不阻止
|
||||
}
|
||||
break;
|
||||
}
|
||||
let cls = el.classList;
|
||||
if (cls) {
|
||||
if (cls.contains('mescroll-touch')) { // 采用scroll-view 此处不能过滤mescroll-uni,否则下拉仍然有回弹
|
||||
isPrevent = false; // mescroll-touch无需拦截touchmove事件
|
||||
break;
|
||||
} else if (cls.contains('mescroll-touch-x') || cls.contains('mescroll-touch-y')) {
|
||||
// 如果配置了水平或者垂直滑动
|
||||
let curX = e.touches ? e.touches[0].pageX : e.clientX; // 当前第一个手指距离列表顶部的距离x
|
||||
let curY = e.touches ? e.touches[0].pageY : e.clientY; // 当前第一个手指距离列表顶部的距离y
|
||||
if (!this.preWinX) this.preWinX = curX; // 设置上次移动的距离x
|
||||
if (!this.preWinY) this.preWinY = curY; // 设置上次移动的距离y
|
||||
// 计算两点之间的角度
|
||||
let x = Math.abs(this.preWinX - curX);
|
||||
let y = Math.abs(this.preWinY - curY);
|
||||
let z = Math.sqrt(x * x + y * y);
|
||||
this.preWinX = curX; // 记录本次curX的值
|
||||
this.preWinY = curY; // 记录本次curY的值
|
||||
if (z !== 0) {
|
||||
let angle = Math.asin(y / z) / Math.PI * 180; // 角度区间 [0,90]
|
||||
if ((angle <= 45 && cls.contains('mescroll-touch-x')) || (angle > 45 && cls.contains('mescroll-touch-y'))) {
|
||||
isPrevent = false; // 水平滑动或者垂直滑动,不拦截touchmove事件
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
el = el.parentNode; // 继续检查其父元素
|
||||
}
|
||||
// 拦截touchmove事件:是否可以被禁用&&是否已经被禁用 (这里不使用me.preventDefault(e)的方法,因为某些情况下会报找不到方法的异常)
|
||||
if (isPrevent && e.cancelable && !e.defaultPrevented && typeof e.preventDefault === "function") e.preventDefault();
|
||||
}
|
||||
window.addEventListener('touchmove', window.bounceTouchmove, {
|
||||
passive: false
|
||||
});
|
||||
} else {
|
||||
this.optUp.isBounce = true; // 允许
|
||||
if (window.bounceTouchmove) {
|
||||
window.removeEventListener('touchmove', window.bounceTouchmove);
|
||||
window.bounceTouchmove = null;
|
||||
window.isSetBounce = false;
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
<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,'padding-bottom':padBottomConstant,'padding-bottom':padBottomEnv,'top':fixedTop,'bottom':fixedBottom,'bottom':fixedBottomConstant,'bottom':fixedBottomEnv}" :scroll-top="scrollTop" :scroll-into-view="scrollToViewId" :scroll-with-animation="scrollAnim" @scroll="scroll" @touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent" @touchcancel="touchendEvent" :scroll-y='isDownReset' :enable-back-to-top="true">
|
||||
<view class="mescroll-uni-content" :style="{'transform': translateY, 'transition': transition}">
|
||||
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
|
||||
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
|
||||
<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
|
||||
<view class="downwarp-content">
|
||||
<view class="downwarp-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" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
|
||||
<view v-if="mescroll.optUp.use && !isDownLoading" class="mescroll-upwarp" :style="{'background-color':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>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
|
||||
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<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';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollEmpty,
|
||||
MescrollTop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mescroll: {optDown:{},optUp:{}}, // mescroll实例
|
||||
viewId: 'id_' + Math.random().toString(36).substr(2), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
|
||||
downLoadType: 4, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
scrollTop: 0, // 滚动条的位置
|
||||
scrollAnim: false, // 是否开启滚动动画
|
||||
windowTop: 0, // 可使用窗口的顶部位置
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
statusBarHeight: 0, // 状态栏高度
|
||||
isSafearea: false, // 支持安全区
|
||||
scrollToViewId: '' // 滚动到指定view的id
|
||||
}
|
||||
},
|
||||
props: {
|
||||
down: Object, // 下拉刷新的参数配置
|
||||
up: Object, // 上拉加载的参数配置
|
||||
top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
topbar: Boolean, // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可自动加上状态栏高度的偏移量)
|
||||
bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
|
||||
fixed: { // 是否通过fixed固定mescroll的高度, 默认true
|
||||
type: Boolean,
|
||||
default () {
|
||||
return true
|
||||
}
|
||||
},
|
||||
height: [String, Number] // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
|
||||
},
|
||||
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) + (this.topbar ? this.statusBarHeight : 0)
|
||||
},
|
||||
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
|
||||
},
|
||||
fixedBottomConstant(){
|
||||
return this.isSafearea ? "calc("+this.fixedBottom+" + constant(safe-area-inset-bottom))" : this.fixedBottom
|
||||
},
|
||||
fixedBottomEnv(){
|
||||
return this.isSafearea ? "calc("+this.fixedBottom+" + env(safe-area-inset-bottom))" : this.fixedBottom
|
||||
},
|
||||
padBottom() {
|
||||
return !this.isFixed ? this.numBottom + 'px' : 0
|
||||
},
|
||||
padBottomConstant(){
|
||||
return this.isSafearea ? "calc("+this.padBottom+" + constant(safe-area-inset-bottom))" : this.padBottom
|
||||
},
|
||||
padBottomEnv(){
|
||||
return this.isSafearea ? "calc("+this.padBottom+" + env(safe-area-inset-bottom))" : this.padBottom
|
||||
},
|
||||
// 是否为重置下拉的状态
|
||||
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(){
|
||||
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获取是否向上滑动
|
||||
})
|
||||
},
|
||||
//注册列表touchstart事件,用于下拉刷新
|
||||
touchstartEvent(e) {
|
||||
this.mescroll.touchstartEvent(e);
|
||||
},
|
||||
//注册列表touchmove事件,用于下拉刷新
|
||||
touchmoveEvent(e) {
|
||||
this.mescroll.touchmoveEvent(e);
|
||||
},
|
||||
//注册列表touchend事件,用于下拉刷新
|
||||
touchendEvent(e) {
|
||||
this.mescroll.touchendEvent(e);
|
||||
},
|
||||
// 点击空布局的按钮回调
|
||||
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 view = uni.createSelectorQuery().in(this).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(mescroll) {
|
||||
vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
outOffset(mescroll) {
|
||||
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(mescroll) {
|
||||
vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
|
||||
vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
|
||||
},
|
||||
// 派发下拉刷新的回调
|
||||
callback: function(mescroll) {
|
||||
vm.$emit('down', mescroll)
|
||||
}
|
||||
},
|
||||
// 上拉加载的配置
|
||||
up: {
|
||||
// 显示加载中的回调
|
||||
showLoading() {
|
||||
vm.upLoadType = 1;
|
||||
},
|
||||
// 显示无更多数据的回调
|
||||
showNoMore() {
|
||||
vm.upLoadType = 2;
|
||||
},
|
||||
// 隐藏上拉加载的回调
|
||||
hideUpScroll() {
|
||||
vm.upLoadType = 0;
|
||||
},
|
||||
// 空布局
|
||||
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
|
||||
vm.scrollToViewId = y;
|
||||
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(sys.platform == "ios"){
|
||||
vm.isSafearea = vm.safearea;
|
||||
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
|
||||
vm.mescroll.optUp.toTop.safearea = vm.safearea;
|
||||
}
|
||||
}else{
|
||||
vm.isSafearea = false
|
||||
vm.mescroll.optUp.toTop.safearea = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 设置容器的高度
|
||||
this.setClientHeight()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "./mescroll-uni.css";
|
||||
@import "./components/mescroll-down.css";
|
||||
@import './components/mescroll-up.css';
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
|
||||
* 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
|
||||
* 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
|
||||
*/
|
||||
const MescrollCompMixin = {
|
||||
// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
|
||||
onPageScroll(e) {
|
||||
let item = this.$refs["mescrollItem"];
|
||||
if(item && item.mescroll) item.mescroll.onPageScroll(e);
|
||||
},
|
||||
onReachBottom() {
|
||||
let item = this.$refs["mescrollItem"];
|
||||
if(item && item.mescroll) item.mescroll.onReachBottom();
|
||||
},
|
||||
// 当down的native: true时, 还需传递此方法进到子组件
|
||||
onPullDownRefresh(){
|
||||
let item = this.$refs["mescrollItem"];
|
||||
if(item && item.mescroll) item.mescroll.onPullDownRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
export default MescrollCompMixin;
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
|
||||
*/
|
||||
const MescrollMoreItemMixin = {
|
||||
props:{
|
||||
i: Number, // 每个tab页的专属下标
|
||||
index: { // 当前tab的下标
|
||||
type: Number,
|
||||
default(){
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
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对象
|
||||
mescrollInit(mescroll) {
|
||||
this.mescroll = mescroll;
|
||||
this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序 (mescroll-mixins.js)
|
||||
// 自动加载当前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;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
/* eslint-disable */
|
||||
var provinceData = [{
|
||||
"label": "北京市",
|
||||
"value": "11"
|
||||
},
|
||||
{
|
||||
"label": "天津市",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"label": "河北省",
|
||||
"value": "13"
|
||||
},
|
||||
{
|
||||
"label": "山西省",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"label": "内蒙古自治区",
|
||||
"value": "15"
|
||||
},
|
||||
{
|
||||
"label": "辽宁省",
|
||||
"value": "21"
|
||||
},
|
||||
{
|
||||
"label": "吉林省",
|
||||
"value": "22"
|
||||
},
|
||||
{
|
||||
"label": "黑龙江省",
|
||||
"value": "23"
|
||||
},
|
||||
{
|
||||
"label": "上海市",
|
||||
"value": "31"
|
||||
},
|
||||
{
|
||||
"label": "江苏省",
|
||||
"value": "32"
|
||||
},
|
||||
{
|
||||
"label": "浙江省",
|
||||
"value": "33"
|
||||
},
|
||||
{
|
||||
"label": "安徽省",
|
||||
"value": "34"
|
||||
},
|
||||
{
|
||||
"label": "福建省",
|
||||
"value": "35"
|
||||
},
|
||||
{
|
||||
"label": "江西省",
|
||||
"value": "36"
|
||||
},
|
||||
{
|
||||
"label": "山东省",
|
||||
"value": "37"
|
||||
},
|
||||
{
|
||||
"label": "河南省",
|
||||
"value": "41"
|
||||
},
|
||||
{
|
||||
"label": "湖北省",
|
||||
"value": "42"
|
||||
},
|
||||
{
|
||||
"label": "湖南省",
|
||||
"value": "43"
|
||||
},
|
||||
{
|
||||
"label": "广东省",
|
||||
"value": "44"
|
||||
},
|
||||
{
|
||||
"label": "广西壮族自治区",
|
||||
"value": "45"
|
||||
},
|
||||
{
|
||||
"label": "海南省",
|
||||
"value": "46"
|
||||
},
|
||||
{
|
||||
"label": "重庆市",
|
||||
"value": "50"
|
||||
},
|
||||
{
|
||||
"label": "四川省",
|
||||
"value": "51"
|
||||
},
|
||||
{
|
||||
"label": "贵州省",
|
||||
"value": "52"
|
||||
},
|
||||
{
|
||||
"label": "云南省",
|
||||
"value": "53"
|
||||
},
|
||||
{
|
||||
"label": "西藏自治区",
|
||||
"value": "54"
|
||||
},
|
||||
{
|
||||
"label": "陕西省",
|
||||
"value": "61"
|
||||
},
|
||||
{
|
||||
"label": "甘肃省",
|
||||
"value": "62"
|
||||
},
|
||||
{
|
||||
"label": "青海省",
|
||||
"value": "63"
|
||||
},
|
||||
{
|
||||
"label": "宁夏回族自治区",
|
||||
"value": "64"
|
||||
},
|
||||
{
|
||||
"label": "新疆维吾尔自治区",
|
||||
"value": "65"
|
||||
},
|
||||
{
|
||||
"label": "台湾",
|
||||
"value": "66"
|
||||
},
|
||||
{
|
||||
"label": "香港",
|
||||
"value": "67"
|
||||
},
|
||||
{
|
||||
"label": "澳门",
|
||||
"value": "68"
|
||||
},
|
||||
{
|
||||
"label": "钓鱼岛",
|
||||
"value": "69"
|
||||
}
|
||||
]
|
||||
export default provinceData;
|
||||
@@ -0,0 +1,381 @@
|
||||
<template>
|
||||
<view class="simple-address" v-if="showPopup" @touchmove.stop.prevent="clear">
|
||||
<!-- 遮罩层 -->
|
||||
<view
|
||||
class="simple-address-mask"
|
||||
@touchmove.stop.prevent="clear"
|
||||
v-if="maskClick"
|
||||
:class="[ani + '-mask', animation ? 'mask-ani' : '']"
|
||||
:style="{
|
||||
'background-color': maskBgColor
|
||||
}"
|
||||
@tap="hideMask(true)"
|
||||
></view>
|
||||
|
||||
<view class="simple-address-content simple-address--fixed" :class="[type, ani + '-content', animation ? 'content-ani' : '']">
|
||||
<view class="simple-address__header">
|
||||
<view class="simple-address__header-btn-box" @click="pickerCancel"><text class="simple-address__header-text">取消</text></view>
|
||||
<view class="simple-address__header-btn-box" @click="pickerConfirm"><text class="simple-address__header-text" :style="{ color: themeColor }">确定</text></view>
|
||||
</view>
|
||||
<view class="simple-address__box">
|
||||
<picker-view indicator-style="height: 70rpx;" class="simple-address-view" :value="pickerValue" @change="pickerChange">
|
||||
<picker-view-column>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view class="picker-item" v-for="(item, index) in provinceDataList" :key="index">{{ item.label }}</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<text class="picker-item" v-for="(item, index) in provinceDataList" :key="index">{{ item.label }}</text>
|
||||
<!-- #endif -->
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view class="picker-item" v-for="(item, index) in cityDataList" :key="index">{{ item.label }}</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<text class="picker-item" v-for="(item, index) in cityDataList" :key="index">{{ item.label }}</text>
|
||||
<!-- #endif -->
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view class="picker-item" v-for="(item, index) in areaDataList" :key="index">{{ item.label }}</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<text class="picker-item" v-for="(item, index) in areaDataList" :key="index">{{ item.label }}</text>
|
||||
<!-- #endif -->
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import provinceData from './city-data/province.js';
|
||||
import cityData from './city-data/city.js';
|
||||
import areaData from './city-data/area.js';
|
||||
export default {
|
||||
name: 'simpleAddress',
|
||||
props: {
|
||||
mode: {
|
||||
// 地址类型
|
||||
// default 则代表老版本根据index索引获取数据
|
||||
//
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
// 开启动画
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/* 弹出层类型,可选值;
|
||||
bottom:底部弹出层
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
// maskClick
|
||||
maskClick: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maskBgColor: {
|
||||
type: String,
|
||||
default: 'rgba(0, 0, 0, 0.4)' //背景颜色 rgba(0, 0, 0, 0.4) 为空则调用 uni.scss
|
||||
},
|
||||
themeColor: {
|
||||
type: String,
|
||||
default: '' // 主题色
|
||||
},
|
||||
/* 默认值 */
|
||||
pickerValueDefault: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [0, 0, 0];
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ani: '',
|
||||
showPopup: false,
|
||||
pickerValue: [0, 0, 0],
|
||||
provinceDataList: [],
|
||||
cityDataList: [],
|
||||
areaDataList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show(newValue) {
|
||||
if (newValue) {
|
||||
this.open();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
pickerValueDefault() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.handPickValueDefault(); // 对 pickerValueDefault 做兼容处理
|
||||
this.provinceDataList = provinceData;
|
||||
this.cityDataList = cityData[this.pickerValueDefault[0]];
|
||||
this.areaDataList = areaData[this.pickerValueDefault[0]][this.pickerValueDefault[1]];
|
||||
this.pickerValue = this.pickerValueDefault;
|
||||
},
|
||||
handPickValueDefault() {
|
||||
if (this.pickerValueDefault !== [0, 0, 0]) {
|
||||
if (this.pickerValueDefault[0] > provinceData.length - 1) {
|
||||
this.pickerValueDefault[0] = provinceData.length - 1;
|
||||
}
|
||||
if (this.pickerValueDefault[1] > cityData[this.pickerValueDefault[0]].length - 1) {
|
||||
this.pickerValueDefault[1] = cityData[this.pickerValueDefault[0]].length - 1;
|
||||
}
|
||||
if (this.pickerValueDefault[2] > areaData[this.pickerValueDefault[0]][this.pickerValueDefault[1]].length - 1) {
|
||||
this.pickerValueDefault[2] = areaData[this.pickerValueDefault[0]][this.pickerValueDefault[1]].length - 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
pickerChange(e) {
|
||||
let changePickerValue = e.detail.value;
|
||||
if (this.pickerValue[0] !== changePickerValue[0]) {
|
||||
// 第一级发生滚动
|
||||
this.cityDataList = cityData[changePickerValue[0]];
|
||||
this.areaDataList = areaData[changePickerValue[0]][0];
|
||||
changePickerValue[1] = 0;
|
||||
changePickerValue[2] = 0;
|
||||
} else if (this.pickerValue[1] !== changePickerValue[1]) {
|
||||
// 第二级滚动
|
||||
this.areaDataList = areaData[changePickerValue[0]][changePickerValue[1]];
|
||||
changePickerValue[2] = 0;
|
||||
}
|
||||
this.pickerValue = changePickerValue;
|
||||
this._$emit('onChange');
|
||||
},
|
||||
_$emit(emitName) {
|
||||
let pickObj = {
|
||||
label: this._getLabel(),
|
||||
value: this.pickerValue,
|
||||
cityCode: this._getCityCode(),
|
||||
areaCode: this._getAreaCode(),
|
||||
provinceCode: this._getProvinceCode(),
|
||||
labelArr:this._getLabel().split('-')
|
||||
};
|
||||
console.log(this._getLabel().split('-'));
|
||||
this.$emit(emitName, pickObj);
|
||||
},
|
||||
_getLabel() {
|
||||
let pcikerLabel =
|
||||
this.provinceDataList[this.pickerValue[0]].label + '-' + this.cityDataList[this.pickerValue[1]].label + '-' + this.areaDataList[this.pickerValue[2]].label;
|
||||
return pcikerLabel;
|
||||
},
|
||||
_getCityCode() {
|
||||
return this.cityDataList[this.pickerValue[1]].value;
|
||||
},
|
||||
_getProvinceCode() {
|
||||
return this.provinceDataList[this.pickerValue[0]].value;
|
||||
},
|
||||
_getAreaCode() {
|
||||
return this.areaDataList[this.pickerValue[2]].value;
|
||||
},
|
||||
queryIndex(params = [], type = 'value') {
|
||||
// params = [ 11 ,1101,110101 ];
|
||||
// 1.获取省份的index
|
||||
let provinceIndex = provinceData.findIndex(res => res[type] == params[0]);
|
||||
let cityIndex = cityData[provinceIndex].findIndex(res => res[type] == params[1]);
|
||||
let areaIndex = areaData[provinceIndex][cityIndex].findIndex(res => res[type] == params[2]);
|
||||
return {
|
||||
index: [provinceIndex, cityIndex, areaIndex],
|
||||
data: {
|
||||
province: provinceData[provinceIndex],
|
||||
city: cityData[provinceIndex][cityIndex],
|
||||
area: areaData[cityIndex][0][areaIndex]
|
||||
}
|
||||
};
|
||||
},
|
||||
clear() {},
|
||||
hideMask() {
|
||||
this._$emit('onCancel');
|
||||
this.close();
|
||||
},
|
||||
pickerCancel() {
|
||||
this._$emit('onCancel');
|
||||
this.close();
|
||||
},
|
||||
pickerConfirm() {
|
||||
this._$emit('onConfirm');
|
||||
this.close();
|
||||
},
|
||||
open() {
|
||||
this.showPopup = true;
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.ani = 'simple-' + this.type;
|
||||
}, 100);
|
||||
});
|
||||
},
|
||||
close(type) {
|
||||
if (!this.maskClick && type) return;
|
||||
this.ani = '';
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.showPopup = false;
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.simple-address {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.simple-address-mask {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
transition-property: opacity;
|
||||
transition-duration: 0.3s;
|
||||
opacity: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.mask-ani {
|
||||
transition-property: opacity;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
.simple-bottom-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.simple-center-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.simple-address--fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
transition-property: transform;
|
||||
transition-duration: 0.3s;
|
||||
transform: translateY(460rpx);
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.simple-address-content {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.simple-content-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
transform: translateY(500rpx);
|
||||
}
|
||||
|
||||
.content-ani {
|
||||
transition-property: transform, opacity;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
.simple-bottom-content {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.simple-center-content {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.simple-address__header {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
border-bottom-color: #f2f2f2;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1rpx;
|
||||
}
|
||||
|
||||
.simple-address--fixed-top {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
border-top-color: $uni-border-color;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1rpx;
|
||||
}
|
||||
|
||||
.simple-address__header-btn-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 70rpx;
|
||||
}
|
||||
|
||||
.simple-address__header-text {
|
||||
text-align: center;
|
||||
font-size: $uni-font-size-base;
|
||||
color: #1aad19;
|
||||
line-height: 70rpx;
|
||||
padding-left: 40rpx;
|
||||
padding-right: 40rpx;
|
||||
}
|
||||
|
||||
.simple-address__box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.simple-address-view {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
width: 750rpx;
|
||||
/* #endif */
|
||||
height: 408rpx;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.picker-item {
|
||||
text-align: center;
|
||||
line-height: 70rpx;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
export default {
|
||||
created() {
|
||||
if (this.type === 'message') {
|
||||
// 不显示遮罩
|
||||
this.maskShow = false
|
||||
// 获取子组件对象
|
||||
this.childrenMsg = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
customOpen() {
|
||||
if (this.childrenMsg) {
|
||||
this.childrenMsg.open()
|
||||
}
|
||||
},
|
||||
customClose() {
|
||||
if (this.childrenMsg) {
|
||||
this.childrenMsg.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import message from './message.js';
|
||||
// 定义 type 类型:弹出类型:top/bottom/center
|
||||
const config = {
|
||||
// 顶部弹出
|
||||
top:'top',
|
||||
// 底部弹出
|
||||
bottom:'bottom',
|
||||
// 居中弹出
|
||||
center:'center',
|
||||
// 消息提示
|
||||
message:'top',
|
||||
// 对话框
|
||||
dialog:'center',
|
||||
// 分享
|
||||
share:'bottom',
|
||||
}
|
||||
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
config:config
|
||||
}
|
||||
},
|
||||
mixins: [message],
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<view class="uni-popup-dialog">
|
||||
<view class="uni-dialog-title">
|
||||
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{title}}</text>
|
||||
</view>
|
||||
<view class="uni-dialog-content">
|
||||
<text class="uni-dialog-content-text" v-if="mode === 'base'">{{content}}</text>
|
||||
<input v-else class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus" >
|
||||
</view>
|
||||
<view class="uni-dialog-button-group">
|
||||
<view class="uni-dialog-button" @click="close">
|
||||
<text class="uni-dialog-button-text">取消</text>
|
||||
</view>
|
||||
<view class="uni-dialog-button uni-border-left" @click="onOk">
|
||||
<text class="uni-dialog-button-text uni-button-color">确定</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* PopUp 弹出层-对话框样式
|
||||
* @description 弹出层-对话框样式
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||
* @property {String} value input 模式下的默认值
|
||||
* @property {String} placeholder input 模式下输入提示
|
||||
* @property {String} type = [success|warning|info|error] 主题样式
|
||||
* @value success 成功
|
||||
* @value warning 提示
|
||||
* @value info 消息
|
||||
* @value error 错误
|
||||
* @property {String} mode = [base|input] 模式、
|
||||
* @value base 基础对话框
|
||||
* @value input 可输入对话框
|
||||
* @property {String} content 对话框内容
|
||||
* @property {Boolean} beforeClose 是否拦截取消事件
|
||||
* @event {Function} confirm 点击确认按钮触发
|
||||
* @event {Function} close 点击取消按钮触发
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: "uniPopupDialog",
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: [String, Number],
|
||||
default: '请输入内容'
|
||||
},
|
||||
/**
|
||||
* 对话框主题 success/warning/info/error 默认 success
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'error'
|
||||
},
|
||||
/**
|
||||
* 对话框模式 base/input
|
||||
*/
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'base'
|
||||
},
|
||||
/**
|
||||
* 对话框标题
|
||||
*/
|
||||
title: {
|
||||
type: String,
|
||||
default: '提示'
|
||||
},
|
||||
/**
|
||||
* 对话框内容
|
||||
*/
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 拦截取消事件 ,如果拦截取消事件,必须监听close事件,执行 done()
|
||||
*/
|
||||
beforeClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogType: 'error',
|
||||
focus: false,
|
||||
val: ""
|
||||
}
|
||||
},
|
||||
inject: ['popup'],
|
||||
watch: {
|
||||
type(val) {
|
||||
this.dialogType = val
|
||||
},
|
||||
mode(val) {
|
||||
if (val === 'input') {
|
||||
this.dialogType = 'info'
|
||||
}
|
||||
},
|
||||
value(val) {
|
||||
this.val = val
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 对话框遮罩不可点击
|
||||
this.popup.mkclick = false
|
||||
if (this.mode === 'input') {
|
||||
this.dialogType = 'info'
|
||||
this.val = this.value
|
||||
} else {
|
||||
this.dialogType = this.type
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.focus = true
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 点击确认按钮
|
||||
*/
|
||||
onOk() {
|
||||
this.$emit('confirm', () => {
|
||||
this.popup.close()
|
||||
if (this.mode === 'input') this.val = this.value
|
||||
}, this.mode === 'input' ? this.val : '')
|
||||
},
|
||||
/**
|
||||
* 点击取消按钮
|
||||
*/
|
||||
close() {
|
||||
if (this.beforeClose) {
|
||||
this.$emit('close', () => {
|
||||
this.popup.close()
|
||||
})
|
||||
return
|
||||
}
|
||||
this.popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.uni-popup-dialog {
|
||||
width: 300px;
|
||||
border-radius: 15px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.uni-dialog-title {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.uni-dialog-title-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uni-dialog-content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 5px 15px 15px 15px;
|
||||
}
|
||||
|
||||
.uni-dialog-content-text {
|
||||
font-size: 14px;
|
||||
color: #6e6e6e;
|
||||
}
|
||||
|
||||
.uni-dialog-button-group {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
border-top-color: #f5f5f5;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.uni-dialog-button {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.uni-border-left {
|
||||
border-left-color: #f0f0f0;
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.uni-dialog-button-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.uni-button-color {
|
||||
color: $uni-color-primary;
|
||||
}
|
||||
|
||||
.uni-dialog-input {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.uni-popup__success {
|
||||
color: $uni-color-success;
|
||||
}
|
||||
|
||||
.uni-popup__warn {
|
||||
color: $uni-color-warning;
|
||||
}
|
||||
|
||||
.uni-popup__error {
|
||||
color: $uni-color-error;
|
||||
}
|
||||
|
||||
.uni-popup__info {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<view class="uni-popup-message" :class="'uni-popup__'+[type]">
|
||||
<text class="uni-popup-message-text" :class="'uni-popup__'+[type]+'-text'">{{message}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
/**
|
||||
* PopUp 弹出层-消息提示
|
||||
* @description 弹出层-消息提示
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||
* @property {String} type = [success|warning|info|error] 主题样式
|
||||
* @value success 成功
|
||||
* @value warning 提示
|
||||
* @value info 消息
|
||||
* @value error 错误
|
||||
* @property {String} message 消息提示文字
|
||||
* @property {String} duration 显示时间,设置为 0 则不会自动关闭
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniPopupMessage',
|
||||
props: {
|
||||
/**
|
||||
* 主题 success/warning/info/error 默认 success
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'success'
|
||||
},
|
||||
/**
|
||||
* 消息文字
|
||||
*/
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 显示时间,设置为 0 则不会自动关闭
|
||||
*/
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 3000
|
||||
}
|
||||
},
|
||||
inject: ['popup'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {
|
||||
this.popup.childrenMsg = this
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
if (this.duration === 0) return
|
||||
clearTimeout(this.popuptimer)
|
||||
this.popuptimer = setTimeout(() => {
|
||||
this.popup.close()
|
||||
}, this.duration)
|
||||
},
|
||||
close() {
|
||||
clearTimeout(this.popuptimer)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.uni-popup-message {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
background-color: #e1f3d8;
|
||||
padding: 10px 15px;
|
||||
border-color: #eee;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
.uni-popup-message-text {
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.uni-popup__success {
|
||||
background-color: #e1f3d8;
|
||||
}
|
||||
|
||||
.uni-popup__success-text {
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
.uni-popup__warn {
|
||||
background-color: #faecd8;
|
||||
}
|
||||
|
||||
.uni-popup__warn-text {
|
||||
color: #E6A23C;
|
||||
}
|
||||
|
||||
.uni-popup__error {
|
||||
background-color: #fde2e2;
|
||||
}
|
||||
|
||||
.uni-popup__error-text {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
.uni-popup__info {
|
||||
background-color: #F2F6FC;
|
||||
}
|
||||
|
||||
.uni-popup__info-text {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<view class="uni-popup-share">
|
||||
<view class="uni-share-title"><text class="uni-share-title-text">{{title}}</text></view>
|
||||
<view class="uni-share-content">
|
||||
<view class="uni-share-content-box">
|
||||
<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
|
||||
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
|
||||
<text class="uni-share-text">{{item.text}}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-share-button-box">
|
||||
<button class="uni-share-button" @click="close">取消</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UniPopupShare',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '分享到'
|
||||
}
|
||||
},
|
||||
inject: ['popup'],
|
||||
data() {
|
||||
return {
|
||||
bottomData: [{
|
||||
text: '微信',
|
||||
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-2.png',
|
||||
name: 'wx'
|
||||
},
|
||||
{
|
||||
text: '支付宝',
|
||||
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-8.png',
|
||||
name: 'wx'
|
||||
},
|
||||
{
|
||||
text: 'QQ',
|
||||
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/gird-3.png',
|
||||
name: 'qq'
|
||||
},
|
||||
{
|
||||
text: '新浪',
|
||||
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-1.png',
|
||||
name: 'sina'
|
||||
},
|
||||
{
|
||||
text: '百度',
|
||||
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-7.png',
|
||||
name: 'copy'
|
||||
},
|
||||
{
|
||||
text: '其他',
|
||||
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-5.png',
|
||||
name: 'more'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
/**
|
||||
* 选择内容
|
||||
*/
|
||||
select(item, index) {
|
||||
this.$emit('select', {
|
||||
item,
|
||||
index
|
||||
}, () => {
|
||||
this.popup.close()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 关闭窗口
|
||||
*/
|
||||
close() {
|
||||
this.popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.uni-popup-share {
|
||||
background-color: #fff;
|
||||
}
|
||||
.uni-share-title {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
}
|
||||
.uni-share-title-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.uni-share-content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.uni-share-content-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.uni-share-content-item {
|
||||
width: 90px;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uni-share-content-item:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.uni-share-image {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.uni-share-text {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #3B4144;
|
||||
}
|
||||
|
||||
.uni-share-button-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.uni-share-button {
|
||||
flex: 1;
|
||||
border-radius: 50px;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.uni-share-button::after {
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear">
|
||||
<uni-transition v-if="maskShow" :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans"
|
||||
@click="onTap" />
|
||||
<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
|
||||
<view class="uni-popup__wrapper-box" @click.stop="clear">
|
||||
<slot />
|
||||
</view>
|
||||
</uni-transition>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniTransition from '../uni-transition/uni-transition.vue'
|
||||
import popup from './popup.js'
|
||||
/**
|
||||
* PopUp 弹出层
|
||||
* @description 弹出层组件,为了解决遮罩弹层的问题
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||
* @property {String} type = [top|center|bottom] 弹出方式
|
||||
* @value top 顶部弹出
|
||||
* @value center 中间弹出
|
||||
* @value bottom 底部弹出
|
||||
* @value message 消息提示
|
||||
* @value dialog 对话框
|
||||
* @value share 底部分享示例
|
||||
* @property {Boolean} animation = [ture|false] 是否开启动画
|
||||
* @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
|
||||
* @event {Function} change 打开关闭弹窗触发,e={show: false}
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniPopup',
|
||||
components: {
|
||||
uniTransition
|
||||
},
|
||||
props: {
|
||||
// 开启动画
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
||||
// message: 消息提示 ; dialog : 对话框
|
||||
type: {
|
||||
type: String,
|
||||
default: 'center'
|
||||
},
|
||||
// maskClick
|
||||
maskClick: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
popup: this
|
||||
}
|
||||
},
|
||||
mixins: [popup],
|
||||
watch: {
|
||||
/**
|
||||
* 监听type类型
|
||||
*/
|
||||
type: {
|
||||
handler: function(newVal) {
|
||||
this[this.config[newVal]]()
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
/**
|
||||
* 监听遮罩是否可点击
|
||||
* @param {Object} val
|
||||
*/
|
||||
maskClick(val) {
|
||||
this.mkclick = val
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
duration: 300,
|
||||
ani: [],
|
||||
showPopup: false,
|
||||
showTrans: false,
|
||||
maskClass: {
|
||||
'position': 'fixed',
|
||||
'bottom': 0,
|
||||
'top': 0,
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
'backgroundColor': 'rgba(0, 0, 0, 0.4)'
|
||||
},
|
||||
transClass: {
|
||||
'position': 'fixed',
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
},
|
||||
maskShow: true,
|
||||
mkclick: true,
|
||||
popupstyle: 'top'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.mkclick = this.maskClick
|
||||
if (this.animation) {
|
||||
this.duration = 300
|
||||
} else {
|
||||
this.duration = 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clear(e) {
|
||||
// TODO nvue 取消冒泡
|
||||
e.stopPropagation()
|
||||
},
|
||||
open() {
|
||||
this.showPopup = true
|
||||
this.$nextTick(() => {
|
||||
new Promise(resolve => {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = setTimeout(() => {
|
||||
this.showTrans = true
|
||||
// fixed by mehaotian 兼容 app 端
|
||||
this.$nextTick(() => {
|
||||
resolve();
|
||||
})
|
||||
}, 50);
|
||||
}).then(res => {
|
||||
// 自定义打开事件
|
||||
clearTimeout(this.msgtimer)
|
||||
this.msgtimer = setTimeout(() => {
|
||||
this.customOpen && this.customOpen()
|
||||
}, 100)
|
||||
this.$emit('change', {
|
||||
show: true,
|
||||
type: this.type
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
close(type) {
|
||||
this.showTrans = false
|
||||
this.$nextTick(() => {
|
||||
this.$emit('change', {
|
||||
show: false,
|
||||
type: this.type
|
||||
})
|
||||
clearTimeout(this.timer)
|
||||
// 自定义关闭事件
|
||||
this.customOpen && this.customClose()
|
||||
this.timer = setTimeout(() => {
|
||||
this.showPopup = false
|
||||
}, 300)
|
||||
})
|
||||
},
|
||||
onTap() {
|
||||
if (!this.mkclick) return
|
||||
this.close()
|
||||
},
|
||||
/**
|
||||
* 顶部弹出样式处理
|
||||
*/
|
||||
top() {
|
||||
this.popupstyle = 'top'
|
||||
this.ani = ['slide-top']
|
||||
this.transClass = {
|
||||
'position': 'fixed',
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 底部弹出样式处理
|
||||
*/
|
||||
bottom() {
|
||||
this.popupstyle = 'bottom'
|
||||
this.ani = ['slide-bottom']
|
||||
this.transClass = {
|
||||
'position': 'fixed',
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
'bottom': 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 中间弹出样式处理
|
||||
*/
|
||||
center() {
|
||||
this.popupstyle = 'center'
|
||||
this.ani = ['zoom-out', 'fade']
|
||||
this.transClass = {
|
||||
'position': 'fixed',
|
||||
/* #ifndef APP-NVUE */
|
||||
'display': 'flex',
|
||||
'flexDirection': 'column',
|
||||
/* #endif */
|
||||
'bottom': 0,
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
'top': 0,
|
||||
'justifyContent': 'center',
|
||||
'alignItems': 'center'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.uni-popup {
|
||||
position: fixed;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.uni-popup__mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: $uni-bg-color-mask;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.mask-ani {
|
||||
transition-property: opacity;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
.uni-top-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.uni-bottom-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.uni-center-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.uni-popup__wrapper {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.top {
|
||||
/* #ifdef H5 */
|
||||
top: var(--window-top);
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.uni-popup__wrapper-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
/* iphonex 等安全区设置,底部安全区适配 */
|
||||
/* #ifndef APP-NVUE */
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.content-ani {
|
||||
// transition: transform 0.3s;
|
||||
transition-property: transform, opacity;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
|
||||
.uni-top-content {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.uni-bottom-content {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.uni-center-content {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<view :class="[styleType === 'text'?'segmented-control--text' : 'segmented-control--button' ]" :style="{ borderColor: styleType === 'text' ? '' : activeColor }"
|
||||
class="segmented-control">
|
||||
<view v-for="(item, index) in values" :class="[ styleType === 'text'?'segmented-control__item--text': 'segmented-control__item--button' , index === currentIndex&&styleType === 'button'?'segmented-control__item--button--active': '' , index === 0&&styleType === 'button'?'segmented-control__item--button--first': '',index === values.length - 1&&styleType === 'button'?'segmented-control__item--button--last': '' ]"
|
||||
:key="index" :style="{
|
||||
backgroundColor: index === currentIndex && styleType === 'button' ? activeColor : '',borderColor: index === currentIndex&&styleType === 'text'||styleType === 'button'?activeColor:'transparent'
|
||||
}"
|
||||
class="segmented-control__item" @click="_onClick(index)">
|
||||
<text :style="{color:
|
||||
index === currentIndex
|
||||
? styleType === 'text'
|
||||
? activeColor
|
||||
: '#fff'
|
||||
: styleType === 'text'
|
||||
? '#000'
|
||||
: activeColor}"
|
||||
class="segmented-control__text">{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* SegmentedControl 分段器
|
||||
* @description 用作不同视图的显示
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=54
|
||||
* @property {Number} current 当前选中的tab索引值,从0计数
|
||||
* @property {String} styleType = [button|text] 分段器样式类型
|
||||
* @value button 按钮类型
|
||||
* @value text 文字类型
|
||||
* @property {String} activeColor 选中的标签背景色与边框颜色
|
||||
* @property {Array} values 选项数组
|
||||
* @event {Function} clickItem 组件触发点击事件时触发,e={currentIndex}
|
||||
*/
|
||||
export default {
|
||||
name: 'UniSegmentedControl',
|
||||
props: {
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#007aff'
|
||||
},
|
||||
styleType: {
|
||||
type: String,
|
||||
default: 'button'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
current(val) {
|
||||
if (val !== this.currentIndex) {
|
||||
this.currentIndex = val
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.currentIndex = this.current
|
||||
},
|
||||
methods: {
|
||||
_onClick(index) {
|
||||
if (this.currentIndex !== index) {
|
||||
this.currentIndex = index
|
||||
this.$emit('clickItem', {
|
||||
currentIndex: index
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.segmented-control {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
height: 50rpx;
|
||||
overflow: hidden;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
.segmented-control__item {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10rpx;
|
||||
}
|
||||
.segmented-control__item--button {
|
||||
border-style: solid;
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
.segmented-control__item--button--first {
|
||||
border-left-width: 1px;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
.segmented-control__item--button--last {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
.segmented-control__item--text {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 3px;
|
||||
}
|
||||
.segmented-control__text {
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject"
|
||||
@click="change">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
const animation = uni.requireNativePlugin('animation');
|
||||
// #endif
|
||||
/**
|
||||
* Transition 过渡动画
|
||||
* @description 简单过渡动画组件
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
|
||||
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
|
||||
* @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
|
||||
* @value fade 渐隐渐出过渡
|
||||
* @value slide-top 由上至下过渡
|
||||
* @value slide-right 由右至左过渡
|
||||
* @value slide-bottom 由下至上过渡
|
||||
* @value slide-left 由左至右过渡
|
||||
* @value zoom-in 由小到大过渡
|
||||
* @value zoom-out 由大到小过渡
|
||||
* @property {Number} duration 过渡动画持续时间
|
||||
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
|
||||
*/
|
||||
export default {
|
||||
name: 'uniTransition',
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
modeClass: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 300
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
transform: '',
|
||||
ani: { in: '',
|
||||
active: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.open()
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
stylesObject() {
|
||||
let styles = {
|
||||
...this.styles,
|
||||
'transition-duration': this.duration / 1000 + 's'
|
||||
}
|
||||
let transfrom = ''
|
||||
for (let i in styles) {
|
||||
let line = this.toLine(i)
|
||||
transfrom += line + ':' + styles[i] + ';'
|
||||
}
|
||||
return transfrom
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// this.timer = null
|
||||
// this.nextTick = (time = 50) => new Promise(resolve => {
|
||||
// clearTimeout(this.timer)
|
||||
// this.timer = setTimeout(resolve, time)
|
||||
// return this.timer
|
||||
// });
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
this.$emit('click', {
|
||||
detail: this.isShow
|
||||
})
|
||||
},
|
||||
open() {
|
||||
clearTimeout(this.timer)
|
||||
this.isShow = true
|
||||
this.transform = ''
|
||||
this.ani.in = ''
|
||||
for (let i in this.getTranfrom(false)) {
|
||||
if (i === 'opacity') {
|
||||
this.ani.in = 'fade-in'
|
||||
} else {
|
||||
this.transform += `${this.getTranfrom(false)[i]} `
|
||||
}
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this._animation(true)
|
||||
}, 50)
|
||||
})
|
||||
|
||||
},
|
||||
close(type) {
|
||||
clearTimeout(this.timer)
|
||||
this._animation(false)
|
||||
},
|
||||
_animation(type) {
|
||||
let styles = this.getTranfrom(type)
|
||||
// #ifdef APP-NVUE
|
||||
if(!this.$refs['ani']) return
|
||||
animation.transition(this.$refs['ani'].ref, {
|
||||
styles,
|
||||
duration: this.duration, //ms
|
||||
timingFunction: 'ease',
|
||||
needLayout: false,
|
||||
delay: 0 //ms
|
||||
}, () => {
|
||||
if (!type) {
|
||||
this.isShow = false
|
||||
}
|
||||
this.$emit('change', {
|
||||
detail: this.isShow
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
this.transform = ''
|
||||
for (let i in styles) {
|
||||
if (i === 'opacity') {
|
||||
this.ani.in = `fade-${type?'out':'in'}`
|
||||
} else {
|
||||
this.transform += `${styles[i]} `
|
||||
}
|
||||
}
|
||||
this.timer = setTimeout(() => {
|
||||
if (!type) {
|
||||
this.isShow = false
|
||||
}
|
||||
this.$emit('change', {
|
||||
detail: this.isShow
|
||||
})
|
||||
|
||||
}, this.duration)
|
||||
// #endif
|
||||
|
||||
},
|
||||
getTranfrom(type) {
|
||||
let styles = {
|
||||
transform: ''
|
||||
}
|
||||
this.modeClass.forEach((mode) => {
|
||||
switch (mode) {
|
||||
case 'fade':
|
||||
styles.opacity = type ? 1 : 0
|
||||
break;
|
||||
case 'slide-top':
|
||||
styles.transform += `translateY(${type?'0':'-100%'}) `
|
||||
break;
|
||||
case 'slide-right':
|
||||
styles.transform += `translateX(${type?'0':'100%'}) `
|
||||
break;
|
||||
case 'slide-bottom':
|
||||
styles.transform += `translateY(${type?'0':'100%'}) `
|
||||
break;
|
||||
case 'slide-left':
|
||||
styles.transform += `translateX(${type?'0':'-100%'}) `
|
||||
break;
|
||||
case 'zoom-in':
|
||||
styles.transform += `scale(${type?1:0.8}) `
|
||||
break;
|
||||
case 'zoom-out':
|
||||
styles.transform += `scale(${type?1:1.2}) `
|
||||
break;
|
||||
}
|
||||
})
|
||||
return styles
|
||||
},
|
||||
_modeClassArr(type) {
|
||||
let mode = this.modeClass
|
||||
if (typeof(mode) !== "string") {
|
||||
let modestr = ''
|
||||
mode.forEach((item) => {
|
||||
modestr += (item + '-' + type + ',')
|
||||
})
|
||||
return modestr.substr(0, modestr.length - 1)
|
||||
} else {
|
||||
return mode + '-' + type
|
||||
}
|
||||
},
|
||||
// getEl(el) {
|
||||
// console.log(el || el.ref || null);
|
||||
// return el || el.ref || null
|
||||
// },
|
||||
toLine(name) {
|
||||
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.uni-transition {
|
||||
transition-timing-function: ease;
|
||||
transition-duration: 0.3s;
|
||||
transition-property: transform, opacity;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-top-in {
|
||||
/* transition-property: transform, opacity; */
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.slide-top-active {
|
||||
transform: translateY(0);
|
||||
/* opacity: 1; */
|
||||
}
|
||||
|
||||
.slide-right-in {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.slide-right-active {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.slide-bottom-in {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.slide-bottom-active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.slide-left-in {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.slide-left-active {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.zoom-in-in {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.zoom-out-active {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.zoom-out-in {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,182 @@
|
||||
## 插件说明
|
||||
|
||||
> 这是 `v-tabs` 插件的升级版本,参数上有很大变动,支持 `H5` `小程序` `手机端`,如果是在之前的插件上升级的话,请注意参数的变更,触发的事件没有变更。
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 1、最基本用法
|
||||
|
||||
- 视图文件
|
||||
|
||||
```html
|
||||
<v-tabs v-model="current" :tabs="tabs" @change="changeTab"></v-tabs>
|
||||
```
|
||||
|
||||
- 脚本文件
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
tabs: ['军事', '国内', '新闻新闻', '军事', '国内', '新闻', '军事', '国内', '新闻']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeTab(index) {
|
||||
console.log('当前选中的项:' + index)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2、平铺整个屏幕
|
||||
|
||||
- 视图文件
|
||||
|
||||
```html
|
||||
<v-tabs v-model="activeTab" :scroll="false" :tabs="['全部', '进行中', '已完成']"></v-tabs>
|
||||
```
|
||||
|
||||
- 脚本文件
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3、胶囊用法
|
||||
|
||||
- 视图文件
|
||||
|
||||
```html
|
||||
<v-tabs v-model="current" :tabs="tabs" :pills="true" line-height="0" activeColor="#fff" @change="changeTab"></v-tabs>
|
||||
```
|
||||
|
||||
- 脚本文件
|
||||
|
||||
```js
|
||||
data() {
|
||||
return {
|
||||
current: 2,
|
||||
tabs: [
|
||||
'军事',
|
||||
'国内',
|
||||
'新闻新闻',
|
||||
'军事',
|
||||
'国内',
|
||||
'新闻',
|
||||
'军事',
|
||||
'国内',
|
||||
'新闻',
|
||||
],
|
||||
},
|
||||
methods: {
|
||||
changeTab(index) {
|
||||
console.log('当前选中索引:' + index)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 文档说明
|
||||
|
||||
### 1、属性说明
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| :---------------: | :-----: | :-------: | :----------------------------------------: |
|
||||
| value | Number | 0 | 必传(双向绑定的值) |
|
||||
| color | String | '#333' | 默认文字颜色 |
|
||||
| activeColor | String | '#2979ff' | 选中文字的颜色 |
|
||||
| fontSize | String | '28rpx' | 默认文字大小(rpx 或 px) |
|
||||
| bold | Boolean | true | 是否加粗选中项 |
|
||||
| scroll | Boolean | true | 是否显示滚动条,平铺设置 false |
|
||||
| height | String | '70rpx' | tab 高度(rpx 或 px) |
|
||||
| lineHeight | String | '10rpx' | 滑块高度(rpx 或 px) |
|
||||
| lineColor | String | '#2979ff' | 滑块的颜色 |
|
||||
| lineScale | Number | 0.5 | 滑块宽度缩放值 |
|
||||
| lineRadius | String | '10rpx' | 滑块圆角宽度(rpx 或 px) |
|
||||
| pills | Boolean | false | 是否开启胶囊 |
|
||||
| pillsColor | String | '#2979ff' | 胶囊背景颜色(rpx 或 px) |
|
||||
| pillsBorderRadius | String | '10rpx' | 胶囊圆角宽度(rpx 或 px) |
|
||||
| field | String | '' | 如果 tabs 子项是对象,输入需要展示的键名 |
|
||||
| bgColor | String | '#fff' | 背景色,支持 linear-gradient 渐变 |
|
||||
| padding | String | '0' | 整个 tab padding 属性 |
|
||||
| fixed | Boolean | false | 是否固定在顶部 |
|
||||
| paddingItem | String | '0 22rpx' | 选项的边距(设置上下不生效,需要设置高度) |
|
||||
|
||||
### 2、事件说明
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| :----: | :---: | :--------------------------------: |
|
||||
| change | index | 改变选中项触发, index 选中项的下标 |
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 2020-09-24
|
||||
|
||||
1. 修复 `v-tabs` 第一次可能出现第一个标签显示不完整的情况
|
||||
2. 修改了 `pages/tabs/order` 示例文件
|
||||
|
||||
### 2020-09-21
|
||||
|
||||
1. 修复添加 `fixed` 属性后,滚动条无效
|
||||
2. 修复选项很少的情况下,下划线计算计算错误
|
||||
3. 新增 `paddingItem` 属性,设置选项左右边距(上下边距需要设置 `height` 属性,或者设置 `padding` 属性)
|
||||
|
||||
**写在最后:**
|
||||
欢迎各位老铁反馈 bug ,本人后端 PHP 一枚,只是应为感兴趣前端,自己琢磨,自己搞。如果你在使用的过程中有什么不合理,需要优化的,都可以在下面评论(或加我 QQ: 1207791534),本人看见后回复、修正,感谢。
|
||||
|
||||
### 2020-09-17
|
||||
|
||||
1. 紧急修复 bug,横向滑动不了的情况
|
||||
|
||||
### 2020-09-16
|
||||
|
||||
1. 新增 `fixed` 属性,是否固定在顶部,示例地址:`pages/tabs/tabs-static`
|
||||
2. 优化之前的页面结构
|
||||
|
||||
**注意:**
|
||||
|
||||
1. 使用 `padding` 属性的时候,尽量不要左右边距,会导致下划线位置不对
|
||||
2. 如果不绑定 `v-model` 会导致 `change` 事件改变的时候,下划线不跟随问题
|
||||
|
||||
### 2020-09-09
|
||||
|
||||
1. 修复 `width` 错误,dom 加载的时候没有及时获取到 `data` 属性导致的。
|
||||
|
||||
### 2020-08-29
|
||||
|
||||
1. 优化异步改变 `tabs` 后,下划线不初始化问题
|
||||
2. `github` 地址上有图 2 的源码,需要的自行下载,页面路径:`pages/tabs/order`
|
||||
|
||||
### 2020-08-20
|
||||
|
||||
1. 优化 `节点查询` 和 `选中渲染`
|
||||
2. 优化支付宝中 `createSelectorQuery()` 的影响
|
||||
|
||||
### 2020-08-19
|
||||
|
||||
1. 优化 `change` 事件触发机制
|
||||
|
||||
### 2020-08-16
|
||||
|
||||
1. 修改默认高度为 `70rpx`
|
||||
2. 新增属性 `bgColor`,可设置背景颜色,默认 `#fff`
|
||||
3. 新增整个 `tab` 的 `padding` 属性,默认 `0`
|
||||
|
||||
### 2020-08-13
|
||||
|
||||
1. 全新的 `v-tabs 2.0`
|
||||
2. 支持 `H5` `小程序` `APP`
|
||||
3. 属性高度可配置
|
||||
|
||||
## 预览
|
||||
|
||||

|
||||

|
||||
@@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<view :id="elId" class="v-tabs">
|
||||
<scroll-view
|
||||
id="scrollContainer"
|
||||
:scroll-x="scroll"
|
||||
:scroll-left="scroll ? scrollLeft : 0"
|
||||
:scroll-with-animation="scroll"
|
||||
:style="{ position: fixed ? 'fixed' : 'relative', zIndex: 1993 }"
|
||||
>
|
||||
<view
|
||||
class="v-tabs__container"
|
||||
:style="{
|
||||
display: scroll ? 'inline-flex' : 'flex',
|
||||
whiteSpace: scroll ? 'nowrap' : 'normal',
|
||||
background: bgColor,
|
||||
height,
|
||||
padding
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="v-tabs__container-item"
|
||||
v-for="(v, i) in tabs"
|
||||
:key="i"
|
||||
:style="{
|
||||
color: current == i ? activeColor : color,
|
||||
fontSize: current == i ? fontSize : fontSize,
|
||||
fontWeight: bold && current == i ? 'bold' : '',
|
||||
justifyContent: !scroll ? 'center' : '',
|
||||
flex: scroll ? '' : 1,
|
||||
padding: paddingItem,
|
||||
marginRight: itemMarginRight,
|
||||
background: current == i ? activeItemBgColor : itemBgColor,
|
||||
borderRadius: itemBorderRadius
|
||||
}"
|
||||
@click="change(i)"
|
||||
>
|
||||
{{ field ? v[field] : v }}
|
||||
</view>
|
||||
<view
|
||||
v-if="!pills"
|
||||
class="v-tabs__container-line"
|
||||
:style="{
|
||||
background: lineColor,
|
||||
width: lineWidth + 'px',
|
||||
height: lineHeight,
|
||||
borderRadius: lineRadius,
|
||||
left: lineLeft + 'px',
|
||||
transform: `translateX(-${lineWidth / 2}px)`
|
||||
}"
|
||||
></view>
|
||||
<view
|
||||
v-else
|
||||
class="v-tabs__container-pills"
|
||||
:style="{
|
||||
background: pillsColor,
|
||||
borderRadius: pillsBorderRadius,
|
||||
left: pillsLeft + 'px',
|
||||
width: currentWidth + 'px',
|
||||
height
|
||||
}"
|
||||
></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view
|
||||
class="v-tabs__placeholder"
|
||||
:style="{
|
||||
height: fixed ? height : '0',
|
||||
padding
|
||||
}"
|
||||
></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* v-tabs
|
||||
* @property {Number} value 选中的下标
|
||||
* @property {Array} tabs tabs 列表
|
||||
* @property {String} bgColor = '#fff' 背景颜色
|
||||
* @property {String} color = '#333' 默认颜色
|
||||
* @property {String} activeColor = '#2979ff' 选中文字颜色
|
||||
* @property {String} fontSize = '28rpx' 默认文字大小
|
||||
* @property {String} activeFontSize = '28rpx' 选中文字大小
|
||||
* @property {Boolean} bold = [true | false] 选中文字是否加粗
|
||||
* @property {Boolean} scroll = [true | false] 是否滚动
|
||||
* @property {String} height = '60rpx' tab 的高度
|
||||
* @property {String} lineHeight = '10rpx' 下划线的高度
|
||||
* @property {String} lineColor = '#2979ff' 下划线的颜色
|
||||
* @property {Number} lineScale = 0.5 下划线的宽度缩放比例
|
||||
* @property {String} lineRadius = '10rpx' 下划线圆角
|
||||
* @property {Boolean} pills = [true | false] 是否胶囊样式
|
||||
* @property {String} pillsColor = '#2979ff' 胶囊背景色
|
||||
* @property {String} pillsBorderRadius = '10rpx' 胶囊圆角大小
|
||||
* @property {String} field 如果是对象,显示的键名
|
||||
* @property {Boolean} fixed = [true | false] 是否固定
|
||||
* @property {String} paddingItem = '0 22rpx' 选项的边距
|
||||
*
|
||||
* @event {Function(current)} change 改变标签触发
|
||||
*/
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
tabs: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
padding: {
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#2979ff'
|
||||
},
|
||||
itemBgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
activeItemBgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
itemBorderRadius: {
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
itemMarginRight:{
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: '28rpx'
|
||||
},
|
||||
activeFontSize: {
|
||||
type: String,
|
||||
default: '32rpx'
|
||||
},
|
||||
bold: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
scroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '70rpx'
|
||||
},
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#2979ff'
|
||||
},
|
||||
lineHeight: {
|
||||
type: String,
|
||||
default: '10rpx'
|
||||
},
|
||||
lineScale: {
|
||||
type: Number,
|
||||
default: 0.5
|
||||
},
|
||||
lineRadius: {
|
||||
type: String,
|
||||
default: '10rpx'
|
||||
},
|
||||
pills: {
|
||||
type: Boolean,
|
||||
deafult: false
|
||||
},
|
||||
pillsColor: {
|
||||
type: String,
|
||||
default: '#2979ff'
|
||||
},
|
||||
pillsBorderRadius: {
|
||||
type: String,
|
||||
default: '10rpx'
|
||||
},
|
||||
field: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
paddingItem: {
|
||||
type: String,
|
||||
default: '0 22rpx'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
elId: '',
|
||||
lineWidth: 30,
|
||||
currentWidth: 0, // 当前选项的宽度
|
||||
lineLeft: 0, // 滑块距离左侧的位置
|
||||
pillsLeft: 0, // 胶囊距离左侧的位置
|
||||
scrollLeft: 0, // 距离左边的位置
|
||||
containerWidth: 0, // 容器的宽度
|
||||
current: 0 // 当前选中项
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.current = newVal
|
||||
this.$nextTick(() => {
|
||||
this.getTabItemWidth()
|
||||
})
|
||||
},
|
||||
current(newVal) {
|
||||
this.$emit('input', newVal)
|
||||
},
|
||||
tabs(newVal) {
|
||||
this.$nextTick(() => {
|
||||
this.getTabItemWidth()
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 产生随机字符串
|
||||
randomString(len) {
|
||||
len = len || 32
|
||||
let $chars =
|
||||
'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
||||
let maxPos = $chars.length
|
||||
let pwd = ''
|
||||
for (let i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
|
||||
}
|
||||
return pwd
|
||||
},
|
||||
// 切换事件
|
||||
change(index) {
|
||||
if (this.current !== index) {
|
||||
this.current = index
|
||||
|
||||
this.$emit('change', index)
|
||||
}
|
||||
},
|
||||
// 获取左移动位置
|
||||
getTabItemWidth() {
|
||||
let query = uni
|
||||
.createSelectorQuery()
|
||||
// #ifndef MP-ALIPAY
|
||||
.in(this)
|
||||
// #endif
|
||||
// 获取容器的宽度
|
||||
query
|
||||
.select(`#scrollContainer`)
|
||||
.boundingClientRect((data) => {
|
||||
if (!this.containerWidth && data) {
|
||||
this.containerWidth = data.width
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
// 获取所有的 tab-item 的宽度
|
||||
query
|
||||
.selectAll('.v-tabs__container-item')
|
||||
.boundingClientRect((data) => {
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
let lineLeft = 0
|
||||
let currentWidth = 0
|
||||
if (data) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i < this.current) {
|
||||
lineLeft += data[i].width
|
||||
} else if (i == this.current) {
|
||||
currentWidth = data[i].width
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// 当前滑块的宽度
|
||||
this.currentWidth = currentWidth
|
||||
// 缩放后的滑块宽度
|
||||
this.lineWidth = currentWidth * this.lineScale * 1
|
||||
// 滑块作移动的位置
|
||||
this.lineLeft = lineLeft + currentWidth / 2
|
||||
// 胶囊距离左侧的位置
|
||||
this.pillsLeft = lineLeft
|
||||
// 计算滚动的距离左侧的位置
|
||||
if (this.scroll) {
|
||||
this.scrollLeft = this.lineLeft - this.containerWidth / 2
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.elId = 'xfjpeter_' + this.randomString()
|
||||
this.current = this.value
|
||||
this.$nextTick(() => {
|
||||
this.getTabItemWidth()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.v-tabs {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__container {
|
||||
min-width: 100%;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
// padding: 0 11px;
|
||||
transition: all 0.3s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-line {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
&-pills {
|
||||
position: absolute;
|
||||
transition: all 0.3s linear;
|
||||
z-index: 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- mask层 -->
|
||||
<view :class="['mask', show ? '' : 'visible']"></view>
|
||||
<!-- 金额 -->
|
||||
<view :class="['content', show ? '' : 'contenthide']">
|
||||
<view class="title">
|
||||
<view class="close" @click="close()"><image :src="`${staticUrl}/close.png`" /></view>
|
||||
<view class="center">请输入支付密码</view>
|
||||
<view class="close"></view>
|
||||
</view>
|
||||
<block v-if="showMoney">
|
||||
<view class="pay-money">
|
||||
<view>金额</view>
|
||||
<view class="money">¥ <text>{{force2Decimal(money)}}</text></view>
|
||||
</view>
|
||||
<view class="pay-way">
|
||||
<view>支付方式</view>
|
||||
<view class="pay-img">余额</view>
|
||||
</view>
|
||||
</block>
|
||||
<view :class="['code', show ? '' : 'visible']">
|
||||
<view class="code-box">
|
||||
<block v-for="(item, index) in 6" :key="index">
|
||||
<view class="code-box-item">{{ (password[index] && '●') || '' }}</view>
|
||||
</block>
|
||||
</view>
|
||||
<input type="number" v-model="password" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 键盘 -->
|
||||
<view :class="['keyboard', show ? '' : 'active', isIphoneX ? 'isIphone' : '']">
|
||||
<block v-for="(item, index) in 9" :key="index">
|
||||
<view class="keyboard-item" @tap="key(index + 1)">{{index + 1}}</view>
|
||||
</block>
|
||||
<view class="keyboard-item hide"></view>
|
||||
<view class="keyboard-item" @tap="key(0)"><text>0</text></view>
|
||||
<view class="keyboard-item delte" @tap="del()"><image class="img" :src="`${staticUrl}/del.png`" mode="aspectFill" :lazy-load="true"></image></view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showMoney: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
money: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
isIphoneX: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
staticUrl:this.$STATIC_RESOURCE_URL,
|
||||
password: '',
|
||||
trantision: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
force2Decimal(value){
|
||||
return this.$force2Decimal(value);
|
||||
},
|
||||
key(key) {
|
||||
if (this.password.length < 6) {
|
||||
this.password += key;
|
||||
if (this.password.length == 6) {
|
||||
console.log(this.password);
|
||||
//uni.showToast({ title: '输入完成' });
|
||||
|
||||
this.$emit('finish',this.password);
|
||||
}
|
||||
}
|
||||
},
|
||||
clear() {
|
||||
this.password = '';
|
||||
},
|
||||
close() {
|
||||
this.password = '';
|
||||
this.trantision = false;
|
||||
this.$emit('close', false);
|
||||
},
|
||||
del() {
|
||||
if (this.password.length > 0) {
|
||||
this.password = this.password.substring(0, this.password.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
.mask {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(102, 102, 102, 0.3);
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
visibility: visible;
|
||||
}
|
||||
.content {
|
||||
width: 588rpx;
|
||||
height: 570rpx;
|
||||
border-radius: 15rpx;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top:35%;
|
||||
margin-left: -294rpx;
|
||||
margin-top: -285rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.contenthide {
|
||||
margin-left: -350rpx;
|
||||
margin-top: -340rpx;
|
||||
width: 700rpx;
|
||||
height: 680rpx;
|
||||
opacity: 0;
|
||||
z-index: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.visible {
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
visibility: hidden;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 80rpx;
|
||||
width: 560rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.center {
|
||||
color: #333;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.close {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
}
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.pay-money {
|
||||
width: 525rpx;
|
||||
height: 240rpx;
|
||||
border-bottom: 1rpx solid #EBEBEB;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.pay-money view {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.pay-way {
|
||||
width: 520rpx;
|
||||
height: 85rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #8A8A8A;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.money {
|
||||
font-size: 45rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
.money text {
|
||||
font-size: 60rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
.pay-img {
|
||||
color: #333333;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.pay-img image {
|
||||
width: 35rpx;
|
||||
height: 35rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
.code {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.code-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #d0d0d0;
|
||||
}
|
||||
.code-box-item {
|
||||
width: 86rpx;
|
||||
height: 86rpx;
|
||||
background: #fff;
|
||||
font-size: 40rpx;
|
||||
line-height: 75rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.code-box-item:not(:last-child) {
|
||||
border-right: 1px solid #d0d0d0;
|
||||
}
|
||||
.keyboard {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: #EBEBEB;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
flex-wrap: wrap;
|
||||
transition:all 0.2s ease-in 0.2s;
|
||||
}
|
||||
.active {
|
||||
bottom: -400rpx;
|
||||
}
|
||||
.keyboard-item {
|
||||
box-sizing: border-box;
|
||||
width: 250rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
font-size: 40rpx;
|
||||
color: #333;
|
||||
height: 99rpx;
|
||||
border: 1rpx solid #EBEBEB;
|
||||
border-top:none;
|
||||
border-left:none;
|
||||
}
|
||||
.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
.delte {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.delte image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
.isIphone {
|
||||
padding-bottom: 68rpx !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,13 @@
|
||||
export const VUE_APP_API_URL = "https://shop.xdd618.com/api";
|
||||
// export const VUE_APP_API_URL = "https://www.xdd618.com/api";
|
||||
|
||||
export const SERVICE_API_URL = "https://service-test.xdd618.com/api";
|
||||
// export const SERVICE_API_URL = "https://service.xdd618.com/api";
|
||||
|
||||
export const SOCKET_URL = "wss://service-test.xdd618.com/ws";
|
||||
// export const SOCKET_URL = "wss://service.xdd618.com/ws";
|
||||
|
||||
export const VUE_APP_RESOURCES_URL = "https://www.xdd618.com/api/file/pic";
|
||||
export const STATIC_RESOURCE_URL = "https://resource.xdd618.com/image"
|
||||
|
||||
export const WX_LIVE_APPID = "wx2b03c6e691cd7370";
|
||||
@@ -0,0 +1,51 @@
|
||||
import $store from "@//store";
|
||||
import { VUE_APP_WS_URL } from "@/utils";
|
||||
|
||||
const Socket = function() {
|
||||
this.ws = new WebSocket(VUE_APP_WS_URL);
|
||||
this.ws.onopen = this.onOpen.bind(this);
|
||||
this.ws.onerror = this.onError.bind(this);
|
||||
this.ws.onmessage = this.onMessage.bind(this);
|
||||
this.ws.onclose = this.onClose.bind(this);
|
||||
};
|
||||
|
||||
Socket.prototype = {
|
||||
vm(vm) {
|
||||
this.vm = vm;
|
||||
},
|
||||
close() {
|
||||
clearInterval(this.timer);
|
||||
this.ws.close();
|
||||
},
|
||||
onOpen: function() {
|
||||
this.init();
|
||||
this.send({
|
||||
type: "login",
|
||||
data: $store.state.token
|
||||
});
|
||||
this.vm.$emit("socket_open");
|
||||
},
|
||||
init: function() {
|
||||
var that = this;
|
||||
this.timer = setInterval(function() {
|
||||
that.send({ type: "ping" });
|
||||
}, 10000);
|
||||
},
|
||||
send: function(data) {
|
||||
return this.ws.send(JSON.stringify(data));
|
||||
},
|
||||
onMessage: function(res) {
|
||||
const { type, data = {} } = JSON.parse(res.data);
|
||||
this.vm.$emit(type, data);
|
||||
},
|
||||
onClose: function() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
onError: function(e) {
|
||||
this.vm.$emit("socket_error", e);
|
||||
}
|
||||
};
|
||||
|
||||
Socket.prototype.constructor = Socket;
|
||||
|
||||
export default Socket;
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
import { cancelOrder, takeOrder, delOrder, payOrder, yuePayOrder } from "@/api/order";
|
||||
import dialog from "@/utils/dialog";
|
||||
import { weappPay } from "@/libs/wechat";
|
||||
|
||||
export function cancelOrderHandle(orderId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认取消该订单?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
cancelOrder(orderId)
|
||||
.then(res => {
|
||||
uni.showToast({
|
||||
title: '取消成功', icon: 'success', duration: 2000
|
||||
});
|
||||
resolve(res);
|
||||
})
|
||||
.catch(err => {
|
||||
uni.showToast({
|
||||
title: '取消失败', icon: 'none', duration: 2000
|
||||
});
|
||||
reject(err);
|
||||
});
|
||||
} else if (res.cancel) {
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export function takeOrderHandle(orderId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
takeOrder(orderId)
|
||||
.then(res => {
|
||||
uni.showToast({
|
||||
title: '收货成功', icon: 'success', duration: 2000
|
||||
});
|
||||
resolve(res);
|
||||
})
|
||||
.catch(err => {
|
||||
uni.showToast({
|
||||
title: '收货失败', icon: 'none', duration: 2000
|
||||
});
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function delOrderHandle(orderId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dialog.confirm({
|
||||
mes: "确认删除该订单?",
|
||||
opts() {
|
||||
delOrder(orderId)
|
||||
.then(res => {
|
||||
uni.showToast({
|
||||
title: '删除成功', icon: 'success', duration: 2000
|
||||
});
|
||||
resolve(res);
|
||||
})
|
||||
.catch(err => {
|
||||
uni.showToast({
|
||||
title: '删除失败', icon: 'none', duration: 2000
|
||||
});
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function payOrderHandle(orderId, type, from) {
|
||||
console.log(orderId, type, from, '支付')
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showLoading({ title: '加载中' })
|
||||
payOrder(orderId, type, from)
|
||||
.then(res => {
|
||||
const data = res.data;
|
||||
uni.hideLoading()
|
||||
switch (data.status) {
|
||||
case "WECHAT_H5_PAY":
|
||||
location.replace(data.result.jsConfig.mweb_url);
|
||||
reject(data);
|
||||
break;
|
||||
case "ORDER_EXIST":
|
||||
case "EXTEND_ORDER":
|
||||
case "PAY_ERROR":
|
||||
case "PAY_DEFICIENCY":
|
||||
uni.showToast({ title: res.msg, icon: 'none', duration: 2000 });
|
||||
reject(data);
|
||||
break;
|
||||
case "SUCCESS":
|
||||
uni.showToast({ title: res.msg, icon: 'none', duration: 2000 });
|
||||
resolve(data);
|
||||
break;
|
||||
case "WECHAT_PAY":
|
||||
weappPay(data.result.jsConfig).then(res => {
|
||||
resolve(data);
|
||||
});
|
||||
break;
|
||||
case "WECHAT_APP_PAY":
|
||||
weappPay(data.result.jsConfig).then(res => {
|
||||
resolve(data);
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '订单支付失败', icon: 'none', duration: 2000
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function yuePayOrderHandle(orderId, type, from, passwordPay) {
|
||||
console.log(orderId, type, from, '支付')
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showLoading({ title: '加载中' })
|
||||
yuePayOrder(orderId, type, from, passwordPay)
|
||||
.then(res => {
|
||||
const data = res.data;
|
||||
uni.hideLoading()
|
||||
switch (data.status) {
|
||||
case "WECHAT_H5_PAY":
|
||||
location.replace(data.result.jsConfig.mweb_url);
|
||||
reject(data);
|
||||
break;
|
||||
case "ORDER_EXIST":
|
||||
case "EXTEND_ORDER":
|
||||
case "PAY_ERROR":
|
||||
case "PAY_DEFICIENCY":
|
||||
uni.showToast({ title: res.msg, icon: 'none', duration: 2000 });
|
||||
reject(data);
|
||||
break;
|
||||
case "SUCCESS":
|
||||
uni.showToast({ title: res.msg, icon: 'none', duration: 2000 });
|
||||
resolve(data);
|
||||
break;
|
||||
case "WECHAT_PAY":
|
||||
weappPay(data.result.jsConfig).then(res => {
|
||||
resolve(data);
|
||||
});
|
||||
break;
|
||||
case "WECHAT_APP_PAY":
|
||||
weappPay(data.result.jsConfig).then(res => {
|
||||
resolve(data);
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: err.msg||'订单支付失败', icon: 'none', duration: 2000
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
import { subscribeMessage } from '@/libs/order'
|
||||
import { getProvider } from '@/utils'
|
||||
import WechatJSSDK from 'wechat-jssdk/dist/client.umd'
|
||||
import { getWechatConfig, wechatAuth } from '@/api/public'
|
||||
import { parseQuery } from '@/utils'
|
||||
import cookie from '@/utils/store/cookie'
|
||||
import store from '@/store'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
// 支付模块
|
||||
export const weappPay = option => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (store.state.$deviceType == 'weixinh5') {
|
||||
setTimeout(() => {
|
||||
location.href = option.mweb_url
|
||||
}, 100)
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
if (store.state.$deviceType == 'weixin') {
|
||||
pay(option)
|
||||
.then(() => {
|
||||
uni.showToast({
|
||||
title: '支付成功',
|
||||
icon: 'success',
|
||||
duration: 5000,
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
.finally(res => {
|
||||
//if(typeof(res) == "undefined") return
|
||||
})
|
||||
.catch(function() {
|
||||
uni.showToast({ title: '支付失败', icon: 'none', duration: 5000 })
|
||||
reject()
|
||||
})
|
||||
return
|
||||
}
|
||||
// 吊起微信支付
|
||||
// getProvider('payment').then(provider => {
|
||||
let orderInfo = {
|
||||
appid: option.appid,
|
||||
noncestr: option.noncestr,
|
||||
package: option.package,
|
||||
partnerid: option.partnerid,
|
||||
prepayid: option.prepayid,
|
||||
sign: option.sign,
|
||||
timestamp: option.timestamp + '',
|
||||
}
|
||||
// 调用只接口
|
||||
uni.requestPayment({
|
||||
provider: 'wxpay',
|
||||
...option,
|
||||
timestamp: orderInfo.timestamp,
|
||||
orderInfo,
|
||||
success: success => {
|
||||
console.log(success)
|
||||
uni.showToast({
|
||||
title: '支付成功',
|
||||
icon: 'success',
|
||||
duration: 5000,
|
||||
})
|
||||
let time = setTimeout(() => {
|
||||
clearTimeout(time)
|
||||
resolve(success)
|
||||
}, 3000)
|
||||
// #ifdef MP-WEIXIN
|
||||
subscribeMessage()
|
||||
// #endif
|
||||
},
|
||||
fail: error => {
|
||||
console.log(error)
|
||||
if (error.errMsg == 'requestPayment:fail cancel') {
|
||||
uni.showToast({ title: '已取消支付', icon: 'none', duration: 5000 })
|
||||
} else {
|
||||
uni.showToast({ title: error || error.msg, icon: 'none', duration: 5000 })
|
||||
}
|
||||
reject(error)
|
||||
},
|
||||
})
|
||||
// })
|
||||
})
|
||||
}
|
||||
|
||||
const STATE_KEY = 'wx_authorize_state'
|
||||
const WX_AUTH = 'wx_auth'
|
||||
const BACK_URL = 'login_back_url'
|
||||
const LOGINTYPE = 'loginType'
|
||||
let instance
|
||||
let wechatObj
|
||||
let appId
|
||||
let wechatLoading = false
|
||||
|
||||
export function wechat() {
|
||||
console.log('初始化微信配置')
|
||||
wechatLoading = false
|
||||
return new Promise((resolve, reject) => {
|
||||
if (instance) return resolve(instance)
|
||||
getWechatConfig()
|
||||
.then(res => {
|
||||
console.log(res.data)
|
||||
const _wx = WechatJSSDK(res.data)
|
||||
console.log(_wx)
|
||||
appId = res.data.appId
|
||||
wechatObj = _wx
|
||||
_wx
|
||||
.initialize()
|
||||
.then(() => {
|
||||
instance = _wx.wx
|
||||
instance.initConfig = res.data
|
||||
resolve(instance)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
uni.showToast({
|
||||
title: error,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
reject()
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
reject()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function clearAuthStatus() {
|
||||
cookie.remove(WX_AUTH)
|
||||
cookie.remove(STATE_KEY)
|
||||
}
|
||||
|
||||
export async function oAuth() {
|
||||
console.log('处理微信授权')
|
||||
console.log(store)
|
||||
console.log(store.state)
|
||||
return new Promise((resolve, reject) => {
|
||||
// if (cookie.has(WX_AUTH)) {
|
||||
if (cookie.has(WX_AUTH) && store.state.token) {
|
||||
reject()
|
||||
return
|
||||
}
|
||||
const { code } = parseQuery()
|
||||
if (!code) {
|
||||
toAuth()
|
||||
return
|
||||
} else {
|
||||
auth(code)
|
||||
}
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
export async function auth(code) {
|
||||
console.log('获取微信授权')
|
||||
return new Promise((resolve, reject) => {
|
||||
let loginType = cookie.get(LOGINTYPE)
|
||||
let spread = cookie.get('spread')
|
||||
console.log('微信授权登录前获取spread', spread)
|
||||
wechatAuth(code, spread, loginType)
|
||||
.then(({ data }) => {
|
||||
console.log(data)
|
||||
const expires_time = dayjs(data.expires_time)
|
||||
const newTime = Math.round(new Date() / 1000)
|
||||
store.commit('login', data.token, expires_time - newTime)
|
||||
cookie.set(WX_AUTH, code, expires_time)
|
||||
cookie.remove(STATE_KEY)
|
||||
loginType && cookie.remove(LOGINTYPE)
|
||||
console.log('微信公众号授权登录,获取用户信息')
|
||||
store.dispatch('getUser').finally(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
.catch(reject)
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
export async function toAuth() {
|
||||
if (wechatLoading) {
|
||||
return
|
||||
}
|
||||
wechatLoading = true
|
||||
wechat().then(wx => {
|
||||
location.href = getAuthUrl(appId)
|
||||
})
|
||||
}
|
||||
|
||||
function getAuthUrl(appId) {
|
||||
// const redirect_uri = encodeURIComponent(window.location.href);
|
||||
// const redirect_uri = encodeURIComponent(`${location.origin}/pages/Loading/index`);
|
||||
|
||||
// #ifdef H5
|
||||
// #endif
|
||||
cookie.set('redirect', window.location.href)
|
||||
const redirect_uri = encodeURIComponent(`${location.origin}/pages/Loading/index`)
|
||||
// const redirect_uri = encodeURIComponent(`${location.origin}/pages/Loading/index?path=${encodeURIComponent(window.location.href)}`);
|
||||
// const redirect_uri = encodeURIComponent(`${window.location.origin}${window.location.pathname}`)
|
||||
// const redirect_uri = encodeURIComponent(`${location.origin}`)
|
||||
cookie.remove(BACK_URL)
|
||||
const state = 'STATE'
|
||||
// const state = encodeURIComponent(("" + Math.random()).split(".")[1] + "authorizestate");
|
||||
cookie.set(STATE_KEY, state)
|
||||
return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
|
||||
}
|
||||
|
||||
function toPromise(fn, config = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fn({
|
||||
...config,
|
||||
success(res) {
|
||||
resolve(res)
|
||||
},
|
||||
fail(err) {
|
||||
reject(err)
|
||||
},
|
||||
complete(err) {
|
||||
reject(err)
|
||||
},
|
||||
cancel(err) {
|
||||
reject(err)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function pay(config) {
|
||||
console.log(instance)
|
||||
return toPromise(instance.chooseWXPay, config)
|
||||
}
|
||||
|
||||
export function openAddress() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wechatEvevt('openAddress', {})
|
||||
.then(res => {
|
||||
resolve(res)
|
||||
})
|
||||
.catch(res => {
|
||||
if (res.is_ready) {
|
||||
res.wx.openAddress({
|
||||
fail(res) {
|
||||
reject(res)
|
||||
},
|
||||
success(res) {
|
||||
resolve(res)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function openShareAll(config) {
|
||||
config || {}
|
||||
config.type = config.type == undefined ? 'link' : config.type
|
||||
return new Promise(resolve => {
|
||||
getWechatConfig().then(res => {
|
||||
wechatObj.signSignature({
|
||||
nonceStr: res.data.nonceStr,
|
||||
signature: res.data.signature,
|
||||
timestamp: res.data.timestamp,
|
||||
})
|
||||
instance = wechatObj.getOriginalWx()
|
||||
instance.ready(() => {
|
||||
instance.updateAppMessageShareData(config)
|
||||
instance.updateTimelineShareData(config)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function openShareAppMessage(config) {
|
||||
instance.updateAppMessageShareData(config)
|
||||
instance.onMenuShareAppMessage && instance.onMenuShareAppMessage(config)
|
||||
}
|
||||
|
||||
export function openShareTimeline(config) {
|
||||
instance.updateTimelineShareData(config)
|
||||
instance.onMenuShareTimeline && instance.onMenuShareTimeline(config)
|
||||
}
|
||||
|
||||
export function wechatEvevt(name, config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let wx
|
||||
let configDefault = {
|
||||
fail(res) {
|
||||
if (wx) return reject({ is_ready: true, wx: wx })
|
||||
getWechatConfig().then(res => {
|
||||
wechatObj.signSignature({
|
||||
nonceStr: res.data.nonceStr,
|
||||
signature: res.data.signature,
|
||||
timestamp: res.data.timestamp,
|
||||
})
|
||||
wx = wechatObj.getOriginalWx()
|
||||
reject({ is_ready: true, wx: wx })
|
||||
})
|
||||
},
|
||||
success(res) {
|
||||
resolve(res)
|
||||
},
|
||||
}
|
||||
Object.assign(configDefault, config)
|
||||
if (typeof instance !== 'undefined') {
|
||||
instance.ready(() => {
|
||||
if (typeof name === 'object') {
|
||||
name.forEach(item => {
|
||||
instance[item] && instance[item](configDefault)
|
||||
})
|
||||
} else instance[name] && instance[name](configDefault)
|
||||
})
|
||||
} else {
|
||||
getWechatConfig().then(res => {
|
||||
const _wx = WechatJSSDK(res.data)
|
||||
_wx.initialize().then(() => {
|
||||
instance = _wx.getOriginalWx()
|
||||
instance.ready(() => {
|
||||
if (typeof name === 'object') {
|
||||
name.forEach(item => {
|
||||
instance[item] && instance[item](configDefault)
|
||||
})
|
||||
} else instance[name] && instance[name](configDefault)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function ready() {
|
||||
return new Promise(resolve => {
|
||||
if (typeof instance !== 'undefined') {
|
||||
instance.ready(() => {
|
||||
resolve(instance)
|
||||
})
|
||||
} else {
|
||||
getWechatConfig().then(res => {
|
||||
const _wx = WechatJSSDK(res.data)
|
||||
_wx.initialize().then(() => {
|
||||
instance = _wx.wx
|
||||
instance.ready(() => {
|
||||
resolve(instance)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function wxShowLocation() {
|
||||
return new Promise(() => {
|
||||
wechatEvevt('getLocation', { type: 'wgs84' })
|
||||
.then(res => {
|
||||
let latitude = res.latitude // 纬度
|
||||
let longitude = res.longitude // 经度
|
||||
cookie.set(LATITUDE, latitude)
|
||||
cookie.set(LONGITUDE, longitude)
|
||||
})
|
||||
.catch(res => {
|
||||
if (res.is_ready) {
|
||||
res.wx.getLocation({
|
||||
success(res) {
|
||||
let latitude = res.latitude // 纬度
|
||||
let longitude = res.longitude // 经度
|
||||
cookie.set(LATITUDE, latitude)
|
||||
cookie.set(LONGITUDE, longitude)
|
||||
},
|
||||
cancel() {
|
||||
cookie.remove(LATITUDE)
|
||||
cookie.remove(LONGITUDE)
|
||||
uni.showToast({
|
||||
title: '取消获取位置',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
},
|
||||
fail() {
|
||||
cookie.remove(LATITUDE)
|
||||
cookie.remove(LONGITUDE)
|
||||
uni.showToast({
|
||||
title: '授权失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import global from "./assets/script/global";
|
||||
import store from "./store";
|
||||
import schema from "async-validator";
|
||||
import dialog from "./utils/dialog";
|
||||
import mixin from '@/mixins/share-mixins.js'
|
||||
import {_router, parseRoute} from "@/utils";
|
||||
import {
|
||||
SERVICE_API_URL,
|
||||
SOCKET_URL,
|
||||
STATIC_RESOURCE_URL,
|
||||
VUE_APP_API_URL,
|
||||
VUE_APP_RESOURCES_URL,
|
||||
WX_LIVE_APPID
|
||||
} from "@/config";
|
||||
import uView from '@/uni_modules/uview-ui'
|
||||
|
||||
Vue.use(uView);
|
||||
Vue.mixin(mixin)
|
||||
uni.$u.setConfig({
|
||||
// 修改$u.config对象的属性
|
||||
config: {
|
||||
// 修改默认单位为rpx,相当于执行 uni.$u.config.unit = 'rpx'
|
||||
unit: 'rpx'
|
||||
},
|
||||
// 修改$u.props对象的属性
|
||||
props: {
|
||||
// 修改radio组件的size参数的默认值,相当于执行 uni.$u.props.radio.size = 30
|
||||
radio: {
|
||||
size: 30,
|
||||
labelSize: 30,
|
||||
iconSize: 25
|
||||
},
|
||||
icon: {
|
||||
size: 40,
|
||||
fontSize: 30
|
||||
},
|
||||
picker: {
|
||||
fontSize: 30
|
||||
},
|
||||
button: {
|
||||
fontSize: 30,
|
||||
iconSize: 30
|
||||
},
|
||||
navbar: {
|
||||
leftIconSize: 40,
|
||||
titleStyle: {
|
||||
fontSize: '30rpx'
|
||||
}
|
||||
},
|
||||
tabbar: {
|
||||
size: 30
|
||||
},
|
||||
tabs: {
|
||||
lineWidth: 40
|
||||
},
|
||||
search: {
|
||||
searchIconSize: 40
|
||||
},
|
||||
codeInput: {
|
||||
size: 60
|
||||
},
|
||||
calendar: {
|
||||
rowHeight: 112
|
||||
},
|
||||
empty: {
|
||||
textSize: 28
|
||||
},
|
||||
checkbox: {
|
||||
fontSize: 30,
|
||||
size: 30,
|
||||
labelSize: 24,
|
||||
iconSize: 30
|
||||
}
|
||||
// 其他组件属性配置
|
||||
// ......
|
||||
}
|
||||
})
|
||||
|
||||
// 注册全局组件
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.config.devtools = process.env.NODE_ENV !== "production";
|
||||
|
||||
Vue.prototype.$SERVICE_API_URL = SERVICE_API_URL
|
||||
Vue.prototype.$SOCKET_URL = SOCKET_URL
|
||||
Vue.prototype.$VUE_APP_RESOURCES_URL = VUE_APP_RESOURCES_URL
|
||||
Vue.prototype.$STATIC_RESOURCE_URL = STATIC_RESOURCE_URL
|
||||
Vue.prototype.$VUE_APP_API_URL = VUE_APP_API_URL
|
||||
Vue.prototype.$WX_LIVE_APPID = WX_LIVE_APPID
|
||||
Vue.prototype.$global = global;
|
||||
|
||||
Vue.prototype.$validator = function (rule) {
|
||||
return new schema(rule);
|
||||
};
|
||||
|
||||
// const CACHE_KEY = "clear_0.0.1";
|
||||
|
||||
// if (!cookie.has(CACHE_KEY)) {
|
||||
// cookie.clearAll();
|
||||
// cookie.set(CACHE_KEY, 1);
|
||||
// }
|
||||
//强制转为几位小数,不足的补0
|
||||
var forceToDecimal = function changeToDecimal(x, len, ignore = false) {
|
||||
var f_x = parseFloat(x);
|
||||
if (isNaN(f_x)) {
|
||||
return 0;
|
||||
}
|
||||
var f = Math.round(f_x * 100) / 100;
|
||||
if (ignore) {
|
||||
f = Math.floor(f_x * 100) / 100;
|
||||
}
|
||||
var s = f.toString();
|
||||
var rs = s.indexOf('.');
|
||||
if (rs < 0) {
|
||||
rs = s.length;
|
||||
s += '.';
|
||||
}
|
||||
while (s.length <= rs + len) {
|
||||
s += '0';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
//强制转为2位小数,不足的补0
|
||||
var force2Decimal = function change2Decimal(x, ignore = false) {
|
||||
return forceToDecimal(x, 2, false);
|
||||
}
|
||||
|
||||
Vue.prototype.$force2Decimal = force2Decimal
|
||||
Vue.prototype.$forceToDecimal = forceToDecimal
|
||||
|
||||
Vue.config.productionTip = false
|
||||
App.mpType = 'app'
|
||||
Vue.prototype.$store = store
|
||||
Vue.prototype.$dialog = dialog;
|
||||
|
||||
|
||||
const app = new Vue(App)
|
||||
|
||||
Vue.mixin({
|
||||
onLoad() {
|
||||
const {
|
||||
$mp
|
||||
} = this.$root
|
||||
this._route = parseRoute($mp)
|
||||
this._data.$VUE_APP_RESOURCES_URL = VUE_APP_RESOURCES_URL;
|
||||
},
|
||||
onShow() {
|
||||
_router.app = this
|
||||
_router.currentRoute = this._route
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(Vue.prototype, '$yrouter', {
|
||||
get() {
|
||||
return _router
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(Vue.prototype, '$yroute', {
|
||||
get() {
|
||||
return this._route
|
||||
}
|
||||
})
|
||||
|
||||
// #ifdef H5
|
||||
// H5编译的代码
|
||||
Vue.prototype.$deviceType = 'h5'
|
||||
store.commit('updateDevicetype', 'h5')
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// App平台编译的代码
|
||||
Vue.prototype.$deviceType = 'app'
|
||||
store.commit('updateDevicetype', 'app')
|
||||
Vue.prototype.$platform = uni.getSystemInfoSync().platform
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序编译的代码
|
||||
Vue.prototype.$deviceType = 'routine'
|
||||
store.commit('updateDevicetype', 'routine')
|
||||
// #endif
|
||||
|
||||
app.$mount()
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
{
|
||||
"name": "云南香道滇官方商城",
|
||||
"appid": "__UNI__C7A519E",
|
||||
"description": "",
|
||||
"versionName": "4.0.0",
|
||||
"versionCode": 4,
|
||||
"transformPx": false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus": {
|
||||
"usingComponents": true,
|
||||
"nvueCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": false,
|
||||
"waiting": false,
|
||||
"autoclose": true,
|
||||
"delay": 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules": {
|
||||
"OAuth": {},
|
||||
"Payment": {},
|
||||
"Share": {}
|
||||
},
|
||||
/* 应用发布信息 */
|
||||
"distribute": {
|
||||
/* android打包配置 */
|
||||
"android": {
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios": {},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs": {
|
||||
"oauth": {
|
||||
"weixin": {
|
||||
"appid": "wx7c84ede33062d1e4",
|
||||
"appsecret": "c47ef66d3311194da44e60387d5c1abd",
|
||||
"UniversalLinks": ""
|
||||
}
|
||||
},
|
||||
"payment": {
|
||||
"weixin": {
|
||||
"appid": "wx7c84ede33062d1e4",
|
||||
"UniversalLinks": ""
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"weixin": {
|
||||
"appid": "wx7c84ede33062d1e4",
|
||||
"UniversalLinks": ""
|
||||
}
|
||||
},
|
||||
"ad": {}
|
||||
},
|
||||
"splashscreen": {
|
||||
"ios": {
|
||||
"iphone": {
|
||||
"portrait-896h@3x": "unpackage/res/splash/1242+2688.png",
|
||||
"portrait-896h@2x": "unpackage/res/splash/828+1792.png",
|
||||
"iphonex": "unpackage/res/splash/1125+2436.png",
|
||||
"retina55": "unpackage/res/splash/1142+2208.png",
|
||||
"retina47": "unpackage/res/splash/750+1334.png",
|
||||
"retina40": "unpackage/res/splash/640+1136.png",
|
||||
"retina35": "unpackage/res/splash/640+960.png"
|
||||
}
|
||||
},
|
||||
"android": {
|
||||
"hdpi": "unpackage/res/splash/480+762.png",
|
||||
"xhdpi": "unpackage/res/splash/720+1242.png",
|
||||
"xxhdpi": "unpackage/res/splash/1080+1882.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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp": {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin": {
|
||||
"appid": "wx9417047e1a0c340b",
|
||||
"setting": {
|
||||
"urlCheck": false
|
||||
},
|
||||
"usingComponents": true,
|
||||
"permission": {
|
||||
"scope.userLocation": {
|
||||
"desc": "你的位置信息将用于小程序位置接口的效果展示"
|
||||
}
|
||||
},
|
||||
"requiredPrivateInfos": [
|
||||
"getLocation",
|
||||
"chooseLocation"
|
||||
],
|
||||
"plugins": {
|
||||
"live-player-plugin": {
|
||||
"version": "1.3.5",
|
||||
//最新直播组件版本号
|
||||
"provider": "wx2b03c6e691cd7370"
|
||||
//直播appid
|
||||
}
|
||||
},
|
||||
"optimization": {
|
||||
"subPackages": true
|
||||
}
|
||||
},
|
||||
"mp-alipay": {
|
||||
"usingComponents": true,
|
||||
"appid": ""
|
||||
},
|
||||
"mp-baidu": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-toutiao": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"vueVersion": "2"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
disabled: false,
|
||||
text: "获取验证码"
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
sendCode() {
|
||||
if (this.disabled) return;
|
||||
this.disabled = true;
|
||||
let n = 60;
|
||||
this.text = "剩余 " + n + "s";
|
||||
const run = setInterval(() => {
|
||||
n = n - 1;
|
||||
if (n < 0) {
|
||||
clearInterval(run);
|
||||
}
|
||||
this.text = "剩余 " + n + "s";
|
||||
if (this.text < "剩余 " + 0 + "s") {
|
||||
this.disabled = false;
|
||||
this.text = "重新获取";
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
//封装下拉刷新混入对象
|
||||
export default ({ getPageData}) => {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
mescroll: null, //mescroll实例对象
|
||||
};
|
||||
},
|
||||
//注册滚动到底部的事件,用于上拉加载
|
||||
onReachBottom() {
|
||||
this.mescroll && this.mescroll.onReachBottom();
|
||||
},
|
||||
//注册列表滚动事件,用于下拉刷新
|
||||
onPageScroll(e) {
|
||||
this.mescroll && this.mescroll.onPageScroll(e);
|
||||
},
|
||||
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
|
||||
}
|
||||
},
|
||||
|
||||
/*下拉刷新的回调, 有三种处理方式: */
|
||||
downCallback(mescroll){
|
||||
// 第2种: 下拉刷新和上拉加载调同样的接口, 那以上请求可删, 直接用mescroll.resetUpScroll()代替
|
||||
mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
|
||||
},
|
||||
/*上拉加载的回调*/
|
||||
upCallback(mescroll) {
|
||||
getPageData.call(this,mescroll)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
export default{
|
||||
data(){
|
||||
return {
|
||||
//设置默认的分享参数
|
||||
share:{
|
||||
title:'云南香道滇官方商城',
|
||||
path:'/pages/Loading/index',
|
||||
imageUrl:'http://admin-api.xdd618.com/file/pic/20210617211221217375.jpg',
|
||||
desc:'',
|
||||
content:''
|
||||
}
|
||||
}
|
||||
},
|
||||
onShareAppMessage(res) {
|
||||
return {
|
||||
title:this.share.title,
|
||||
path:this.share.path,
|
||||
imageUrl:this.share.imageUrl,
|
||||
desc:this.share.desc,
|
||||
content:this.share.content,
|
||||
success(res){
|
||||
uni.showToast({
|
||||
title:'分享成功'
|
||||
})
|
||||
},
|
||||
fail(res){
|
||||
uni.showToast({
|
||||
title:'分享失败',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
{
|
||||
"name": "xdd_mp",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "npm run dev:h5",
|
||||
"build": "npm run build:h5",
|
||||
"build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
|
||||
"build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
|
||||
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build",
|
||||
"build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
|
||||
"build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
|
||||
"build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
|
||||
"build:mp-jd": "cross-env NODE_ENV=production UNI_PLATFORM=mp-jd vue-cli-service uni-build",
|
||||
"build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
|
||||
"build:mp-lark": "cross-env NODE_ENV=production UNI_PLATFORM=mp-lark vue-cli-service uni-build",
|
||||
"build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
|
||||
"build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
|
||||
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
|
||||
"build:mp-xhs": "cross-env NODE_ENV=production UNI_PLATFORM=mp-xhs vue-cli-service uni-build",
|
||||
"build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
|
||||
"build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
|
||||
"build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
|
||||
"build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
|
||||
"dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
|
||||
"dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
|
||||
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve",
|
||||
"dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
|
||||
"dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
|
||||
"dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
|
||||
"dev:mp-jd": "cross-env NODE_ENV=development UNI_PLATFORM=mp-jd vue-cli-service uni-build --watch",
|
||||
"dev:mp-kuaishou": "cross-env NODE_ENV=development UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build --watch",
|
||||
"dev:mp-lark": "cross-env NODE_ENV=development UNI_PLATFORM=mp-lark vue-cli-service uni-build --watch",
|
||||
"dev:mp-qq": "cross-env NODE_ENV=development UNI_PLATFORM=mp-qq vue-cli-service uni-build --watch",
|
||||
"dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
|
||||
"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize",
|
||||
"dev:mp-xhs": "cross-env NODE_ENV=development UNI_PLATFORM=mp-xhs vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-native": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-native vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-webview": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-webview-huawei": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build --watch",
|
||||
"dev:quickapp-webview-union": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build --watch",
|
||||
"info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
|
||||
"serve:quickapp-native": "node node_modules/@dcloudio/uni-quickapp-native/bin/serve.js",
|
||||
"test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
|
||||
"test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
|
||||
"test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
|
||||
"test:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu jest -i",
|
||||
"test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app-plus": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-h5": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-helper-json": "*",
|
||||
"@dcloudio/uni-i18n": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-360": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-alipay": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-baidu": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-jd": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-kuaishou": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-lark": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-qq": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-toutiao": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-vue": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-weixin": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-mp-xhs": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-quickapp-native": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-quickapp-webview": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-stacktracey": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-stat": "^2.0.1-35320220729002",
|
||||
"@vue/shared": "^3.0.0",
|
||||
"animate.css": "^3.7.2",
|
||||
"async-validator": "^3.2.4",
|
||||
"core-js": "^3.6.5",
|
||||
"dayjs": "^1.11.2",
|
||||
"flyio": "^0.6.2",
|
||||
"jweixin-module": "^1.6.0",
|
||||
"miniapp-color-thief": "^1.0.5",
|
||||
"number-precision": "^1.5.2",
|
||||
"regenerator-runtime": "^0.12.1",
|
||||
"uview-ui": "2.0.31",
|
||||
"vconsole": "^3.14.6",
|
||||
"vue": "^2.6.11",
|
||||
"vue-ydui": "^1.2.6",
|
||||
"vuex": "^3.2.0",
|
||||
"wechat-jssdk": "^5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "~7.17.9",
|
||||
"@dcloudio/types": "^3.0.4",
|
||||
"@dcloudio/uni-automator": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-cli-i18n": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-cli-shared": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-migration": "^2.0.1-35320220729002",
|
||||
"@dcloudio/uni-template-compiler": "^2.0.1-35320220729002",
|
||||
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.1-35320220729002",
|
||||
"@dcloudio/vue-cli-plugin-uni": "^2.0.1-35320220729002",
|
||||
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.1-35320220729002",
|
||||
"@dcloudio/webpack-uni-mp-loader": "^2.0.1-35320220729002",
|
||||
"@dcloudio/webpack-uni-pages-loader": "^2.0.1-35320220729002",
|
||||
"@vue/cli-plugin-babel": "~4.5.19",
|
||||
"@vue/cli-service": "~4.5.19",
|
||||
"babel-plugin-import": "^1.11.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"jest": "^25.4.0",
|
||||
"less": "^4.1.0",
|
||||
"less-loader": "^4.1.0",
|
||||
"mini-types": "*",
|
||||
"miniprogram-api-typings": "*",
|
||||
"postcss-comment": "^2.0.0",
|
||||
"sass": "^1.5.0",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"Android >= 4.4",
|
||||
"ios >= 9"
|
||||
],
|
||||
"resolutions": {
|
||||
"@babel/runtime": "~7.17.9"
|
||||
},
|
||||
"uni-app": {
|
||||
"scripts": {}
|
||||
}
|
||||
}
|
||||
+894
@@ -0,0 +1,894 @@
|
||||
{
|
||||
"easycom": {
|
||||
"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/Loading/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "香道滇",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/authorization/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "微信授权"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/Login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/RetrievePassword/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "重置密码"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/home/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "香道滇",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "components/chose-city/chose-city",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择城市"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/foodInfomation/information",
|
||||
"style": {
|
||||
"navigationBarTitleText": "彩云资讯"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/foodInfomation/foodInfomation",
|
||||
"style": {
|
||||
"navigationBarTitleText": "美食攻略",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/foodInfomation/foodInfomationDetail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "资讯详情",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodSearch/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "搜索商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodsClass/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分类",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/ShoppingCart/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "购物车",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/StoreList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商家列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodsList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/NotDefined/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "404"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "path": "pages/cloud/cloud",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "七彩云上"
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"path": "pages/cloud/haveFun",
|
||||
"style": {
|
||||
"navigationBarTitleText": "寻趣味",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/User/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodsCollection/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品收藏"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/EvaluateList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "评价列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodsEvaluate/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品评价"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodsPromotion/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "促销商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/HotNewGoods/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "热门商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/shop/GoodsCon/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品详情",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/address/AddAddress/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增收货地址"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/UserAccount/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "账户余额"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/address/AddressManagement/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "收货地址"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/Poster/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "推广名片"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/signIn/Sign/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "签到"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/signIn/SignRecord/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "签到记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/CashAudit/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现结果"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/PromoterOrder/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分销详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/PromoterList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "分销列表",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/UserPromotion/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队管理",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/UserBill/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "账单记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/CashRecord/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/CommissionDetails/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "佣金明细"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/signIn/Integral/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的积分"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/UserVip/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户vip"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/coupon/UserCoupon/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "优惠券"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/coupon/GetCoupon/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "领取优惠券"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/promotion/UserCash/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/CustomerList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "客服列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/Recharge/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "充值"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/MyOrder/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"navigationBarBackgroundColor": "#fff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/Logistics/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "查看物流"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/OrderDetails/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/OrderSubmission/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提交订单",
|
||||
"navigationBarBackgroundColor": "#fff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/PaymentStatus/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "支付状态"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/GoodsReturn/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品退货"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/ReturnList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "退货列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orderAdmin/OrderIndex/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商家订单统计"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orderAdmin/AdminOrderList/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orderAdmin/GoodsDeliver/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发货"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orderAdmin/AdminOrder/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商家订单列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orderAdmin/Statistics/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商家统计数据"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orderAdmin/OrderCancellation/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商家取消订单"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/Poster/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "推广海报"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/DargainDetails/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "帮砍价"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/GoodsBargain/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "砍价列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/BargainRecord/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "砍价记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/GoodsGroup/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团购商品列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/GroupDetails/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团购商品详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/GroupRule/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团购规则"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/GoodsSeckill/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "秒杀"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/SeckillDetails/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "秒杀详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/map/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "地图"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/Message/Message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/MessageDetail/MessageDetail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息详情",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/MemberUpgradeIntro/MemberUpgradeIntro",
|
||||
"style": {
|
||||
"navigationBarTitleText": "升级说明",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/VideoPlayBackList/VideoPlayBackList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "寻看点",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/VideoPlayBackList/VideoPlayBackDetail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "视频详情",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/UpgradeElectronicProtocol/UpgradeElectronicProtocol",
|
||||
"style": {
|
||||
"navigationBarTitleText": "升级",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/UpgradeProtocolDetail/UpgradeProtocolDetail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "共享股东协议"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/LiveAndShortVideo/LiveAndShortVideo",
|
||||
"style": {
|
||||
"navigationBarTitleText": "直播/视频"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/LiveBroadcastList/LiveBroadcastList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "直播列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/UserFavorite/UserFavorite",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的喜欢"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pagesInn",
|
||||
"pages": [
|
||||
{
|
||||
"path": "inn/innHome",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "inn/innApply",
|
||||
"style": {
|
||||
"navigationBarTitleText": "店铺申请"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "inn/innInfoPublish",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发布店铺动态"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "inn/innProfileEdit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改店铺资料"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "inn/innInfoDetail",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "inn/innPartnerAgreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "店主合伙人条款协议"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pkg_common",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/pdf",
|
||||
"style": {
|
||||
"navigationBarTitleText": "详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/room",
|
||||
"style": {
|
||||
"navigationBarTitleText": "客服"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/roomLogs",
|
||||
"style": {
|
||||
"navigationBarTitleText": "历史会话"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pkg_join",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商户入驻"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/shop",
|
||||
"style": {
|
||||
"navigationBarTitleText": "店铺入驻"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/supplier",
|
||||
"style": {
|
||||
"navigationBarTitleText": "供应商入驻"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pkg_orders",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/refund",
|
||||
"style": {
|
||||
"navigationBarTitleText": "申请退款"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pkg_product",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/healthList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "康养 · 食材"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/giftList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "定制礼品",
|
||||
"onReachBottomDistance": 100
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/famousList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "千县名品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/famousStory",
|
||||
"style": {
|
||||
"navigationBarTitleText": "名品故事"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/famousHot",
|
||||
"style": {
|
||||
"navigationBarTitleText": "爆款主推"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/scenicSpot",
|
||||
"style": {
|
||||
"navigationBarTitleText": "景点门票"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/seckill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "天天秒杀"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/sharpList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "尖货推荐"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/nativeList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "特产",
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarBackgroundColor": "#FF564A"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pkg_user",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/bankCardList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "银行卡列表",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/addBankCard",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加银行卡",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/withdrawal",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/personalData",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人资料"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/bindPhone",
|
||||
"style": {
|
||||
"navigationBarTitleText": "绑定手机号"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/payPassword",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改支付密码"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "v4",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/preSale/preSale",
|
||||
"style": {
|
||||
"navigationBarTitleText": "预售"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/seckill/seckill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "限时秒杀"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/offer/offer",
|
||||
"style": {
|
||||
"navigationBarTitleText": "每天特价"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "components/select-city",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/specialty/specialty",
|
||||
"style": {
|
||||
"navigationBarTitleText": "特产",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/shop/shop",
|
||||
"style": {
|
||||
"navigationBarTitleText": "店铺"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/shop/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "店铺详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/trial/trial",
|
||||
"style": {
|
||||
"navigationBarTitleText": "体验店"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/live/live",
|
||||
"style": {
|
||||
"navigationBarTitleText": "直播"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/attract/attract",
|
||||
"style": {
|
||||
"navigationBarTitleText": "招商"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/attract/project",
|
||||
"style": {
|
||||
"navigationBarTitleText": "招商项目"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/attract/projectList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "招商项目"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/luck/luck",
|
||||
"style": {
|
||||
"navigationBarTitleText": "幸运抽奖"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/luck/logs",
|
||||
"style": {
|
||||
"navigationBarTitleText": "抽奖记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/luck/receive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择收货地址"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "wenwan",
|
||||
"pages": [
|
||||
{
|
||||
"path": "views/goodsClass",
|
||||
"style": {
|
||||
"navigationBarTitleText": "文玩"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/store",
|
||||
"style": {
|
||||
"navigationBarTitleText": "文玩店铺详情",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "views/storeList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "文玩"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"preloadRule": {
|
||||
"pages/user/User/index": {
|
||||
"network": "all",
|
||||
"packages": [
|
||||
"pagesInn"
|
||||
]
|
||||
}
|
||||
},
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#F8F8F8",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#8B8F99",
|
||||
"selectedColor": "#FF564A",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/index",
|
||||
"iconPath": "static/tabbar/icon-home.png",
|
||||
"selectedIconPath": "static/tabbar/icon-home-hot.png",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/shop/ShoppingCart/index",
|
||||
"iconPath": "static/tabbar/icon-cart.png",
|
||||
"selectedIconPath": "static/tabbar/icon-cart-hot.png",
|
||||
"text": "购物车"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/cloud/haveFun",
|
||||
"iconPath": "static/tabbar/icon-cloud.png",
|
||||
"selectedIconPath": "static/tabbar/icon-cloud-hot.png",
|
||||
"text": "寻趣味"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/user/User/index",
|
||||
"iconPath": "static/tabbar/icon-user.png",
|
||||
"selectedIconPath": "static/tabbar/icon-user-hot.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"condition": {
|
||||
"current": 0,
|
||||
"list": [
|
||||
{
|
||||
"name": "文玩店铺",
|
||||
"path": "wenwan/views/store",
|
||||
"query": "scene=id%3D74%26partnerId%3D5659"
|
||||
},
|
||||
{
|
||||
"name": "普通店铺",
|
||||
"path": "pagesInn/inn/innHome",
|
||||
"query": "scene=id%3D74%26partnerId%3D5659"
|
||||
},
|
||||
{
|
||||
"name": "商品详情",
|
||||
"path": "pages/shop/GoodsCon/index",
|
||||
"query": "scene=id%3D289%26partnerId%3D5675"
|
||||
},
|
||||
{
|
||||
"name": "DEBUG",
|
||||
"path": "pkg_product/views/giftList",
|
||||
"query": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user