diff --git a/README.md b/README.md index 95292b1..da1a1bd 100644 --- a/README.md +++ b/README.md @@ -169,11 +169,16 @@ lilishop-uniapp | ✅ 购物卡 | ✅ 腾讯云-云直播 | | ✅ 卡密商品 | ✅ 二级分销 | | ✅ 定时任务管理 | ✅ 预约服务商品 | -| ✅ 主题色管理 | ✅ 商家发货改造(整单/拆单/无需配送) | +| ✅ 主题色管理 | ✅ 新增无需配送发货方式 | | ✅ 多维统计(营业/商品/会员/积分/储值/营销/分销) | ✅ 经营报表(店铺业绩/商品同比环比/订单明细/销售汇总) | +| ✅ 分类页多模板设置 | ✅ 移动端楼层装修组件库 | +| ✅ PC端楼层装修重构 | ✅ 全站统一顶栏 | +| ✅ PC端店铺独立列表 | ✅ 敏感词场景化过滤 | ### 🔜 近期规划中 - 🔜 店铺结算汇总 & 结算台账 - 🔜 支付方式汇总 & 钱包流水 & 财务汇总 - 🔜 微信小店对接 - 🔜 抖店对接 +- 🔜 微信小程序原生重构与体验对齐 +- 🔜 OpenAPI 开放平台与开发者中心 diff --git a/api/message.js b/api/message.js index b46a606..e381e02 100644 --- a/api/message.js +++ b/api/message.js @@ -110,3 +110,15 @@ export function getLogisticsMessages(params) { data }); } + + +/** + * 获取微信公众号通知配置及绑定状态 + */ +export function getWechatOfficialAccountSetting() { + return http.request({ + url: '/message/wechatOfficialAccount', + method: Method.GET, + needToken: true, + }); +} \ No newline at end of file diff --git a/components/verification/verification.vue b/components/verification/verification.vue index ba2afa0..637c34c 100644 --- a/components/verification/verification.vue +++ b/components/verification/verification.vue @@ -101,12 +101,18 @@ const props = withDefaults(defineProps<{ left?: string top?: string business?: string + apiType?: string }>(), { height: '80rpx', width: '350rpx', left: '180rpx', top: '30rpx', - business: 'LOGIN' + business: 'LOGIN', + apiType: 'buyer' +}) + +const sliderBaseUrl = computed(() => { + return props.apiType === 'store' ? api.store : api.buyer }) const store = useStore() @@ -182,7 +188,7 @@ function getCode() { storage.setUuid(uuid.v1()) } uni.request({ - url: api.buyer + '/common/slider/' + props.business, + url: sliderBaseUrl.value + '/common/slider/' + props.business, header: { uuid: storage.getUuid() }, @@ -217,7 +223,7 @@ function end() { uni.request({ method: 'POST', url: - api.buyer + + sliderBaseUrl.value + '/common/slider/' + props.business + '?xPos=' + diff --git a/config/api.js b/config/api.js index 7c5dd28..e8ab044 100644 --- a/config/api.js +++ b/config/api.js @@ -1,35 +1,41 @@ -/** - * buyer : 买家 API(baseURL 为 buyer + /buyer) - */ -// 开发环境 -const dev = { - buyer: "https://buyer-api.pickmall.cn", - im: "https://im-api.pickmall.cn", - mqtt: "wss://lilishop-mqtt-pull.dllll.xyz/mqtt", -}; -// 生产环境 -const prod = { - buyer: "https://buyer-api.pickmall.cn", - im: "https://im-api.pickmall.cn", - mqtt: "wss://lilishop-mqtt-pull.dllll.xyz/mqtt", -}; - -let api = dev; -if (process.env.NODE_ENV == "development") { - api = dev; -} else { - api = prod; -} -// 微信小程序、App 打包使用生产环境配置 -// #ifdef MP-WEIXIN || APP-PLUS -api = prod; -// #endif - -const buyerHost = api.buyer; -api.buyer = buyerHost + "/buyer"; -api.im = api.im + "/im"; - -export default { - ...api, - buyerHost, -}; +/** + * buyer : 买家 API(baseURL 为 buyer + /buyer) + * store : 商家 API(baseURL 为 store + /store) + */ +// 开发环境 +const dev = { + buyer: "https://buyer-api.pickmall.cn", + store: "https://admin-api.pickmall.cn", + im: "https://im-api.pickmall.cn", + mqtt: "wss://lilishop-mqtt-pull.dllll.xyz/mqtt", +}; +// 生产环境 +const prod = { + buyer: "https://buyer-api.pickmall.cn", + store: "https://admin-api.pickmall.cn", + im: "https://im-api.pickmall.cn", + mqtt: "wss://lilishop-mqtt-pull.dllll.xyz/mqtt", +}; + +let api = dev; +if (process.env.NODE_ENV == "development") { + api = dev; +} else { + api = prod; +} +// 微信小程序、App 打包使用生产环境配置 +// #ifdef MP-WEIXIN || APP-PLUS +api = prod; +// #endif + +const buyerHost = api.buyer; +const storeHost = api.store; +api.buyer = buyerHost + "/buyer"; +api.store = storeHost + "/store"; +api.im = api.im + "/im"; + +export default { + ...api, + buyerHost, + storeHost, +}; diff --git a/js_sdk/lili-pay/wx-pay.js b/js_sdk/lili-pay/wx-pay.js index 1e9ffc7..7f14499 100644 --- a/js_sdk/lili-pay/wx-pay.js +++ b/js_sdk/lili-pay/wx-pay.js @@ -2,18 +2,105 @@ * 微信小程序支付 * 此处针对于微信小程序开发的支付插件 * 第一次支付成功后会跳出订阅的消息 如果用户拒绝或同意都会跳转到支付成功页面 - * 如果点击订阅 会将状态写进缓存 之后不再提醒。 - * - * @param {sn,price} + * + * @param {sn,price,orderPromotionType} */ import { getWeChatMpMessage } from "@/api/message.js"; import { initiatePay } from "@/api/trade"; + +const SCENE_NAME_MAP = { + ORDER_DELIVER: ["订单发货提醒", "订单发货通知"], + ORDER_COMPLETE: ["订单已完成提醒", "订单完成通知"], + PINTUAN_SUCCESS: ["拼团成功通知"], + AFTER_SALE_AUDIT: ["售后申请审核通知", "售后审核通知"], + POINT_CHANGE: ["积分变更提醒", "积分变更通知"], + WALLET_CHANGE: ["账户余额变动通知", "余额账户变更通知"], + WALLET_WITHDRAWAL_AUDIT_ERROR: ["提现结果通知", "提现申请审核通知"], +}; + +let cachedMpMessageList = null; + +export function prefetchMpMessageTemplates() { + return getWeChatMpMessage() + .then((res) => { + if (res && res.data && Array.isArray(res.data.result)) { + cachedMpMessageList = res.data.result; + } + return cachedMpMessageList || []; + }) + .catch(() => cachedMpMessageList || []); +} + +function extractTmplIdsByScenes(list, targetScenes) { + const tmplIds = []; + (targetScenes || []).forEach((scene) => { + const aliasNames = SCENE_NAME_MAP[scene] || []; + const matched = (list || []).find( + (item) => + item && + item.enable !== false && + item.code && + (item.sceneCode === scene || aliasNames.includes(item.name)) + ); + if (matched && matched.code && !tmplIds.includes(matched.code)) { + tmplIds.push(matched.code); + } + }); + return tmplIds.slice(0, 3); +} + +export function subscribeMpMessageByScenes(sceneCodes, onComplete) { + const done = typeof onComplete === "function" ? onComplete : () => {}; + const targetScenes = Array.isArray(sceneCodes) ? sceneCodes : []; + if (!targetScenes.length) { + done(); + return; + } + if (Array.isArray(cachedMpMessageList) && cachedMpMessageList.length) { + const finalTmplIds = extractTmplIdsByScenes(cachedMpMessageList, targetScenes); + if (!finalTmplIds.length) { + done(); + return; + } + console.log("[微信订阅消息] 请求订阅 tmplIds:", finalTmplIds); + uni.requestSubscribeMessage({ + tmplIds: finalTmplIds, + complete: (res) => { + console.log("[微信订阅消息] 授权返回结果:", res); + done(); + }, + }); + return; + } + prefetchMpMessageTemplates() + .then((list) => { + const finalTmplIds = extractTmplIdsByScenes(list, targetScenes); + if (!finalTmplIds.length) { + done(); + return; + } + uni.requestSubscribeMessage({ + tmplIds: finalTmplIds, + complete: () => done(), + }); + }) + .catch(() => done()); +} + +export function subscribePayMessageThen(onComplete, options = {}) { + const isPintuan = + options && + String(options.orderPromotionType || "").toUpperCase() === "PINTUAN"; + const scenes = isPintuan + ? ["ORDER_DELIVER", "ORDER_COMPLETE", "PINTUAN_SUCCESS"] + : ["ORDER_DELIVER", "ORDER_COMPLETE"]; + subscribeMpMessageByScenes(scenes, onComplete); +} + class LiLiWXPay { constructor(...payList) { this.data = payList[0]; - console.log(payList); - // 调用支付 this.pay = () => { uni.showLoading({ title: "加载中", @@ -26,7 +113,6 @@ class LiLiWXPay { }; const paymentMethod = "WECHAT"; const paymentClient = "MP"; - // 调用支付 initiatePay(paymentMethod, paymentClient, submitData).then((res) => { let response = res.data.result; uni.hideLoading(); @@ -38,16 +124,25 @@ class LiLiWXPay { package: response.package, signType: response.signType, paySign: response.paySign, - success: (e) => { + success: () => { uni.showToast({ icon: "none", title: "支付成功!", }); - sendMessage(payList[0].price); + subscribePayMessageThen( + () => { + uni.redirectTo({ + url: + "/pages/cart/payment/success?paymentMethod=WECHAT" + + "&payPrice=" + + payList[0].price, + }); + }, + { orderPromotionType: this.data.orderPromotionType } + ); }, fail: (e) => { this.exception = e; - // 支付异常或支付失败之后跳转到订单页面 uni.showModal({ content: "支付失败,如果您已支付,请勿反复支付", showCancel: false, @@ -64,41 +159,4 @@ class LiLiWXPay { } } -function sendMessage(price) { - - - //订阅消息 - getWeChatMpMessage().then((res) => { - var message = res.data.result; - var templateid = message.map((item) => item.code); - uni.requestSubscribeMessage({ - tmplIds: templateid, - success: (res) => { - - }, - fail: (res) => { - console.log('fail', res) - uni.showToast({ - icon: "none", - title: "订阅消息失败", - }) - }, - complete: (res) => { - console.log('complete', res) - - /** - * 已经支付成功 - */ - uni.redirectTo({ - url: - "/pages/cart/payment/success?paymentMethod=WECHAT" + - "&payPrice=" + - price, - }); - }, - }); - }); - -} - export default LiLiWXPay; diff --git a/pages-sub/merchant/api/evaluation.js b/pages-sub/merchant/api/evaluation.js new file mode 100644 index 0000000..029e16f --- /dev/null +++ b/pages-sub/merchant/api/evaluation.js @@ -0,0 +1,26 @@ +import { storeHttp } from "../utils/storeRequest.js"; + +/** + * 分页获取客户评价列表 + * @param {Object} params { grade, goodsName, replyStatus, pageNumber, pageSize } + */ +export function getStoreEvaluationList(params) { + return storeHttp.request({ + url: "/member/evaluation", + method: "GET", + params, + }); +} + +/** + * 回复客户评价 + * @param {String} id 评价ID + * @param {String} reply 回复文字 + * @param {String} replyImage 可选回复图片 + */ +export function replyStoreEvaluation(id, reply, replyImage = "") { + return storeHttp.request({ + url: `/member/evaluation/reply/${id}?reply=${encodeURIComponent(reply)}&replyImage=${encodeURIComponent(replyImage)}`, + method: "PUT", + }); +} diff --git a/pages-sub/merchant/api/goods.js b/pages-sub/merchant/api/goods.js new file mode 100644 index 0000000..19948d9 --- /dev/null +++ b/pages-sub/merchant/api/goods.js @@ -0,0 +1,70 @@ +import { storeHttp } from "../utils/storeRequest.js"; + +/** + * 获取商家商品列表 + * @param {Object} params { goodsName, marketEnable, authFlag, pageNumber, pageSize } + */ +export function getStoreGoodsList(params) { + return storeHttp.request({ + url: "/goods/goods/list", + method: "GET", + params, + }); +} + +/** + * 获取商家商品详情 + * @param {String} id 商品ID + */ +export function getStoreGoodsDetail(id) { + return storeHttp.request({ + url: `/goods/goods/get/${id}`, + method: "GET", + }); +} + +/** + * 获取商品关联的 SKU 列表 + * @param {String} goodsId 商品ID + */ +export function getStoreGoodsSkuList(goodsId) { + return storeHttp.request({ + url: `/goods/goods/sku/${goodsId}/list`, + method: "GET", + }); +} + +/** + * 上架商品 + * @param {String|Array} goodsId + */ +export function upGoods(goodsId) { + const ids = Array.isArray(goodsId) ? goodsId.join(",") : goodsId; + return storeHttp.request({ + url: `/goods/goods/up?goodsId=${ids}`, + method: "PUT", + }); +} + +/** + * 下架商品 + * @param {String|Array} goodsId + */ +export function downGoods(goodsId) { + const ids = Array.isArray(goodsId) ? goodsId.join(",") : goodsId; + return storeHttp.request({ + url: `/goods/goods/under?goodsId=${ids}`, + method: "PUT", + }); +} + +/** + * 获取商品统计数量(出售中、已下架等) + */ +export function getStoreGoodsNumber(params) { + return storeHttp.request({ + url: "/goods/goods/goodsNumber", + method: "GET", + params, + }); +} diff --git a/pages-sub/merchant/api/order.js b/pages-sub/merchant/api/order.js new file mode 100644 index 0000000..9bd9a97 --- /dev/null +++ b/pages-sub/merchant/api/order.js @@ -0,0 +1,104 @@ +import { storeHttp } from "../utils/storeRequest.js"; + +/** + * 商家订单列表 + * @param {Object} params { orderStatus, orderType, keywords, pageNumber, pageSize } + */ +export function getStoreOrderList(params) { + return storeHttp.request({ + url: "/order/order", + method: "GET", + params, + }); +} + +/** + * 商家订单详情 + * @param {String} sn + */ +export function getStoreOrderDetail(sn) { + return storeHttp.request({ + url: `/order/order/${sn}`, + method: "GET", + }); +} + +/** + * 自提 / 虚拟商品单码核销 + * @param {String} sn + * @param {String} verificationCode + */ +export function storeOrderTake(sn, verificationCode) { + return storeHttp.request({ + url: `/order/order/take/${sn}/${verificationCode}`, + method: "PUT", + }); +} + +/** + * 通用核销预检(按核销码查询) + * @param {String} code + */ +export function previewStoreVerification(code) { + return storeHttp.request({ + url: "/order/order/verification/preview", + method: "GET", + params: { code }, + }); +} + +/** + * 通用确认核销 + * @param {String} code + */ +export function confirmStoreVerification(code) { + return storeHttp.request({ + url: "/order/order/verification/confirm", + method: "POST", + data: { code }, + }); +} + +/** + * 预约服务 — 根据核销码查询预约订单详情 + * @param {String} code + */ +export function getAppointmentOrderByCode(code) { + return storeHttp.request({ + url: `/appointment/verification/order/${code}`, + method: "GET", + }); +} + +/** + * 预约服务 — 执行核销 + * @param {Object} params { orderSn, code, serviceStaffId } + */ +export function verifyAppointmentCode(params) { + return storeHttp.request({ + url: "/appointment/verification/verify", + method: "POST", + data: params, + }); +} + +/** + * 预约服务 — 获取已启用的服务人员列表(供核销时指派/确认) + */ +export function getEnabledServiceStaff() { + return storeHttp.request({ + url: "/appointment/service-staff/enabled", + method: "GET", + }); +} + +/** + * 预约服务 — 根据订单号获取预约单核销状态 + * @param {String} orderSn + */ +export function getAppointmentVerificationByOrderSn(orderSn) { + return storeHttp.request({ + url: `/appointment/verification/order-by-sn/${orderSn}`, + method: "GET", + }); +} diff --git a/pages-sub/merchant/api/passport.js b/pages-sub/merchant/api/passport.js new file mode 100644 index 0000000..66498ed --- /dev/null +++ b/pages-sub/merchant/api/passport.js @@ -0,0 +1,94 @@ +import { storeHttp } from "../utils/storeRequest.js"; + +/** + * 账号密码登录(支持店主与店员账号) + * @param {Object} params { username, password } + */ +export function userLogin(params) { + return storeHttp.request({ + url: "/passport/login/userLogin", + method: "POST", + params, + header: { + "content-type": "application/x-www-form-urlencoded", + }, + }); +} + +/** + * 短信验证码登录(支持店主与店员手机号) + * @param {Object} params { mobile, code } + */ +export function smsLogin(params) { + return storeHttp.request({ + url: "/passport/login/smsLogin", + method: "POST", + params, + header: { + "content-type": "application/x-www-form-urlencoded", + }, + }); +} + +/** + * 发送登录短信 + * @param {String} mobile + * @param {String} verificationEnums (LOGIN) + */ +export function sendStoreSms(mobile, verificationEnums = "LOGIN") { + return storeHttp.request({ + url: `/common/sms/${verificationEnums}/${mobile}`, + method: "GET", + }); +} + +/** + * 获取滑块验证码 + * @param {String} verificationEnums (LOGIN) + */ +export function getStoreSlider(verificationEnums = "LOGIN") { + return storeHttp.request({ + url: `/common/slider/${verificationEnums}`, + method: "GET", + }); +} + +/** + * 登出 + */ +export function storeLogout() { + return storeHttp.request({ + url: "/passport/login/logout", + method: "POST", + }); +} + +/** + * 获取店铺基本配置与信息 + */ +export function getStoreSettings() { + return storeHttp.request({ + url: "/settings/storeSettings", + method: "GET", + }); +} + +/** + * 获取当前登录人员(店主 / 店员)个人信息 + */ +export function getStoreUserInfo() { + return storeHttp.request({ + url: "/member/user/info", + method: "GET", + }); +} + +/** + * 获取当前登录所属店铺详情 + */ +export function getStoreDetail() { + return storeHttp.request({ + url: "/member/user", + method: "GET", + }); +} diff --git a/pages-sub/merchant/pages/evaluation/list.vue b/pages-sub/merchant/pages/evaluation/list.vue new file mode 100644 index 0000000..4fd6254 --- /dev/null +++ b/pages-sub/merchant/pages/evaluation/list.vue @@ -0,0 +1,650 @@ + + + + + + + {{ item.label }} + + + + + + + + + + + + + + {{ maskName(item.memberName) }} + {{ item.createTime || '--' }} + + + + + + + + + + + {{ item.content || '用户未填写文字评价' }} + + + + + + + + + + + {{ item.goodsName }} + + + + + + 掌柜回复: + {{ item.replyTime }} + + {{ item.reply }} + + + + + + + + {{ item.reply ? '修改回复' : '立即回复' }} + + + + + + + + + 暂无相关评价数据 + + + + + 正在加载评价数据... + + + 没有更多了 + + + + + + + + 回复买家评价 + + + + + + + + + {{ replyText.length }}/300 + + + + + 取消 + + {{ submittingReply ? '提交中...' : '提交回复' }} + + + + + + + + + + diff --git a/pages-sub/merchant/pages/goods/detail.vue b/pages-sub/merchant/pages/goods/detail.vue new file mode 100644 index 0000000..d9ccbd4 --- /dev/null +++ b/pages-sub/merchant/pages/goods/detail.vue @@ -0,0 +1,737 @@ + + + + + + + + + + + {{ currentSwiperIndex + 1 }} / {{ galleryImages.length }} + + + + + + + + ¥ + {{ goodsDetail.price || '0.00' }} + + + 成本价 ¥{{ goodsDetail.cost }} + + + + {{ goodsDetail.marketEnable === 'UPPER' ? '出售中' : '已下架' }} + + + {{ getAuthText(goodsDetail.authFlag) }} + + + + + + {{ goodsDetail.goodsName }} + + + + + {{ goodsDetail.quantity ?? 0 }} + 总库存 + + + + {{ goodsDetail.buyCount ?? 0 }} + 累计销量 + + + + {{ goodsDetail.sn || '--' }} + 商品货号 + + + + + + + + + 规格与 SKU 明细 + 共 {{ skuList.length }} 种规格 + + + + + + + {{ formatSpecs(sku, index) }} + SKU编码:{{ sku.sn }} + + 售价:¥{{ sku.price || '0.00' }} + 库存:{{ sku.quantity ?? sku.stock ?? sku.enableQuantity ?? 0 }} + + + + + + + + + + 基本属性 + + + 所属分类 + {{ formatCategoryName(goodsDetail.categoryName) }} + + + 商品品牌 + {{ goodsDetail.brandName }} + + + 计量单位 + {{ goodsDetail.goodsUnit }} + + + 发布时间 + {{ goodsDetail.releaseTime }} + + + + + + + 商品图文详情 + + + + + + + + + + + 买家预览 + + + 下架商品 + + + 立即上架 + + + + + + + + diff --git a/pages-sub/merchant/pages/goods/list.vue b/pages-sub/merchant/pages/goods/list.vue new file mode 100644 index 0000000..8c2e6b3 --- /dev/null +++ b/pages-sub/merchant/pages/goods/list.vue @@ -0,0 +1,472 @@ + + + + + + + + + + + + + + + {{ item.label }} + + + + + + + + + + + + + {{ goods.goodsName }} + + + 库存: {{ goods.quantity ?? 0 }} + 销量: {{ goods.buyCount ?? 0 }} + + + + ¥ + {{ goods.price || goods.sellingPrice || '0.00' }} + + + {{ goods.marketEnable === 'UPPER' ? '出售中' : '已下架' }} + + + + + + + + 商品货号:{{ goods.sn }} + + + 下架商品 + + + 立即上架 + + + + + + + + + 暂无相关商品 + + + + + 正在加载商品列表... + + + 没有更多了 + + + + + + + + diff --git a/pages-sub/merchant/pages/index/index.vue b/pages-sub/merchant/pages/index/index.vue new file mode 100644 index 0000000..f5eaf7e --- /dev/null +++ b/pages-sub/merchant/pages/index/index.vue @@ -0,0 +1,812 @@ + + + + + + + + + + + {{ storeInfo.settings?.storeName || storeInfo.store?.storeName || '商家移动工作台' }} + + {{ isClerk ? '店员' : '店主' }} + + + 当前操作员:{{ operatorName }} + + + 退出 + + + + + + + + 快速核销 + 支持预约单、自提单与虚拟卡密 + + + + + + + + + 扫码快速核销 + 扫描顾客出示的核销码/订单码 + + + + + + + + + + + + + + + 核销 + + + + + + + + 经营管理 + + + + + + + + 订单管理 + + + + + + + + 商品管理 + + + + + + + + 评价管理 + + + + + + + + 待核销单 + + + + + + + + + + + 核销信息确认 + + + + + + + + + 核销码: + {{ currentCode }} + + + 订单编号: + {{ verifyingOrder.sn || verifyingOrder.orderSn || '--' }} + + + 顾客电话: + {{ maskPhone(verifyingOrder.consigneeMobile || verifyingOrder.memberPhone) }} + + + 商品名称: + + {{ verifyingOrder.goodsName || verifyingOrder.orderItems?.[0]?.goodsName }} + + + + 购买数量: + {{ verifyingOrder.num }} 件 + + + + + 指派/核销服务人员: + + + {{ currentStaffName }} + + + + + + + + + 取消 + + {{ confirming ? '核销中...' : '确认核销' }} + + + + + + + + + + diff --git a/pages-sub/merchant/pages/login/index.vue b/pages-sub/merchant/pages/login/index.vue new file mode 100644 index 0000000..18270bc --- /dev/null +++ b/pages-sub/merchant/pages/login/index.vue @@ -0,0 +1,550 @@ + + + + + + + + + 商家管理工作台 + 随时随地 · 快捷扫码核销与订单管理 + + + + + + + + + 账号密码登录 + + + + 短信验证码登录 + + + + + + + + + + + + + + + + + + + + + + + + + + {{ smsCount > 0 ? `${smsCount}s后重试` : '获取验证码' }} + + + + + + + + + {{ submitting ? '正在登录...' : '立即进入工作台' }} + + + + + 仅限已入驻商城的店主/店员账号登录 + + + + + + + + + + + + + diff --git a/pages-sub/merchant/pages/order/detail.vue b/pages-sub/merchant/pages/order/detail.vue new file mode 100644 index 0000000..cbd5263 --- /dev/null +++ b/pages-sub/merchant/pages/order/detail.vue @@ -0,0 +1,586 @@ + + + + + + + + + {{ getStatusText(orderDetail?.order?.orderStatus) }} + + 订单编号:{{ orderSn }} + + + + + + + + + 核销凭证 + + {{ isAppointment ? '预约核销' : '自提核销' }} + + + + {{ orderDetail?.order?.verificationCode || '--' }} + + 立即核销 + + + + + + + + 买家信息 + + + 收货人/自提人: + {{ orderDetail?.order?.consigneeName || '匿名买家' }} + + + 联系电话: + {{ maskPhone(orderDetail?.order?.consigneeMobile) }} + + + 地址: + {{ orderDetail?.order?.consigneeAddressPath }} {{ orderDetail?.order?.consigneeDetail || '' }} + + + + + + + 商品清单 + + + + + {{ getGoodsTitle(item) }} + + {{ getCleanSpecs(item) }} + + + ¥{{ item.unitPrice || item.goodsPrice || '0.00' }} + x{{ item.num || 1 }} + + + + + + + + + 费用明细 + + + 商品总价 + ¥{{ orderDetail?.order?.goodsPrice || '0.00' }} + + + 优惠金额 + -¥{{ orderDetail?.order?.discountPrice }} + + + 运费 + +¥{{ orderDetail?.order?.freightPrice }} + + + + 实收金额 + ¥{{ orderDetail?.order?.flowPrice || '0.00' }} + + + + + + + 下单时间: + {{ orderDetail?.order?.createTime || '--' }} + + + 支付时间: + {{ orderDetail?.order?.paymentTime }} + + + 支付方式: + {{ formatPaymentMethod(orderDetail?.order?.paymentMethod) }} + + + + + + + + diff --git a/pages-sub/merchant/pages/order/list.vue b/pages-sub/merchant/pages/order/list.vue new file mode 100644 index 0000000..eb021c7 --- /dev/null +++ b/pages-sub/merchant/pages/order/list.vue @@ -0,0 +1,653 @@ + + + + + + + + + + + + + + + 搜索 + + + + + + + + {{ item.label }} + + + + + + + + + + + + + + 单号:{{ order.sn }} + + + + {{ getStatusText(order.orderStatus) }} + + + + + + + + + {{ getGoodsName(goods, order) }} + + {{ getSpecText(goods) }} + + + ¥{{ goods.unitPrice || goods.goodsPrice || '0.00' }} + x{{ goods.num || 1 }} + + + + + + + + + + {{ maskPhone(order.consigneeMobile || order.memberPhone) }} + + + 预约商品单 + 自提单 + + + + 实付: + ¥ + {{ order.flowPrice || '0.00' }} + + + + + + + + 扫码/输码核销 + + + + + + + + 暂无相关订单记录 + + + + + 正在加载订单数据... + + + — 没有更多了 — + + + + + + + + diff --git a/pages-sub/merchant/utils/storeRequest.js b/pages-sub/merchant/utils/storeRequest.js new file mode 100644 index 0000000..ab15cf1 --- /dev/null +++ b/pages-sub/merchant/utils/storeRequest.js @@ -0,0 +1,155 @@ +import api from "@/config/api.js"; +import Request from "@/lib/request/index.js"; +import Foundation from "@/utils/Foundation.js"; +import { md5 } from "@/utils/md5.js"; +import storage from "@/utils/storage.js"; +import uuid from "@/utils/uuid.modified.js"; + +let isStoreNavigateTo = false; + +function cleanStoreAuth() { + uni.showToast({ + title: "商家登录状态已过期,请重新登录", + icon: "none", + duration: 1800, + }); + + storage.clearStoreAuth(); + + if (!isStoreNavigateTo) { + isStoreNavigateTo = true; + setTimeout(() => { + isStoreNavigateTo = false; + uni.reLaunch({ + url: "/pages-sub/merchant/pages/login/index", + }); + }, 1200); + } +} + +const storeHttp = new Request(); + +const ensureUuid = () => { + if (!storage.getUuid()) { + storage.setUuid(uuid.v1()); + } +}; + +storeHttp.setConfig((config) => { + ensureUuid(); + config.baseURL = api.store; + config.header = { + ...config.header, + }; + config.validateStatus = () => true; + return config; +}); + +storeHttp.interceptors.request.use( + (config) => { + ensureUuid(); + const token = storage.getStoreAccessToken(); + if (token) { + const nonce = Foundation.randomString(6); + const timestamp = parseInt(new Date().getTime() / 1000); + const sign = md5(nonce + timestamp + token); + const _params = { nonce, timestamp, sign }; + config.params = { ...(config.params || {}), ..._params }; + config.header.accessToken = token; + } + config.header = { + ...config.header, + uuid: storage.getUuid(), + }; + return config; + }, + (config) => Promise.reject(config) +); + +let isStoreRefreshing = false; +let storeRequests = []; + +function refreshTokenApi(refreshToken) { + return uni.request({ + url: `${api.store}/passport/login/refresh/${refreshToken}`, + method: "GET", + header: { + uuid: storage.getUuid(), + }, + }); +} + +storeHttp.interceptors.response.use( + async (response) => { + isStoreNavigateTo = false; + try { + uni.hideLoading(); + } catch (e) {} + + const token = storage.getStoreAccessToken(); + const refreshToken = storage.getStoreRefreshToken(); + + // 403 / 401 并且有 refreshToken,尝试刷新 + if ( + token && + refreshToken && + (response.statusCode === 403 || response.statusCode === 401 || response.data?.code === 403) + ) { + if (!isStoreRefreshing) { + isStoreRefreshing = true; + storage.removeStoreAccessToken(); + try { + const [err, res] = await refreshTokenApi(refreshToken); + const data = res?.data || err?.data; + if (data && data.success && data.result) { + const { accessToken: newAccessToken, refreshToken: newRefreshToken } = data.result; + storage.setStoreAccessToken(newAccessToken); + if (newRefreshToken) { + storage.setStoreRefreshToken(newRefreshToken); + } + response.header = response.header || {}; + response.header.accessToken = `${newAccessToken}`; + storeRequests.forEach((cb) => cb(newAccessToken)); + storeRequests = []; + return storeHttp.request(response.config); + } else { + cleanStoreAuth(); + return Promise.reject(data); + } + } catch (e) { + cleanStoreAuth(); + return Promise.reject(e); + } finally { + isStoreRefreshing = false; + } + } else { + return new Promise((resolve) => { + storeRequests.push((newToken) => { + response.header = response.header || {}; + response.header.accessToken = `${newToken}`; + resolve(storeHttp.request(response.config)); + }); + }); + } + } else if ( + (!token && (response.statusCode === 403 || response.statusCode === 401)) || + response.data?.code === 403 || + response.data?.code === 401 + ) { + cleanStoreAuth(); + } else if ((response.statusCode === 200 && response.data && !response.data.success) || response.statusCode === 400) { + if (response.data?.message) { + uni.showToast({ + title: response.data.message, + icon: "none", + duration: 2000, + }); + } + } + return response; + }, + (error) => error +); + +export { storeHttp }; +export default storeHttp; diff --git a/pages.json b/pages.json index 8a8b926..7650abf 100644 --- a/pages.json +++ b/pages.json @@ -106,6 +106,70 @@ ], "subPackages": [ + { + "root": "pages-sub/merchant", + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "商家工作台", + "navigationBarBackgroundColor": "#111827", + "navigationBarTextStyle": "white" + } + }, + { + "path": "pages/login/index", + "style": { + "navigationBarTitleText": "商家登录", + "navigationBarBackgroundColor": "#111827", + "navigationBarTextStyle": "white" + } + }, + { + "path": "pages/order/list", + "style": { + "navigationBarTitleText": "订单核销管理", + "enablePullDownRefresh": true, + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/order/detail", + "style": { + "navigationBarTitleText": "订单详情", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/goods/list", + "style": { + "navigationBarTitleText": "商品管理", + "enablePullDownRefresh": true, + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/goods/detail", + "style": { + "navigationBarTitleText": "商品详情", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/evaluation/list", + "style": { + "navigationBarTitleText": "评价管理", + "enablePullDownRefresh": true, + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + } + ] + }, { "root": "pages/mine", "pages": [ @@ -447,6 +511,13 @@ "navigationBarTitleText": "设置" } }, + { + "path": "set/messageNotice", + "style": { + "navigationBarTitleText": "消息通知", + "navigationStyle": "custom" + } + }, { "path": "set/personMsg", "style": { @@ -471,7 +542,8 @@ }, { "path": "point/myPoint", "style": { - "navigationBarTitleText": "我的积分" + "navigationBarTitleText": "我的积分", + "navigationStyle": "custom" } }, { @@ -926,6 +998,7 @@ { "path": "afterSales/applySuccess", "style": { + "navigationStyle": "custom", "navigationBarTitleText": "提交成功", "app-plus": { "bounce": "none", @@ -957,13 +1030,6 @@ "navigationBarTitleText": "申请售后" } }, - { - "path": "afterSales/afterSales", - "style": { - "navigationBarTitleText": "售后管理", - "enablePullDownRefresh": true - } - }, { "path": "fillorder", "style": { diff --git a/pages/cart/payment/payOrder.vue b/pages/cart/payment/payOrder.vue index 4187744..410e30c 100644 --- a/pages/cart/payment/payOrder.vue +++ b/pages/cart/payment/payOrder.vue @@ -72,10 +72,10 @@ import { onLoad, onBackPress } from '@dcloudio/uni-app' import { useStore } from '@/store' import * as API_Trade from '@/api/trade' import { payCallback } from '@/api/members' -import { unitPrice } from '@/utils/filters.js' +import { unitPrice } from '@/utils/filters.js' import { getThemeStyle } from '@/utils/theme' -const store = useStore() +const store = useStore() const themeStyle = computed(() => getThemeStyle(store.state.theme)) const { proxy } = getCurrentInstance()! @@ -126,15 +126,20 @@ function hideLoadingIfNeeded() { } function callback(paymentMethod: string) { - uni.redirectTo({ - url: - '/pages/cart/payment/success?paymentMethod=' + - paymentMethod + - '&payPrice=' + - cashierParams.value.price + - '&orderType=' + - orderType.value, - }) + const goSuccess = () => { + uni.redirectTo({ + url: + '/pages/cart/payment/success?paymentMethod=' + + paymentMethod + + '&payPrice=' + + cashierParams.value.price + + '&orderType=' + + orderType.value + + '&orderPromotionType=' + + (cashierParams.value.orderPromotionType || ''), + }) + } + goSuccess() } function cashierData() { diff --git a/pages/cart/payment/success.vue b/pages/cart/payment/success.vue index 9d070d1..a3853b2 100644 --- a/pages/cart/payment/success.vue +++ b/pages/cart/payment/success.vue @@ -1,154 +1,188 @@ - - - - - ¥{{unitPrice(Number(payPrice)) }} - - - 查看{{ orderType == "RECHARGE" ? '余额' : '订单' }} - 回到首页 - - - - - 订单支付成功! - - - 支付方式: - - {{ paymentTypeFilter(paymentMethod) }} - - - - - - - - +const themeStyle = computed(() => getThemeStyle(store.state.theme)) + +const paymentMethod = ref('') +const from = ref('') +const payPrice = ref(0) +const orderType = ref('') +const orderPromotionType = ref('') + +onLoad((options) => { + paymentMethod.value = options.paymentMethod || '' + from.value = options.from || '' + payPrice.value = options.payPrice || 0 + orderType.value = options.orderType || '' + orderPromotionType.value = options.orderPromotionType || '' + // #ifdef MP-WEIXIN + prefetchMpMessageTemplates() + // #endif +}) + +function paymentTypeFilter(val: string) { + switch (val) { + case 'WECHAT': + return '微信' + case 'ALIPAY': + return '支付宝' + case 'WALLET': + return '余额支付' + default: + return '' + } +} + +function checkOrder() { + if (orderType.value == 'RECHARGE') { + uni.reLaunch({ + url: '/pages/mine/deposit/operation', + }) + return + } + const doGoOrder = () => { + uni.reLaunch({ + url: '/pages/order/myOrder?status=0', + }) + } + // #ifdef MP-WEIXIN + subscribePayMessageThen(doGoOrder, { + orderPromotionType: orderPromotionType.value, + }) + // #endif + // #ifndef MP-WEIXIN + doGoOrder() + // #endif +} + +function navigateTo(url: string, type?: string) { + const doNavigate = () => { + if (type === 'switch') { + uni.switchTab({ url }) + } else { + uni.redirectTo({ url }) + } + } + if (orderType.value == 'RECHARGE') { + doNavigate() + return + } + // #ifdef MP-WEIXIN + subscribePayMessageThen(doNavigate, { + orderPromotionType: orderPromotionType.value, + }) + // #endif + // #ifndef MP-WEIXIN + doNavigate() + // #endif +} + + diff --git a/pages/mine/deposit/index.vue b/pages/mine/deposit/index.vue index f2902f0..544c254 100644 --- a/pages/mine/deposit/index.vue +++ b/pages/mine/deposit/index.vue @@ -1,239 +1,484 @@ - - - - - - - 预存款金额 - ¥{{ unitPrice(walletNum) }} - - - - - - - - - - - {{ logItem.detail }} - - - {{ formatLogMoney(logItem) }} - {{ logItem.createTime }} - - - - - - - - - - - - - - - +const themeStyle = computed(() => getThemeStyle(store.state.theme)) + +const walletNum = ref(0) +const loaded = ref(false) +const hasMore = ref(true) +const activeFilter = ref(0) +const filterTabs = [ + { key: 'ALL', name: '全部明细' }, + { key: 'IN', name: '收入 / 退款' }, + { key: 'OUT', name: '支出 / 提现' }, +] + +const params = ref({ + pageNumber: 1, + pageSize: 10, + order: 'desc', +}) +const depositData = ref([]) + +onMounted(async () => { + getWallet() + const result = await getUserWallet() + walletNum.value = result.data?.result?.memberWallet ?? 0 +}) + +onReachBottom(() => { + if (!hasMore.value) return + loadMore() +}) + +function isOutgoing(logItem: any) { + const money = Number(logItem?.money) || 0 + if (money < 0) return true + const serviceType = logItem?.serviceType + return serviceType === 'WALLET_PAY' || serviceType === 'WALLET_WITHDRAWAL' +} + +function isIncoming(logItem: any) { + const money = Number(logItem?.money) || 0 + if (money > 0) return true + const serviceType = logItem?.serviceType + return ( + serviceType === 'WALLET_REFUND' || + serviceType === 'WALLET_RECHARGE' || + serviceType === 'WALLET_COMMISSION' + ) +} + +function parseLogDetail(logItem: any) { + const raw = String(logItem?.detail || '').trim() + const serviceType = logItem?.serviceType || '' + const out = isOutgoing(logItem) + const inc = isIncoming(logItem) + + const absAmount = unitPrice(Math.abs(Number(logItem?.money) || 0)) + const formattedMoney = out ? `-${absAmount}` : inc ? `+${absAmount}` : absAmount + const moneyClass = out ? 'out' : inc ? 'in' : '' + + let title = '余额变动' + let tagText = '变动' + let tagClass = 'tag-default' + + const orderMatch = raw.match(/订单\[([^\]]+)\]/) + const afterSaleMatch = raw.match(/售后单\[([^\]]+)\]/) + const rechargeMatch = raw.match(/充值单号为[::]\s*([A-Za-z0-9_-]+)/) + + if (afterSaleMatch && afterSaleMatch[1] === 'ORDER_CANCEL') { + title = '订单取消退回' + tagText = '退款' + tagClass = 'tag-in' + } else if (serviceType === 'WALLET_REFUND' || raw.includes('退还') || raw.includes('返还')) { + title = '售后退款入账' + tagText = '退款' + tagClass = 'tag-in' + } else if (serviceType === 'WALLET_PAY' || raw.includes('支付金额')) { + title = '订单余额支付' + tagText = '消费' + tagClass = 'tag-out' + } else if (serviceType === 'WALLET_RECHARGE') { + title = raw.includes('后台') ? '平台余额充值' : '预存款充值' + tagText = '充值' + tagClass = 'tag-in' + } else if (serviceType === 'WALLET_WITHDRAWAL') { + title = out ? '余额提现申请' : '提现退回解冻' + tagText = '提现' + tagClass = out ? 'tag-out' : 'tag-in' + } else if (serviceType === 'WALLET_COMMISSION') { + title = '分销佣金入账' + tagText = '佣金' + tagClass = 'tag-in' + } + + const metaList: Array<{ label: string; value: string }> = [] + if (orderMatch && orderMatch[1]) { + metaList.push({ label: '订单编号', value: orderMatch[1] }) + } + if (afterSaleMatch && afterSaleMatch[1] && afterSaleMatch[1] !== 'ORDER_CANCEL') { + metaList.push({ label: '售后单号', value: afterSaleMatch[1] }) + } + if (rechargeMatch && rechargeMatch[1]) { + metaList.push({ label: '充值单号', value: rechargeMatch[1] }) + } + + if (metaList.length === 0 && raw && !raw.includes('???')) { + const cleaned = raw + .replace(/[,,]?\s*(支付金额|退还金额|余额返还)\[[^\]]+\]/g, '') + .replace(/:\s*\d+(\.\d+)?$/, '') + .trim() + if (cleaned && cleaned !== title) { + metaList.push({ label: '变动说明', value: cleaned }) + } + } + + return { + title, + tagText, + tagClass, + formattedMoney, + moneyClass, + isOut: out, + isIn: inc, + metaList, + } +} + +const decoratedList = computed(() => + depositData.value.map((item) => ({ + ...item, + parsed: parseLogDetail(item), + })) +) + +const filteredData = computed(() => { + if (activeFilter.value === 1) { + return decoratedList.value.filter((item) => item.parsed.isIn) + } + if (activeFilter.value === 2) { + return decoratedList.value.filter((item) => item.parsed.isOut) + } + return decoratedList.value +}) + +function getWallet() { + getWalletLog(params.value).then((res) => { + loaded.value = true + const records = (res.data?.success && res.data?.result?.records) || [] + if (records.length) { + depositData.value.push(...records) + } + if (records.length < params.value.pageSize) { + hasMore.value = false + } + }) +} + +function loadMore() { + params.value.pageNumber++ + getWallet() +} + + + diff --git a/pages/mine/deposit/operation.vue b/pages/mine/deposit/operation.vue index c8e3fbc..877b588 100644 --- a/pages/mine/deposit/operation.vue +++ b/pages/mine/deposit/operation.vue @@ -45,6 +45,11 @@ import { getUserWallet } from '@/api/members' import { isLogin, navigateToLogin, unitPrice } from '@/utils/filters.js' import { useStore } from '@/store' import { getThemeStyle } from '@/utils/theme' +import { + prefetchMpMessageTemplates, + subscribeMpMessageByScenes, +} from '@/js_sdk/lili-pay/wx-pay.js' + const store = useStore() const themeStyle = computed(() => getThemeStyle(store.state.theme)) @@ -52,6 +57,9 @@ const walletNum = ref(0) onShow(async () => { if (isLogin('auth')) { + // #ifdef MP-WEIXIN + prefetchMpMessageTemplates() + // #endif const result = await getUserWallet() walletNum.value = result.data.result.memberWallet } else { @@ -60,7 +68,15 @@ onShow(async () => { }) function back() { - uni.switchTab({ url: '/pages/tabbar/user/my' }) + const doBack = () => { + uni.switchTab({ url: '/pages/tabbar/user/my' }) + } + // #ifdef MP-WEIXIN + subscribeMpMessageByScenes(['WALLET_CHANGE'], doBack) + // #endif + // #ifndef MP-WEIXIN + doBack() + // #endif } function navigateTo(url: string) { diff --git a/pages/mine/deposit/withdrawApply.vue b/pages/mine/deposit/withdrawApply.vue index e94493b..6fca9c5 100644 --- a/pages/mine/deposit/withdrawApply.vue +++ b/pages/mine/deposit/withdrawApply.vue @@ -1,41 +1,114 @@ - + - - - - {{ withdrawStatusText(item.applyStatus) }} - {{ item.inspectRemark || item.errorMessage }} - - - -{{unitPrice(item.applyMoney) }} - {{ item.createTime || item.inspectTime || "" }} - 确认收款 + + + + + {{ tab.name }} + + + + + + + + + + + + {{ item.parsed.statusText }} + + 余额提现 + + + ¥ + {{ unitPrice(item.applyMoney) }} + + + + + + + 提现单号: + {{ item.sn }} + + + 审核时间: + {{ item.inspectTime }} + + + 备注说明: + {{ item.inspectRemark || item.errorMessage }} + + + + + + 申请时间:{{ item.createTime || item.inspectTime || '-' }} + + 确认收款 + + - - + + + + diff --git a/pages/mine/deposit/withdrawal.vue b/pages/mine/deposit/withdrawal.vue index 0b1edcb..4f6c208 100644 --- a/pages/mine/deposit/withdrawal.vue +++ b/pages/mine/deposit/withdrawal.vue @@ -118,12 +118,23 @@ onMounted(async () => { withdrawRule.value = res.data.result.withdrawRule || '' }) +let submitting = false + function cashd() { + if (submitting) return const amount = Number(price.value) - if (!proxy.$u.test.amount(parseInt(String(amount)))) { + if (!(amount > 0) || !proxy.$u.test.amount(parseInt(String(amount)))) { uni.showToast({ title: '请输入正确金额', duration: 2000, icon: 'none' }) return } + if (minPrice.value && amount < Number(minPrice.value)) { + uni.showToast({ title: `最低提现金额为 ${minPrice.value} 元`, duration: 2000, icon: 'none' }) + return + } + if (amount > Number(walletNum.value || 0)) { + uni.showToast({ title: '提现金额不能大于可提现金额', duration: 2000, icon: 'none' }) + return + } const params: Record = { price: amount } if (type.value === 'ALI') { @@ -139,14 +150,23 @@ function cashd() { params.realName = realName.value } - withdrawalApply(params).then((res) => { - if (res.data.success) { - uni.showToast({ title: '提现成功!', duration: 2000, icon: 'none' }) - setTimeout(() => { - uni.navigateBack({ delta: 1 }) - }, 1000) - } - }) + const doSubmit = () => { + submitting = true + withdrawalApply(params) + .then((res) => { + if (res.data.success) { + uni.showToast({ title: '提现申请已提交', duration: 2000, icon: 'none' }) + setTimeout(() => { + uni.navigateBack({ delta: 1 }) + }, 1000) + } + }) + .finally(() => { + submitting = false + }) + } + + doSubmit() } function handleAll() { diff --git a/pages/mine/point/myPoint.vue b/pages/mine/point/myPoint.vue index a52c1fe..167730e 100644 --- a/pages/mine/point/myPoint.vue +++ b/pages/mine/point/myPoint.vue @@ -1,5 +1,13 @@ + @@ -41,6 +49,10 @@ import { onLoad, onReachBottom } from '@dcloudio/uni-app' import { useStore } from '@/store' import { getPointsData, getMemberPointSum } from '@/api/members.js' import { getThemeStyle } from '@/utils/theme' +import { + prefetchMpMessageTemplates, + subscribeMpMessageByScenes, +} from '@/js_sdk/lili-pay/wx-pay.js' const store = useStore() const themeStyle = computed(() => getThemeStyle(store.state.theme)) @@ -51,10 +63,29 @@ const params = ref({ pageNumber: 1, pageSize: 10 }) const pointSummary = ref>({}) onLoad(() => { + // #ifdef MP-WEIXIN + prefetchMpMessageTemplates() + // #endif fetchPointSummary() fetchPointLogList() }) +function handleBack() { + const doBack = () => { + uni.navigateBack({ + fail: () => { + uni.switchTab({ url: '/pages/tabbar/user/my' }) + }, + }) + } + // #ifdef MP-WEIXIN + subscribeMpMessageByScenes(['POINT_CHANGE'], doBack) + // #endif + // #ifndef MP-WEIXIN + doBack() + // #endif +} + onReachBottom(() => { params.value.pageNumber++ fetchPointLogList() diff --git a/pages/mine/set/messageNotice.vue b/pages/mine/set/messageNotice.vue new file mode 100644 index 0000000..be3ce6b --- /dev/null +++ b/pages/mine/set/messageNotice.vue @@ -0,0 +1,320 @@ + + + + + + + + 开启通知后,可第一时间接收订单发货、售后审核及资金变动提醒。 + + + + + + + + + + + + {{ oaSetting.accountName || '微信公众号通知' }} + + {{ oaSetting.bound ? '已开启' : '未开启' }} + + + 关注官方公众号,长期接收服务与订单通知 + + + + + + + + • + 订单发货、完成及拼团进度实时推送 + + + • + 售后申请审核结果、商家退款提醒 + + + • + 余额变动、提现审核与到账结果通知 + + + + + + + {{ oaSetting.bound ? '查看公众号文章' : '关注公众号开启通知' }} + + + + + + + + + + + + + + 小程序订阅消息 + 已支持 + + 在支付成功、售后提交、积分与提现等页面主动提示 + + + + + + + 小程序订阅消息支持:订单发货提醒、订单已完成提醒、拼团成功通知、售后申请审核通知、积分变更提醒、账户余额变动通知、提现结果通知。您在相关操作时点击授权即可生效。 + + + + + + + + + + diff --git a/pages/mine/set/setUp.vue b/pages/mine/set/setUp.vue index eedc7e5..287e32e 100644 --- a/pages/mine/set/setUp.vue +++ b/pages/mine/set/setUp.vue @@ -26,6 +26,11 @@ > + getThemeStyle(store.state.theme)) const userImage = config.defaultUserPhoto diff --git a/pages/order/afterSales/afterSales.vue b/pages/order/afterSales/afterSales.vue deleted file mode 100644 index d408765..0000000 --- a/pages/order/afterSales/afterSales.vue +++ /dev/null @@ -1,585 +0,0 @@ - - - - - - - - - - - - - - {{ order.storeName }} - 订单编号:{{ order.sn }} - - - - 售后单号:{{ order.service_sn || order.sn }} - {{ order.serviceType_text }} - - - - - - - - {{ getGoodsName(sku) }} - - - ¥{{ unitPrice(order.flowPrice) }} - - - - x{{ sku.num }} - - - - - - - - 该商品已申请售后服务 - - - - - - 该商品无法申请售后 - - - - - - - 退货处理-{{serviceStatusList(order.serviceStatus) }} - - 补发商品-{{serviceStatusList(order.serviceStatus) }} - - 退款-{{serviceStatusList(order.serviceStatus) }} - - 换货-{{serviceStatusList(order.serviceStatus) }} - - 取消订单-{{serviceStatusList(order.serviceStatus) }} - - - - - - - - - - 申请售后 - - - - - - 提交物流 - - - 取消售后 - - - 售后详情 - - - - - - - - 订单总金额:¥{{unitPrice(order.flowPrice) }} - - - - - - - - - - - - - diff --git a/pages/order/afterSales/afterSalesDetailExpress.vue b/pages/order/afterSales/afterSalesDetailExpress.vue index e313f81..053f1d1 100644 --- a/pages/order/afterSales/afterSalesDetailExpress.vue +++ b/pages/order/afterSales/afterSalesDetailExpress.vue @@ -158,7 +158,7 @@ function onSubmit() { title: '提交成功', type: 'success', back: true, - url: '/pages/order/afterSales/afterSales', + url: '/pages/order/myOrder?status=5', }) } }) diff --git a/pages/order/afterSales/applySuccess.vue b/pages/order/afterSales/applySuccess.vue index d4345a1..695ff7a 100644 --- a/pages/order/afterSales/applySuccess.vue +++ b/pages/order/afterSales/applySuccess.vue @@ -1,80 +1,275 @@ - - - - 售后申请提交成功 - - - 查看记录 + + + + + + + + + + - - 回到主页 + + 售后申请提交成功 + 商家将尽快为您审核处理,请留意消息通知 + + + + 回到主页 + + + 查看记录 + + + + + + + + + 温馨提示 + + + + + 商品寄回地址将在商家审核通过后,在「我的订单 - 售后」详情中展示。 + + + + 提交服务单后,售后专员可能与您电话沟通,请保持手机畅通。 + + + + 售后处理完成后,退款金额将原路退回至您的原支付账户中。 + - - - 温馨提示: - • 商品寄回地址将在审核通过后在申请记录中查询。 - • 提交服务单后,售后专员可能与您电话沟通,请保持手机畅通。 - • 退货处理成功后退款金额将原路返回到您的支持账户中; - diff --git a/pages/order/myOrder.vue b/pages/order/myOrder.vue index 7f926ab..e245423 100644 --- a/pages/order/myOrder.vue +++ b/pages/order/myOrder.vue @@ -38,6 +38,8 @@ {{ afterSaleTypeText(order.serviceType) }} + 取消售后 + 提交物流 售后详情 @@ -131,6 +133,8 @@ + @@ -141,7 +145,8 @@ import { useStore } from '@/store' import { getThemeStyle } from '@/utils/theme' import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue' import { getOrderList, cancelOrder, confirmReceipt } from '@/api/order.js' -import { getClearReason, getAfterSaleList } from '@/api/after-sale.js' +import { getClearReason, getAfterSaleList, cancelAfterSale } from '@/api/after-sale.js' +import storage from '@/utils/storage' import LiLiWXPay from '@/js_sdk/lili-pay/wx-pay.js' import { unitPrice, @@ -200,6 +205,8 @@ const orderSn = ref('') const reason = ref('') const cancelList = ref([]) const rogShow = ref(false) +const cancelAfterSaleShow = ref(false) +const pendingAfterSaleSn = ref('') function hideLoadingIfNeeded() { if (store.state.isShowToast) uni.hideLoading() @@ -418,6 +425,30 @@ function openOrder(order: any) { navigateToOrderDetail(order.sn) } +function onCancelAfterSale(order: any) { + pendingAfterSaleSn.value = order?.serviceSn || order?.sn || '' + cancelAfterSaleShow.value = true +} + +async function confirmCancelAfterSale() { + if (!pendingAfterSaleSn.value) return + uni.showLoading({ title: '加载中' }) + const res = await cancelAfterSale(pendingAfterSaleSn.value) + hideLoadingIfNeeded() + if (res?.data?.success) { + uni.showToast({ title: '取消成功!', duration: 2000, icon: 'none' }) + initData(tabCurrentIndex.value) + } +} + +function onAfterSaleExpress(order: any) { + const sku = (order?.orderItems && order.orderItems[0]) || {} + storage.setAfterSaleData({ ...order, ...sku, storeName: order.storeName }) + uni.navigateTo({ + url: `./afterSales/afterSalesDetailExpress?serviceSn=${order.serviceSn || order.sn}`, + }) +} + function changeTab(e: any) { // H5 的 swiper change 把下标放在 detail.current。 // 读 target.current 会得到 undefined,选中态被清掉,列表就停在「全部」。 diff --git a/pages/tabbar/user/my.vue b/pages/tabbar/user/my.vue index 88fd138..8f0c899 100644 --- a/pages/tabbar/user/my.vue +++ b/pages/tabbar/user/my.vue @@ -109,7 +109,7 @@ - + diff --git a/pages/tabbar/user/utils/tool.vue b/pages/tabbar/user/utils/tool.vue index 93db174..bb3da03 100644 --- a/pages/tabbar/user/utils/tool.vue +++ b/pages/tabbar/user/utils/tool.vue @@ -76,6 +76,11 @@ 店铺入驻 + + + 商家工作台 + + 我的消息 @@ -115,7 +120,7 @@