backup: 紫色UI换肤前完整备份(当前橙色逍遥梦主题)
保留改造前全部页面样式与功能代码,便于回滚。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@ const app = getApp();
|
||||
import request from '../../utils/request.js';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { reconnectForRole } from '../../utils/role-tab-bar.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -62,7 +63,10 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
|
||||
if (!phoneOk) return;
|
||||
|
||||
this.syncShopBannerFromGlobal();
|
||||
this.registerNotificationComponent();
|
||||
this.loadGlobalStatus();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,13 @@
|
||||
<view class="page-container sj-form-theme">
|
||||
<!-- 余额卡片 -->
|
||||
<view class="balance-card">
|
||||
<text class="balance-label">可用余额</text>
|
||||
<text class="balance-label">{{isStaffMode ? '可用额度' : '可用余额'}}</text>
|
||||
<view class="balance-amount">
|
||||
<text class="amount-value">{{sjyue}}</text>
|
||||
<text class="amount-unit">元</text>
|
||||
</view>
|
||||
<text class="balance-tip">生成链接将从此余额扣除</text>
|
||||
<text class="balance-tip" wx:if="{{isStaffMode}}">当前身份:{{staffRoleName || '客服'}} · 生成链接从此额度扣除</text>
|
||||
<text class="balance-tip" wx:else>生成链接将从此余额扣除</text>
|
||||
</view>
|
||||
|
||||
<!-- 商品类型选择 -->
|
||||
@@ -126,6 +127,7 @@
|
||||
<block wx:for="{{detailLinkList}}" wx:key="id">
|
||||
<view class="link-item">
|
||||
<text class="link-url" selectable="true">{{item.lianjie}}</text>
|
||||
<text class="link-operator" wx:if="{{item.roleName || item.displayName}}">{{item.roleName || item.displayName}}</text>
|
||||
<text class="link-status">{{item.is_used ? '已使用' : '未使用'}}</text>
|
||||
<view wx:if="{{!item.is_used}}" class="copy-link-btn" bindtap="onCopyLinkItem" data-url="{{item.lianjie}}">复制</view>
|
||||
</view>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@
|
||||
<view class="vip-price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-number">{{item.jiage}}</text>
|
||||
<text class="price-unit">/30天</text>
|
||||
<text class="price-unit">/{{item.formal_days || 30}}天</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-features">
|
||||
@@ -107,20 +107,24 @@
|
||||
<!-- 🔥 将“机甲能源”改为“价格” -->
|
||||
<text class="price-label">价格:</text>
|
||||
<text class="price-value">¥{{currentHuiyuan.jiage}}</text>
|
||||
<text class="price-days">/{{currentHuiyuan.formal_days || 30}}天</text>
|
||||
</view>
|
||||
<text class="buy-desc" wx:if="{{currentHuiyuan.isBought && currentHuiyuan.buyInfo}}">
|
||||
已激活,<text class="expire-date">{{currentHuiyuan.buyInfo.daoqi}}</text>后到期
|
||||
</text>
|
||||
<text class="buy-desc" wx:else>激活会员特权</text>
|
||||
<text class="buy-desc" wx:else>正式会员支持微信或余额抵扣</text>
|
||||
<text class="buy-desc trial-hint" wx:if="{{currentHuiyuan.can_buy_trial}}">
|
||||
体验版仅微信支付 ¥{{currentHuiyuan.trial_price}}/{{currentHuiyuan.trial_days}}天,终身1次
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="buy-action">
|
||||
<view class="tech-buy-btn {{currentHuiyuan.isBought ? 'renew' : ''}}"
|
||||
bindtap="onBuyHuiyuanClick" data-id="{{currentHuiyuan.id}}">
|
||||
bindtap="onBuyHuiyuanClick" data-id="{{currentHuiyuan.id}}" data-trial="false">
|
||||
<view class="buy-btn-bg"></view>
|
||||
<view class="buy-btn-glow"></view>
|
||||
<text class="buy-btn-text">
|
||||
{{currentHuiyuan.isBought ? '立即续费' : '立即激活'}}
|
||||
{{currentHuiyuan.isBought ? '正式续费' : '购买正式版'}}
|
||||
</text>
|
||||
<view class="buy-btn-energy">
|
||||
<view class="energy-dot"></view>
|
||||
@@ -128,6 +132,16 @@
|
||||
<view class="energy-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
wx:if="{{currentHuiyuan.can_buy_trial}}"
|
||||
class="tech-buy-btn trial-buy-btn"
|
||||
bindtap="onBuyHuiyuanClick"
|
||||
data-id="{{currentHuiyuan.id}}"
|
||||
data-trial="true"
|
||||
>
|
||||
<view class="buy-btn-bg"></view>
|
||||
<text class="buy-btn-text">购买体验版</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -855,11 +855,29 @@
|
||||
/* 购买区域 */
|
||||
.buy-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 20rpx;
|
||||
padding-top: 25rpx;
|
||||
border-top: 1px solid rgba(64, 156, 255, 0.2);
|
||||
}
|
||||
|
||||
.buy-action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.price-days {
|
||||
font-size: 22rpx;
|
||||
color: #a0c8ff;
|
||||
}
|
||||
|
||||
.trial-hint {
|
||||
margin-top: 6rpx;
|
||||
color: #ffb347;
|
||||
}
|
||||
|
||||
.buy-info {
|
||||
display: flex;
|
||||
@@ -928,6 +946,11 @@
|
||||
background: linear-gradient(135deg, #1e4b8f, #409cff);
|
||||
}
|
||||
|
||||
.tech-buy-btn.trial-buy-btn .buy-btn-bg {
|
||||
background: linear-gradient(135deg, rgba(255, 179, 71, 0.35), rgba(255, 120, 80, 0.45));
|
||||
border: 1px solid rgba(255, 179, 71, 0.6);
|
||||
}
|
||||
|
||||
.tech-buy-btn.renew .buy-btn-bg {
|
||||
background: linear-gradient(135deg, #0f2c5c, #36cfc9);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { createPage, request, isRoleStatusActive, isCenterPageActive, syncRoleStatuses, syncProfileFields, syncGroupFields, ensureRoleOnCenterPage, clearCacheAndEnterNormal, lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole, parseSceneOptions } from '../../utils/base-page.js'
|
||||
import { resolveAvatarUrl } from '../../utils/avatar.js'
|
||||
import { openPrivateChat, buildInviterPeerId } from '../../utils/im-user.js'
|
||||
import { isStaffMode, getStaffContext } from '../../utils/staff-api.js'
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js'
|
||||
|
||||
const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC'
|
||||
|
||||
@@ -179,7 +181,10 @@ Page(createPage({
|
||||
this.checkColdStartPopup('dashouduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
|
||||
if (!phoneOk) return;
|
||||
|
||||
migrateLegacyCenterRole(getApp());
|
||||
lockPrimaryRole('dashou', getApp());
|
||||
wx.setStorageSync('isJinpai', 0);
|
||||
@@ -277,6 +282,13 @@ Page(createPage({
|
||||
tag: d.shangjiaCertified ? '进入商家端' : '去认证',
|
||||
done: d.shangjiaCertified,
|
||||
});
|
||||
list.push({
|
||||
type: 'staff',
|
||||
name: '商家客服',
|
||||
icon: img.iconKefu || img.iconShangjia,
|
||||
tag: isStaffMode() ? '进入工作台' : '去入驻',
|
||||
done: isStaffMode(),
|
||||
});
|
||||
if (!d.isGuanshi) {
|
||||
list.push({ type: 'guanshi', name: '管事', icon: img.iconGuanshiAuth, tag: '去认证' });
|
||||
}
|
||||
@@ -1015,6 +1027,14 @@ Page(createPage({
|
||||
|
||||
onTapAuthItem(e) {
|
||||
const type = e.currentTarget.dataset.type;
|
||||
if (type === 'staff') {
|
||||
if (isStaffMode()) {
|
||||
enterLockedRole('shangjia', getApp());
|
||||
} else {
|
||||
wx.navigateTo({ url: '/pages/staff-join/staff-join' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type === 'shangjia') {
|
||||
this.onTapShangjiaAuth();
|
||||
return;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
// pages/merchant-dispatch/merchant-dispatch.js
|
||||
import { createPage, request } from '../../utils/base-page.js'
|
||||
import PopupService from '../../services/popupService.js' // 引入弹窗服务
|
||||
import { STAFF_API, isStaffMode, refreshStaffContext, getStaffContext, restoreStaffContextAfterAuth, isMerchantPortalUser } from '../../utils/staff-api.js'
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 商家余额 (从全局变量获取)
|
||||
// 可用余额 / 客服额度
|
||||
sjyue: '0.00',
|
||||
isStaffMode: false,
|
||||
balanceLabel: '可用余额',
|
||||
balanceTip: '派单将从此余额扣除',
|
||||
|
||||
// 商品类型列表
|
||||
shangpinList: [],
|
||||
@@ -42,7 +46,15 @@ Page(createPage({
|
||||
bgImageUrl: ''
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
async onLoad() {
|
||||
if (wx.getStorageSync('token')) {
|
||||
await restoreStaffContextAfterAuth()
|
||||
}
|
||||
if (!isMerchantPortalUser()) {
|
||||
wx.showToast({ title: '请先成为商家或绑定客服', icon: 'none' })
|
||||
setTimeout(() => wx.navigateBack(), 1500)
|
||||
return
|
||||
}
|
||||
this.loadBgImage() // 拼接背景图
|
||||
this.loadShangjiaYue() // 从全局变量获取商家余额
|
||||
this.loadShangpinTypes() // 加载商品类型
|
||||
@@ -50,15 +62,9 @@ Page(createPage({
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面时刷新余额(可能从其他页面充值后返回)
|
||||
this.loadShangjiaYue()
|
||||
// 重新拼接背景图(确保最新)
|
||||
this.loadBgImage()
|
||||
|
||||
// 注册通知组件(用于全局消息提示)
|
||||
this.registerNotificationComponent()
|
||||
|
||||
// 重置按钮缩进定时器(重新开始5秒倒计时)
|
||||
this.startTutorialBtnTimer()
|
||||
},
|
||||
|
||||
@@ -87,6 +93,22 @@ Page(createPage({
|
||||
|
||||
// ==================== 基础数据加载 ====================
|
||||
loadShangjiaYue() {
|
||||
const staff = isStaffMode()
|
||||
this.setData({
|
||||
isStaffMode: staff,
|
||||
balanceLabel: staff ? '可用额度' : '可用余额',
|
||||
balanceTip: staff ? '派单将从您的客服额度扣除(需老板先划额度)' : '派单将从此余额扣除',
|
||||
})
|
||||
if (staff) {
|
||||
const ctx = getStaffContext() || {}
|
||||
const wallet = ctx.wallet || {}
|
||||
this.setData({ sjyue: wallet.quota_available || '0.00' })
|
||||
refreshStaffContext(request).then((fresh) => {
|
||||
const w = (fresh && fresh.wallet) || wallet
|
||||
this.setData({ sjyue: w.quota_available || '0.00' })
|
||||
}).catch(() => {})
|
||||
return
|
||||
}
|
||||
const app = getApp()
|
||||
const shangjiaData = app.globalData.shangjia || {}
|
||||
this.setData({
|
||||
@@ -97,13 +119,16 @@ Page(createPage({
|
||||
// 加载商品类型(与极速派单完全一致)
|
||||
async loadShangpinTypes() {
|
||||
this.setData({ isLoading: true })
|
||||
const staff = isStaffMode()
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjspleixing',
|
||||
url: staff ? STAFF_API.productTypes : '/dingdan/sjspleixing',
|
||||
method: 'POST'
|
||||
})
|
||||
if (res && res.data.code === 200) {
|
||||
let list = res.data.data || []
|
||||
const code = res && res.data && res.data.code
|
||||
if (code === 200 || code === 0) {
|
||||
let list = (res.data.data || [])
|
||||
if (!Array.isArray(list) && list.list) list = list.list
|
||||
// 强制倒序排列(与极速派单保持一致)
|
||||
list.reverse()
|
||||
this.setData({
|
||||
@@ -115,14 +140,14 @@ Page(createPage({
|
||||
this.setSelectedType(list[0])
|
||||
}
|
||||
} else {
|
||||
throw new Error(res.data.msg || '加载失败')
|
||||
throw new Error((res.data && res.data.msg) || '加载失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载商品类型失败:', error)
|
||||
this.setData({ isLoading: false })
|
||||
wx.showToast({
|
||||
title: '加载类型失败',
|
||||
icon: 'error',
|
||||
title: error.message || '加载类型失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
@@ -282,21 +307,29 @@ Page(createPage({
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 验证余额是否足够
|
||||
// 4. 验证余额/额度是否足够
|
||||
const balance = parseFloat(sjyue)
|
||||
if (price > balance) {
|
||||
wx.showModal({
|
||||
title: '余额不足',
|
||||
content: `当前余额${sjyue}元,需要${jiage}元。请先充值。`,
|
||||
confirmText: '去充值',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.navigateTo({
|
||||
url: '/pages/merchant-recharge/merchant-recharge'
|
||||
})
|
||||
if (this.data.isStaffMode) {
|
||||
wx.showModal({
|
||||
title: '额度不足',
|
||||
content: `当前可用额度${sjyue}元,需要${jiage}元。请联系商家老板划额度。`,
|
||||
showCancel: false,
|
||||
})
|
||||
} else {
|
||||
wx.showModal({
|
||||
title: '余额不足',
|
||||
content: `当前余额${sjyue}元,需要${jiage}元。请先充值。`,
|
||||
confirmText: '去充值',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.navigateTo({
|
||||
url: '/pages/merchant-recharge/merchant-recharge'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -349,29 +382,31 @@ Page(createPage({
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjpaifa',
|
||||
url: isStaffMode() ? STAFF_API.orderDispatch : '/dingdan/sjpaifa',
|
||||
method: 'POST',
|
||||
data: postData
|
||||
})
|
||||
|
||||
if (res && res.data.code === 200) {
|
||||
// 派单成功
|
||||
wx.showToast({
|
||||
title: '派单成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
// 更新全局变量中的商家信息(余额、发单量、流水)
|
||||
const orderAmount = parseFloat(this.data.jiage)
|
||||
const app = getApp()
|
||||
if (app.globalData.shangjia) {
|
||||
const newBalance = parseFloat(app.globalData.shangjia.sjyue || '0') - orderAmount
|
||||
app.globalData.shangjia.sjyue = newBalance.toFixed(2)
|
||||
app.globalData.shangjia.fadanzong = (parseInt(app.globalData.shangjia.fadanzong || '0') + 1).toString()
|
||||
app.globalData.shangjia.riliushui = (parseFloat(app.globalData.shangjia.riliushui || '0') + orderAmount).toFixed(2)
|
||||
app.globalData.shangjia.yueliushui = (parseFloat(app.globalData.shangjia.yueliushui || '0') + orderAmount).toFixed(2)
|
||||
this.setData({ sjyue: app.globalData.shangjia.sjyue })
|
||||
if (this.data.isStaffMode) {
|
||||
this.loadShangjiaYue()
|
||||
} else {
|
||||
const app = getApp()
|
||||
if (app.globalData.shangjia) {
|
||||
const newBalance = parseFloat(app.globalData.shangjia.sjyue || '0') - orderAmount
|
||||
app.globalData.shangjia.sjyue = newBalance.toFixed(2)
|
||||
app.globalData.shangjia.fadanzong = (parseInt(app.globalData.shangjia.fadanzong || '0') + 1).toString()
|
||||
app.globalData.shangjia.riliushui = (parseFloat(app.globalData.shangjia.riliushui || '0') + orderAmount).toFixed(2)
|
||||
app.globalData.shangjia.yueliushui = (parseFloat(app.globalData.shangjia.yueliushui || '0') + orderAmount).toFixed(2)
|
||||
this.setData({ sjyue: app.globalData.shangjia.sjyue })
|
||||
}
|
||||
}
|
||||
|
||||
// 延迟重置表单,避免用户连续提交
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<view class="page sj-form-theme">
|
||||
<!-- 余额卡片 -->
|
||||
<view class="balance-card">
|
||||
<text class="balance-label">可用余额</text>
|
||||
<text class="balance-label">{{balanceLabel}}</text>
|
||||
<view class="balance-row">
|
||||
<text class="balance-value">{{sjyue}}</text>
|
||||
<text class="balance-unit">元</text>
|
||||
</view>
|
||||
<text class="balance-tip">派单将从此余额扣除</text>
|
||||
<text class="balance-tip">{{balanceTip}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单类型 -->
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { createPage, request, ensureRoleOnCenterPage } from '../../utils/base-page.js';
|
||||
import { getOrderStatusText } from '../../utils/api-helper.js';
|
||||
import {
|
||||
STAFF_API, isStaffMode, isMerchantPortalUser, refreshStaffContext, syncStaffUi,
|
||||
restoreStaffContextAfterAuth, applyStaffStatsToPage, getStaffContext,
|
||||
} from '../../utils/staff-api.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
@@ -12,6 +17,11 @@ Page(createPage({
|
||||
statusBar: 20,
|
||||
navBar: 44,
|
||||
isShangjia: false,
|
||||
isStaffMode: false,
|
||||
staffRoleName: '',
|
||||
quotaAvailable: '0.00',
|
||||
canDispatch: true,
|
||||
canStaffManage: true,
|
||||
lunboList: [],
|
||||
lunbozhanwei: '/images/lunbozhanwei.jpg',
|
||||
gonggao: '',
|
||||
@@ -32,21 +42,42 @@ Page(createPage({
|
||||
recentLoading: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
async onLoad() {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
statusBar: sys.statusBarHeight || 20,
|
||||
navBar: (sys.statusBarHeight || 20) + 44,
|
||||
lunbozhanwei: app.globalData.lunbozhanwei || '/images/lunbozhanwei.jpg',
|
||||
});
|
||||
if (wx.getStorageSync('token')) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
}
|
||||
this.checkRole();
|
||||
syncStaffUi(this);
|
||||
this.waitForConfigAndLoadBanner();
|
||||
this.checkColdStartPopup('shangjiaduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' });
|
||||
if (!phoneOk) return;
|
||||
|
||||
this.registerNotificationComponent();
|
||||
// 先用本地缓存渲染,再向服务端同步子客服身份
|
||||
this.checkRole();
|
||||
syncStaffUi(this);
|
||||
const token = wx.getStorageSync('token');
|
||||
const isOwner = Number(wx.getStorageSync('shangjiastatus')) === 1;
|
||||
if (token && !isOwner) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
this.checkRole();
|
||||
syncStaffUi(this);
|
||||
} else if (isStaffMode()) {
|
||||
await refreshStaffContext(request);
|
||||
this.checkRole();
|
||||
syncStaffUi(this);
|
||||
applyStaffStatsToPage(this, getStaffContext());
|
||||
}
|
||||
if (this.data.isShangjia) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
this.loadDashboard();
|
||||
@@ -78,8 +109,7 @@ Page(createPage({
|
||||
},
|
||||
|
||||
checkRole() {
|
||||
const ok = Number(wx.getStorageSync('shangjiastatus')) === 1;
|
||||
this.setData({ isShangjia: ok });
|
||||
this.setData({ isShangjia: isMerchantPortalUser() });
|
||||
},
|
||||
|
||||
loadBannerData(forceFetch) {
|
||||
@@ -132,6 +162,12 @@ Page(createPage({
|
||||
},
|
||||
|
||||
async loadShangjiaBrief() {
|
||||
if (isStaffMode()) {
|
||||
const ctx = await refreshStaffContext(request);
|
||||
syncStaffUi(this);
|
||||
applyStaffStatsToPage(this, ctx);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/shangjiaxinxi',
|
||||
@@ -156,7 +192,7 @@ Page(createPage({
|
||||
async loadPendingStats() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: { zhuangtai_list: [8], page: 1, page_size: 20 },
|
||||
});
|
||||
@@ -187,7 +223,7 @@ Page(createPage({
|
||||
this.setData({ recentLoading: true });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
zhuangtai_list: ALL_ORDER_ZHUANGTAI,
|
||||
@@ -206,7 +242,7 @@ Page(createPage({
|
||||
return {
|
||||
dingdan_id: item.dingdan_id,
|
||||
title: item.jieshao || '暂无描述',
|
||||
time: item.create_time || '',
|
||||
time: item.create_time || item.CreateTime || '',
|
||||
status: getOrderStatusText(item.zhuangtai),
|
||||
money: item.jine || '0.00',
|
||||
raw,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{!isShangjia}}">
|
||||
<block wx:if="{{!isShangjia && !isStaffMode}}">
|
||||
<view class="guest-box sj-mx20 sj-panel">
|
||||
<text class="guest-title">商家入驻</text>
|
||||
<text class="guest-desc">完成入驻后可发单、管理订单</text>
|
||||
@@ -39,7 +39,11 @@
|
||||
<text class="sj-notice-txt">{{gonggao}}</text>
|
||||
</view>
|
||||
|
||||
<view class="sj-fadan-row">
|
||||
<view class="staff-banner sj-mx20" wx:if="{{isStaffMode}}">
|
||||
<text class="staff-banner-txt">商家客服 · {{staffRoleName}} · 可用额度 ¥{{quotaAvailable}}</text>
|
||||
</view>
|
||||
|
||||
<view class="sj-fadan-row" wx:if="{{canDispatch}}">
|
||||
<view class="sj-fadan-img-btn sj-fadan-img-btn--left" bindtap="goExpress"/>
|
||||
<view class="sj-fadan-img-btn sj-fadan-img-btn--right" bindtap="goDispatch"/>
|
||||
</view>
|
||||
@@ -73,7 +77,7 @@
|
||||
<text class="stat-num">{{stats.todayRefund}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-kefu-wrap">
|
||||
<view class="stats-kefu-wrap" wx:if="{{canStaffManage}}">
|
||||
<view class="kefu-row-inset flexm">
|
||||
<view class="kefu-btn-inset" bindtap="goKefuKey">
|
||||
<image class="kefu-inset-img" src="{{imgUrls.kefuKeyBtn}}" mode="widthFix"/>
|
||||
|
||||
@@ -307,3 +307,16 @@ page {
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.staff-banner {
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
background: #fff8e8;
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx solid #f0d89a;
|
||||
}
|
||||
|
||||
.staff-banner-txt {
|
||||
font-size: 24rpx;
|
||||
color: #8a6d2b;
|
||||
}
|
||||
|
||||
@@ -1,60 +1,128 @@
|
||||
/** 客服邀请码/秘钥 - UI 预览,后端接入前使用 mock 数据 */
|
||||
/** 客服邀请码管理 — 对接 merchant-staff API */
|
||||
import request from '../../utils/request.js';
|
||||
import { STAFF_API } from '../../utils/staff-api.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBar: 20,
|
||||
gudingCode: 'XQKF2026',
|
||||
kefuTips: '客服秘钥用于绑定子客服账号,后端接口就绪后将自动替换为真实数据。',
|
||||
sublist: [{ name: '全部' }, { name: '未使用' }, { name: '已使用' }],
|
||||
kefuTips: '一次性邀请码绑定子客服,固定码仅展示不可绑定。',
|
||||
sublist: [{ name: '全部', status: null }, { name: '未使用', status: 0 }, { name: '已使用', status: 1 }],
|
||||
subCurrent: 0,
|
||||
list: [
|
||||
{ id: 1, typeName: '财务', code: 'CW-8X2K9P', status: 0, adddate: '2026-06-10' },
|
||||
{ id: 2, typeName: '总管', code: 'ZG-3M7N1Q', status: 1, adddate: '2026-06-08', usedate: '2026-06-12', user: { nickname: '客服小张', avatar: '/images/default-avatar.png' } },
|
||||
],
|
||||
list: [],
|
||||
roles: [],
|
||||
showQrcodeTips: false,
|
||||
loading: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({ statusBar: sys.statusBarHeight || 20 });
|
||||
this.loadRoles();
|
||||
this.loadList();
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack();
|
||||
onShow() {
|
||||
this.loadList();
|
||||
},
|
||||
|
||||
async loadRoles() {
|
||||
try {
|
||||
const res = await request({ url: STAFF_API.roleList, method: 'POST' });
|
||||
if (res.data && res.data.code === 0) {
|
||||
this.setData({ roles: res.data.data.roles || [] });
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
async loadList() {
|
||||
const tab = this.data.sublist[this.data.subCurrent];
|
||||
try {
|
||||
const res = await request({
|
||||
url: STAFF_API.inviteList,
|
||||
method: 'POST',
|
||||
data: tab.status === null ? {} : { status: tab.status },
|
||||
});
|
||||
if (res.data && res.data.code === 0) {
|
||||
const d = res.data.data || {};
|
||||
this.setData({
|
||||
list: d.list || [],
|
||||
gudingCode: d.gudingCode || 'XQKF2026',
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
goBack() { wx.navigateBack(); },
|
||||
|
||||
copyFixed() {
|
||||
wx.setClipboardData({ data: this.data.gudingCode });
|
||||
},
|
||||
|
||||
switchSub(e) {
|
||||
this.setData({ subCurrent: Number(e.currentTarget.dataset.idx) });
|
||||
this.setData({ subCurrent: Number(e.currentTarget.dataset.idx) }, () => this.loadList());
|
||||
},
|
||||
|
||||
addKey() {
|
||||
wx.showToast({ title: '后端接入后可添加', icon: 'none' });
|
||||
async addKey() {
|
||||
const roles = this.data.roles;
|
||||
if (!roles.length) {
|
||||
wx.showToast({ title: '暂无角色,请稍后重试', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const names = roles.map(r => r.role_name);
|
||||
wx.showActionSheet({
|
||||
itemList: names,
|
||||
success: async (r) => {
|
||||
const role = roles[r.tapIndex];
|
||||
wx.showLoading({ title: '生成中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: STAFF_API.inviteCreate,
|
||||
method: 'POST',
|
||||
data: { role_id: role.id },
|
||||
});
|
||||
if (res.data && res.data.code === 0) {
|
||||
wx.showToast({ title: '已生成', icon: 'success' });
|
||||
this.loadList();
|
||||
} else {
|
||||
wx.showToast({ title: res.data.msg || '失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
copyCode(e) {
|
||||
wx.setClipboardData({ data: e.currentTarget.dataset.code || '' });
|
||||
},
|
||||
|
||||
delKey() {
|
||||
wx.showToast({ title: '预览数据不可删除', icon: 'none' });
|
||||
},
|
||||
|
||||
showTips() {
|
||||
this.setData({ showQrcodeTips: true });
|
||||
},
|
||||
|
||||
closeTips() {
|
||||
this.setData({ showQrcodeTips: false });
|
||||
},
|
||||
|
||||
previewQr() {
|
||||
wx.previewImage({
|
||||
urls: ['https://xy-1314784552.cos.ap-guangzhou.myqcloud.com/uploads/images/ewm.png'],
|
||||
async delKey(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
if (!id) return;
|
||||
const res = await request({
|
||||
url: STAFF_API.inviteRevoke,
|
||||
method: 'POST',
|
||||
data: { id },
|
||||
});
|
||||
if (res.data && res.data.code === 0) {
|
||||
wx.showToast({ title: '已作废' });
|
||||
this.loadList();
|
||||
} else {
|
||||
wx.showToast({ title: res.data.msg || '失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
showTips() { this.setData({ showQrcodeTips: true }); },
|
||||
closeTips() { this.setData({ showQrcodeTips: false }); },
|
||||
goAudit() {
|
||||
wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit' });
|
||||
},
|
||||
goToRoleManage() {
|
||||
wx.navigateTo({ url: '/pages/merchant-staff-role/merchant-staff-role' });
|
||||
},
|
||||
noop() {},
|
||||
});
|
||||
|
||||
@@ -26,6 +26,16 @@
|
||||
<view class="add-btn" bindtap="addKey">添加</view>
|
||||
</view>
|
||||
|
||||
<view class="help-row flexb" bindtap="goToRoleManage">
|
||||
<text class="help-link">角色与权限管理(资金池/划额度在此配置角色)</text>
|
||||
<text class="help-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="help-row flexb" bindtap="goAudit">
|
||||
<text class="help-link">操作日志与客服排行</text>
|
||||
<text class="help-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="help-row flexb" bindtap="showTips">
|
||||
<text class="help-link">秘钥使用方法</text>
|
||||
<text class="help-arrow">›</text>
|
||||
@@ -50,7 +60,6 @@
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!list.length}}" class="empty-tip">暂无卡密</view>
|
||||
<view class="load-tip">预览 · 接口就绪后对接真实数据</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,47 +1,398 @@
|
||||
/** 客服列表 - UI 预览,mock 数据 */
|
||||
/** 客服列表 — 派单量 / 剩余额度 / 封禁解封 */
|
||||
|
||||
import request from '../../utils/request.js';
|
||||
|
||||
import { STAFF_API, isStaffMode } from '../../utils/staff-api.js';
|
||||
|
||||
|
||||
|
||||
const app = getApp();
|
||||
|
||||
|
||||
|
||||
function avatarUrl(path) {
|
||||
|
||||
if (!path) return '/images/default-avatar.png';
|
||||
|
||||
if (path.startsWith('http')) return path;
|
||||
|
||||
const oss = app.globalData.ossImageUrl || '';
|
||||
|
||||
const p = path.startsWith('/') ? path.slice(1) : path;
|
||||
|
||||
return oss ? oss + p : '/images/default-avatar.png';
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Page({
|
||||
|
||||
data: {
|
||||
|
||||
statusBar: 20,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
nickname: '客服总管-阿星',
|
||||
avatar: '/images/default-avatar.png',
|
||||
uid: '1008601',
|
||||
isChief: true,
|
||||
isFinance: false,
|
||||
orderCount: 128,
|
||||
settleCount: 96,
|
||||
settleAmount: '12800.00',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
nickname: '财务-小李',
|
||||
avatar: '/images/default-avatar.png',
|
||||
uid: '1008602',
|
||||
isChief: false,
|
||||
isFinance: true,
|
||||
orderCount: 56,
|
||||
settleCount: 52,
|
||||
settleAmount: '6800.00',
|
||||
},
|
||||
],
|
||||
|
||||
list: [],
|
||||
|
||||
isOwner: false,
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
onLoad() {
|
||||
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({ statusBar: sys.statusBarHeight || 20 });
|
||||
|
||||
this.setData({ statusBar: sys.statusBarHeight || 20, isOwner: !isStaffMode() });
|
||||
|
||||
this.loadList();
|
||||
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack();
|
||||
|
||||
|
||||
onShow() {
|
||||
|
||||
this.loadList();
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
formatList(raw) {
|
||||
|
||||
return (raw || []).map((item) => ({
|
||||
|
||||
...item,
|
||||
|
||||
avatar: avatarUrl(item.avatar),
|
||||
|
||||
todayDispatchCount: item.todayDispatchCount != null ? item.todayDispatchCount : 0,
|
||||
|
||||
todayDispatchAmount: item.todayDispatchAmount || '0.00',
|
||||
|
||||
dispatchCount: item.dispatchCount != null ? item.dispatchCount : (item.orderCount || 0),
|
||||
|
||||
dispatchAmount: item.dispatchAmount || '0.00',
|
||||
|
||||
settleCount: item.settleCount != null ? item.settleCount : 0,
|
||||
|
||||
settleAmount: item.settleAmount || '0.00',
|
||||
|
||||
refundCount: item.refundCount != null ? item.refundCount : 0,
|
||||
|
||||
refundAmount: item.refundAmount || '0.00',
|
||||
|
||||
quotaAvailable: item.quota_available || '0.00',
|
||||
|
||||
quotaRevokable: item.quota_available || '0.00',
|
||||
|
||||
isSelf: !!item.isSelf,
|
||||
|
||||
}));
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
rejectSelf(id) {
|
||||
|
||||
const row = (this.data.list || []).find((x) => String(x.id) === String(id));
|
||||
|
||||
if (row && row.isSelf) {
|
||||
|
||||
wx.showToast({ title: '不能对本人操作', icon: 'none' });
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
async loadList() {
|
||||
|
||||
try {
|
||||
|
||||
const res = await request({ url: STAFF_API.memberList, method: 'POST' });
|
||||
|
||||
if (res.data && res.data.code === 0) {
|
||||
|
||||
const raw = (res.data.data && res.data.data.list) || [];
|
||||
|
||||
this.setData({ list: this.formatList(raw) });
|
||||
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
|
||||
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
goBack() { wx.navigateBack(); },
|
||||
|
||||
|
||||
|
||||
copyUid(e) {
|
||||
|
||||
wx.setClipboardData({ data: e.currentTarget.dataset.uid || '' });
|
||||
|
||||
},
|
||||
|
||||
onAction(e) {
|
||||
wx.showToast({ title: '后端接入后生效', icon: 'none' });
|
||||
|
||||
|
||||
async onAllocate(e) {
|
||||
|
||||
const id = e.currentTarget.dataset.id;
|
||||
|
||||
if (this.rejectSelf(id)) return;
|
||||
|
||||
wx.showModal({
|
||||
|
||||
title: '划额度',
|
||||
|
||||
editable: true,
|
||||
|
||||
placeholderText: '输入金额',
|
||||
|
||||
success: async (r) => {
|
||||
|
||||
if (!r.confirm || !r.content) return;
|
||||
|
||||
const res = await request({
|
||||
|
||||
url: STAFF_API.walletAllocate,
|
||||
|
||||
method: 'POST',
|
||||
|
||||
data: { member_id: id, amount: r.content },
|
||||
|
||||
});
|
||||
|
||||
wx.showToast({
|
||||
|
||||
title: (res.data && res.data.code === 0) ? '成功' : (res.data.msg || '失败'),
|
||||
|
||||
icon: 'none',
|
||||
|
||||
});
|
||||
|
||||
this.loadList();
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
async onRevoke(e) {
|
||||
|
||||
const id = e.currentTarget.dataset.id;
|
||||
|
||||
if (this.rejectSelf(id)) return;
|
||||
|
||||
const available = e.currentTarget.dataset.available || '0';
|
||||
|
||||
wx.showModal({
|
||||
|
||||
title: '收回额度',
|
||||
|
||||
editable: true,
|
||||
|
||||
placeholderText: `最多可收回 ${available} 元`,
|
||||
|
||||
success: async (r) => {
|
||||
|
||||
if (!r.confirm || !r.content) return;
|
||||
|
||||
const res = await request({
|
||||
|
||||
url: STAFF_API.walletRevoke,
|
||||
|
||||
method: 'POST',
|
||||
|
||||
data: { member_id: id, amount: r.content },
|
||||
|
||||
});
|
||||
|
||||
wx.showToast({
|
||||
|
||||
title: (res.data && res.data.code === 0) ? '已收回' : (res.data.msg || '失败'),
|
||||
|
||||
icon: 'none',
|
||||
|
||||
});
|
||||
|
||||
if (res.data && res.data.code === 0) this.loadList();
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
async onToggleDisable(e) {
|
||||
|
||||
const id = e.currentTarget.dataset.id;
|
||||
|
||||
if (this.rejectSelf(id)) return;
|
||||
|
||||
const disabled = e.currentTarget.dataset.disabled;
|
||||
|
||||
const action = disabled ? '解封' : '封禁';
|
||||
|
||||
wx.showModal({
|
||||
|
||||
title: `${action}客服`,
|
||||
|
||||
content: disabled ? '解封后该客服可继续登录操作' : '封禁后该客服无法使用商家端功能',
|
||||
|
||||
success: async (r) => {
|
||||
|
||||
if (!r.confirm) return;
|
||||
|
||||
const res = await request({
|
||||
|
||||
url: STAFF_API.memberDisable,
|
||||
|
||||
method: 'POST',
|
||||
|
||||
data: { member_id: id, disable: !disabled },
|
||||
|
||||
});
|
||||
|
||||
wx.showToast({ title: (res.data && res.data.msg) || action, icon: 'none' });
|
||||
|
||||
this.loadList();
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
async onRemove(e) {
|
||||
|
||||
const id = e.currentTarget.dataset.id;
|
||||
|
||||
wx.showModal({
|
||||
|
||||
title: '移除客服',
|
||||
|
||||
content: '移除后该用户可认证商家或绑定其他商家',
|
||||
|
||||
success: async (r) => {
|
||||
|
||||
if (!r.confirm) return;
|
||||
|
||||
const res = await request({
|
||||
|
||||
url: STAFF_API.memberRemove,
|
||||
|
||||
method: 'POST',
|
||||
|
||||
data: { member_id: id },
|
||||
|
||||
});
|
||||
|
||||
wx.showToast({ title: res.data.msg || '完成', icon: 'none' });
|
||||
|
||||
this.loadList();
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
async onAssignRole(e) {
|
||||
|
||||
const memberId = e.currentTarget.dataset.id;
|
||||
|
||||
if (this.rejectSelf(memberId)) return;
|
||||
|
||||
const rolesRes = await request({ url: STAFF_API.roleList, method: 'POST' });
|
||||
|
||||
const roles = (rolesRes.data && rolesRes.data.data && rolesRes.data.data.roles) || [];
|
||||
|
||||
if (!roles.length) {
|
||||
|
||||
wx.showToast({ title: '请先创建角色', icon: 'none' });
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
wx.showActionSheet({
|
||||
|
||||
itemList: roles.map((r) => r.role_name),
|
||||
|
||||
success: async (r) => {
|
||||
|
||||
const role = roles[r.tapIndex];
|
||||
|
||||
const res = await request({
|
||||
|
||||
url: STAFF_API.memberUpdateRole,
|
||||
|
||||
method: 'POST',
|
||||
|
||||
data: { member_id: memberId, role_id: role.id },
|
||||
|
||||
});
|
||||
|
||||
const ok = res.data && res.data.code === 0;
|
||||
|
||||
const roleName = (res.data && res.data.data && res.data.data.role_name) || role.role_name;
|
||||
|
||||
wx.showToast({
|
||||
|
||||
title: ok ? `已设为${roleName}` : (res.data.msg || '失败'),
|
||||
|
||||
icon: 'none',
|
||||
|
||||
});
|
||||
|
||||
if (ok) this.loadList();
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
goRank() {
|
||||
|
||||
wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit?tab=rank' });
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
goAudit() {
|
||||
|
||||
wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit' });
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,47 +1,162 @@
|
||||
<view class="sub-page sj-page">
|
||||
|
||||
<view class="status-bar" style="height:{{statusBar}}px"></view>
|
||||
|
||||
<view class="sub-nav flexb">
|
||||
|
||||
<text class="back" bindtap="goBack">‹</text>
|
||||
|
||||
<text class="sub-nav-title">客服列表</text>
|
||||
|
||||
<text class="back placeholder"></text>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<scroll-view scroll-y class="sub-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view wx:for="{{list}}" wx:key="id" class="kefu-card sj-mx20">
|
||||
|
||||
<view wx:for="{{list}}" wx:key="id" class="kefu-card sj-mx20 {{item.isDisabled ? 'disabled-card' : ''}}">
|
||||
|
||||
<view class="kefu-head flex">
|
||||
|
||||
<image class="kefu-av" src="{{item.avatar}}" mode="aspectFill"/>
|
||||
|
||||
<view class="kefu-info">
|
||||
|
||||
<view class="flex">
|
||||
|
||||
<text class="kefu-name">{{item.nickname}}</text>
|
||||
|
||||
<text class="badge badge-role">{{item.role_name || '客服'}}</text>
|
||||
|
||||
<text wx:if="{{item.isChief}}" class="badge badge-chief">总管</text>
|
||||
|
||||
<text wx:if="{{item.isSelf}}" class="badge badge-self">本人</text>
|
||||
|
||||
<text wx:if="{{item.isFinance}}" class="badge badge-finance">财务</text>
|
||||
|
||||
<text wx:if="{{item.isDisabled}}" class="badge badge-off">已封禁</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="uid-row flex">
|
||||
|
||||
<text class="kefu-uid">ID: {{item.uid}}</text>
|
||||
|
||||
<text class="copy-link" bindtap="copyUid" data-uid="{{item.uid}}">复制</text>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="quota-row">
|
||||
|
||||
<text class="quota-label">剩余额度</text>
|
||||
|
||||
<text class="quota-val">¥{{item.quotaAvailable}}</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="kefu-stats flexa">
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
<text class="stat-n">{{item.orderCount}}</text>
|
||||
<text class="stat-l">接单数</text>
|
||||
|
||||
<text class="stat-n">{{item.todayDispatchCount}}</text>
|
||||
|
||||
<text class="stat-l">今日派单</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
|
||||
<text class="stat-n">{{item.dispatchCount}}</text>
|
||||
|
||||
<text class="stat-l">累计派单</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
|
||||
<text class="stat-n">{{item.settleCount}}</text>
|
||||
<text class="stat-l">结算数</text>
|
||||
|
||||
<text class="stat-l">结单数</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
<text class="stat-n">¥{{item.settleAmount}}</text>
|
||||
<text class="stat-l">结算金额</text>
|
||||
|
||||
<text class="stat-n">{{item.refundCount}}</text>
|
||||
|
||||
<text class="stat-l">退款数</text>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="kefu-actions flexa">
|
||||
<text class="act" bindtap="onAction">查看订单</text>
|
||||
<text class="act" bindtap="onAction">权限管理</text>
|
||||
<text class="act act-del" bindtap="onAction">删除</text>
|
||||
|
||||
<view class="kefu-stats amount-stats flexa">
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
|
||||
<text class="stat-n stat-amt">¥{{item.todayDispatchAmount}}</text>
|
||||
|
||||
<text class="stat-l">今日派单额</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
|
||||
<text class="stat-n stat-amt">¥{{item.dispatchAmount}}</text>
|
||||
|
||||
<text class="stat-l">派单金额</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
|
||||
<text class="stat-n stat-amt">¥{{item.settleAmount}}</text>
|
||||
|
||||
<text class="stat-l">结算金额</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="stat-box flexmc">
|
||||
|
||||
<text class="stat-n stat-amt">¥{{item.refundAmount}}</text>
|
||||
|
||||
<text class="stat-l">退款金额</text>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!item.isSelf}}" class="kefu-actions flexa">
|
||||
|
||||
<text class="act" bindtap="onAllocate" data-id="{{item.id}}">划额度</text>
|
||||
|
||||
<text class="act" bindtap="onRevoke" data-id="{{item.id}}" data-available="{{item.quotaRevokable}}">收回额度</text>
|
||||
|
||||
<text class="act" bindtap="onAssignRole" data-id="{{item.id}}">换角色</text>
|
||||
|
||||
<text class="act" bindtap="onToggleDisable" data-id="{{item.id}}" data-disabled="{{item.isDisabled}}">{{item.isDisabled ? '解封' : '封禁'}}</text>
|
||||
|
||||
<text wx:if="{{isOwner}}" class="act act-del" bindtap="onRemove" data-id="{{item.id}}">移除</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view wx:else class="self-tip sj-mx20">本人账号不可在此操作,如需调整请联系商家老板</view>
|
||||
|
||||
</view>
|
||||
<view class="load-tip">预览 · 接口就绪后对接真实数据</view>
|
||||
|
||||
<view wx:if="{{!list.length}}" class="load-tip">暂无客服成员</view>
|
||||
|
||||
<view class="load-tip link-tip" bindtap="goAudit">操作日志 ›</view>
|
||||
|
||||
<view class="load-tip link-tip" bindtap="goRank">客服排行 ›</view>
|
||||
|
||||
</scroll-view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
@@ -31,6 +31,34 @@
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.kefu-card.disabled-card { opacity: 0.72; }
|
||||
|
||||
.badge-role {
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
background: #f0f0f0;
|
||||
color: #666;
|
||||
border-radius: 6rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.badge-off {
|
||||
background: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.quota-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12rpx 0 8rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.quota-label { font-size: 24rpx; color: #888; }
|
||||
.quota-val { font-size: 30rpx; color: #C9A962; font-weight: 600; }
|
||||
|
||||
.kefu-av {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
@@ -55,6 +83,15 @@
|
||||
|
||||
.badge-chief { background: #ffb508; }
|
||||
.badge-finance { background: #1b6ef3; }
|
||||
.badge-self { background: #9e9e9e; }
|
||||
|
||||
.self-tip {
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx 0 4rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kefu-uid {
|
||||
font-size: 24rpx;
|
||||
@@ -75,12 +112,23 @@
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.amount-stats {
|
||||
margin-top: 0;
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.stat-n {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #e67e22;
|
||||
}
|
||||
|
||||
.stat-amt {
|
||||
font-size: 24rpx;
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
.stat-l {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,354 +1,373 @@
|
||||
<!--pages/merchant-order-detail/merchant-order-detail.wxml - 商家订单详情页完整结构(正方形九宫格,圆形头像)-->
|
||||
<!-- 页面根容器 -->
|
||||
<view class="sjddxq-page">
|
||||
<!-- 加载状态区域,当 isLoading 为 true 时显示 -->
|
||||
<view class="loading-container" wx:if="{{isLoading}}">
|
||||
<!-- 旋转的加载图标 -->
|
||||
<view class="loading-spinner"></view>
|
||||
<!-- 加载文字提示 -->
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容区域,数据加载完成后显示 -->
|
||||
<view class="content-container" wx:if="{{!isLoading}}">
|
||||
<!-- 商品图片和介绍卡片 -->
|
||||
<view class="card shangpin-card">
|
||||
<!-- 商品图片,固定宽高180rpx,已是正方形 -->
|
||||
<image class="shangpin-img" src="{{jibenShuju.tupian}}" mode="aspectFill"/>
|
||||
<!-- 商品介绍文本区域 -->
|
||||
<view class="shangpin-text">
|
||||
<!-- 订单介绍文字 -->
|
||||
<text class="jieshao-text">{{jibenShuju.jieshao || '无描述'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息卡片,左侧带状态颜色边框 -->
|
||||
<view class="card info-card" style="border-left: 8rpx solid {{zhuangtaiYanseMap[jibenShuju.zhuangtai]}}">
|
||||
<view class="card-title">订单信息</view>
|
||||
<!-- 游戏昵称行 -->
|
||||
<view class="info-row" wx:if="{{jibenShuju.nicheng}}"><text class="label">游戏昵称</text><text class="value">{{jibenShuju.nicheng}}</text></view>
|
||||
<!-- 备注信息行 -->
|
||||
<view class="info-row" wx:if="{{jibenShuju.beizhu}}"><text class="label">备注</text><text class="value">{{jibenShuju.beizhu}}</text></view>
|
||||
<!-- 订单ID行,可复制 -->
|
||||
<view class="info-row"><text class="label">订单ID</text><view class="value-with-copy"><text class="value">{{jibenShuju.dingdan_id}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{jibenShuju.dingdan_id}}">📋</text></view></view>
|
||||
<!-- 创建时间行 -->
|
||||
<view class="info-row"><text class="label">创建时间</text><text class="value">{{jibenShuju.create_time}}</text></view>
|
||||
<!-- 订单金额行,红色高亮 -->
|
||||
<view class="info-row"><text class="label">订单金额</text><text class="value price-red">¥{{jibenShuju.jine}}</text></view>
|
||||
<!-- 订单状态行 -->
|
||||
<view class="info-row"><text class="label">状态</text><text class="value zhuangtai-text">{{zhuangtaiWenziMap[jibenShuju.zhuangtai]}}</text></view>
|
||||
<!-- 新增:退款操作按钮(仅退款中状态) -->
|
||||
<view class="refund-actions" wx:if="{{jibenShuju.zhuangtai == 4}}">
|
||||
<view class="action-btn action-cancel" bindtap="showCancelTuikuan">撤销退款申请</view>
|
||||
<view class="action-btn action-modify" bindtap="showModifyTuikuan">修改退款理由</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 接单员信息卡片,仅当接单员ID存在时显示 -->
|
||||
<view class="card dashou-card" wx:if="{{dashouInfo.yonghuid}}">
|
||||
<view class="card-title">服务接单员</view>
|
||||
<view class="dashou-header">
|
||||
<!-- 接单员头像,圆形 -->
|
||||
<image class="dashou-avatar" src="{{dashouInfo.avatar || '/images/default-avatar.png'}}" mode="aspectFill"/>
|
||||
<view class="dashou-detail">
|
||||
<!-- 接单员昵称 -->
|
||||
<text class="dashou-nicheng">{{dashouInfo.nicheng || '未知昵称'}}</text>
|
||||
<!-- 接单员用户ID行,可复制 -->
|
||||
<view class="dashou-id-row"><text class="label">用户ID</text><text class="value">{{dashouInfo.yonghuid}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{dashouInfo.yonghuid}}">📋</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务详情卡片,展示交付图片、留言、退款理由等 -->
|
||||
<view class="card service-card" wx:if="{{xiangxiShuju.dashou_jiaofu.length > 0 || xiangxiShuju.dashou_liuyan || xiangxiShuju.tuikuan_liyou || xiangxiShuju.shangjia_liuyan}}">
|
||||
<view class="card-title">服务详情</view>
|
||||
<!-- 交付图片区域:使用 grid-img-wrapper 实现正方形九宫格 -->
|
||||
<block wx:if="{{xiangxiShuju.dashou_jiaofu.length > 0}}">
|
||||
<view class="sub-title">交付图片</view>
|
||||
<view class="image-grid">
|
||||
<!-- 循环渲染每张交付图片 -->
|
||||
<block wx:for="{{xiangxiShuju.dashou_jiaofu}}" wx:key="index">
|
||||
<!-- 正方形容器 -->
|
||||
<view class="grid-img-wrapper">
|
||||
<!-- 图片绝对定位填充容器 -->
|
||||
<image class="grid-img" src="{{item}}" mode="aspectFill" bindtap="yulanTupian" data-url="{{item}}" data-urls="{{xiangxiShuju.dashou_jiaofu}}"/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 接单员留言 -->
|
||||
<block wx:if="{{xiangxiShuju.dashou_liuyan}}">
|
||||
<view class="sub-title">接单员留言</view>
|
||||
<view class="content-box">{{xiangxiShuju.dashou_liuyan}}</view>
|
||||
</block>
|
||||
<!-- 退款理由 -->
|
||||
<block wx:if="{{xiangxiShuju.tuikuan_liyou}}">
|
||||
<view class="sub-title">退款理由</view>
|
||||
<view class="content-box">{{xiangxiShuju.tuikuan_liyou}}</view>
|
||||
</block>
|
||||
<!-- 商家留言 -->
|
||||
<block wx:if="{{xiangxiShuju.shangjia_liuyan}}">
|
||||
<view class="sub-title">商家留言</view>
|
||||
<view class="content-box">{{xiangxiShuju.shangjia_liuyan}}</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 罚款信息卡片,仅当罚单数据存在时显示 -->
|
||||
<view class="card fakuan-card" wx:if="{{fadanData}}">
|
||||
<view class="card-title">罚款信息</view>
|
||||
<!-- 罚款金额,红色显示 -->
|
||||
<view class="info-row"><text class="label">罚款金额</text><text class="value price-red">¥{{fadanData.fakuanjine || '0.00'}}</text></view>
|
||||
<!-- 罚款理由 -->
|
||||
<view class="info-row"><text class="label">罚款理由</text><text class="value">{{fadanData.chufaliyou || '-'}}</text></view>
|
||||
<!-- 是否影响抢单 -->
|
||||
<view class="info-row"><text class="label">是否影响抢单</text><text class="value">{{fadanData.yingxiang_qiangdan == 1 ? '是' : '否'}}</text></view>
|
||||
<!-- 罚单状态 -->
|
||||
<view class="info-row"><text class="label">罚单状态</text><text class="value">{{fadanData.zhuangtai == 1 ? '待缴纳' : fadanData.zhuangtai == 2 ? '已缴纳' : fadanData.zhuangtai == 3 ? '申诉中' : '已驳回'}}</text></view>
|
||||
<!-- 如果有申诉理由,显示 -->
|
||||
<block wx:if="{{fadanData.shensuliyou}}"><view class="sub-title">申诉理由</view><view class="content-box">{{fadanData.shensuliyou}}</view></block>
|
||||
<!-- 如果有同意处罚理由,显示 -->
|
||||
<block wx:if="{{fadanData.tongyi_chufaliyou}}"><view class="sub-title">同意处罚理由</view><view class="content-box">{{fadanData.tongyi_chufaliyou}}</view></block>
|
||||
<!-- 如果有驳回理由,显示 -->
|
||||
<block wx:if="{{fadanData.bohuiliyou}}"><view class="sub-title">驳回理由</view><view class="content-box">{{fadanData.bohuiliyou}}</view></block>
|
||||
<!-- 创建时间 -->
|
||||
<view class="info-row"><text class="label">创建时间</text><text class="value">{{fadanData.create_time}}</text></view>
|
||||
<!-- 更新时间 -->
|
||||
<view class="info-row"><text class="label">更新时间</text><text class="value">{{fadanData.update_time}}</text></view>
|
||||
<!-- 新增:罚单操作按钮区域(根据状态显示) -->
|
||||
<view class="fakuan-actions" wx:if="{{fadanData}}">
|
||||
<!-- 待缴纳(1) 或 申诉中(3):显示修改和撤销按钮 -->
|
||||
<block wx:if="{{fadanData.zhuangtai == 1 || fadanData.zhuangtai == 3}}">
|
||||
<view class="action-btn action-modify" bindtap="showModifyFakuan">修改罚款</view>
|
||||
<view class="action-btn action-cancel" bindtap="showCancelFakuan">撤销罚款</view>
|
||||
</block>
|
||||
<!-- 已驳回(4):显示再次申请按钮 -->
|
||||
<block wx:if="{{fadanData.zhuangtai == 4}}">
|
||||
<view class="action-btn action-resubmit" bindtap="showResubmitFakuan">再次申请罚款</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更换接单员后生成的新订单ID展示卡片 -->
|
||||
<view class="card new-order-card" wx:if="{{xinDingdanId}}">
|
||||
<view class="card-title">更换接单员成功</view>
|
||||
<view class="info-row"><text class="label">新订单ID</text><view class="value-with-copy"><text class="value">{{xinDingdanId}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{xinDingdanId}}">📋</text></view></view>
|
||||
</view>
|
||||
|
||||
<!-- 结算区域卡片,仅当订单状态为8(结算中)时显示 -->
|
||||
<view class="card jiesuan-card" wx:if="{{jibenShuju.zhuangtai == 8}}">
|
||||
<view class="card-title">订单结算</view>
|
||||
<view class="star-row">
|
||||
<text class="label">打星评分</text>
|
||||
<view class="stars">
|
||||
<!-- 循环渲染五颗星星 -->
|
||||
<block wx:for="{{xingxingData}}" wx:key="id">
|
||||
<!-- 点击星星触发评分 -->
|
||||
<text class="star {{item.active ? 'star-active' : ''}}" bindtap="dianjiXingxing" data-index="{{index+1}}">{{item.active ? '★' : '☆'}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 结算留言输入框 -->
|
||||
<textarea class="liuyan-input" placeholder="留言(选填,50字)" maxlength="50" value="{{jiesuanLiuyan}}" bindinput="shuruLiuyan"/>
|
||||
<!-- 结算按钮 -->
|
||||
<view class="jiesuan-btn" bindtap="showJiesuanConfirm">立即结算</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<!-- 第一行按钮:联系客服、联系接单员 -->
|
||||
<view class="bottom-btn-group">
|
||||
<view class="btn-outline" bindtap="goToKefu">联系客服</view>
|
||||
<view class="btn-outline" bindtap="goToChatWithDashou" wx:if="{{dashouInfo.yonghuid}}">联系接单员</view>
|
||||
</view>
|
||||
<!-- 第二行按钮:各种操作按钮,根据订单状态显示 -->
|
||||
<view class="bottom-btn-group">
|
||||
<!-- 撤销按钮:状态为1或7时显示 -->
|
||||
<view class="btn-warn" bindtap="showChexiaoBtn" wx:if="{{jibenShuju.zhuangtai == 1 || jibenShuju.zhuangtai == 7}}">撤销订单</view>
|
||||
<!-- 【暂时隐藏】更换接单员按钮:恢复时取消下面整段注释即可 -->
|
||||
<!-- <view class="btn-primary" bindtap="showGhDashouConfirm" wx:if="{{(jibenShuju.zhuangtai == 2 || jibenShuju.zhuangtai == 8) && dashouInfo.yonghuid}}">更换接单员</view> -->
|
||||
<!-- 申请退款按钮:状态为2或8时显示 -->
|
||||
<view class="btn-danger" bindtap="showTuikuanBtn" wx:if="{{jibenShuju.zhuangtai == 2 || jibenShuju.zhuangtai == 8}}">申请退款</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销订单弹窗 -->
|
||||
<view class="modal" wx:if="{{showChexiaoModal}}">
|
||||
<view class="modal-mask" bindtap="closeChexiaoModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">撤销订单<text class="modal-close" bindtap="closeChexiaoModal">×</text></view>
|
||||
<textarea class="modal-input" placeholder="输入撤销原因(最多50字)" maxlength="50" value="{{chexiaoLiyou}}" bindinput="shuruChexiaoLiyou"/>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeChexiaoModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoChexiao">{{isChexiaoLoading ? '处理中' : '确认撤销'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请退款弹窗 -->
|
||||
<view class="modal" wx:if="{{showTuikuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeTuikuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">申请退款<text class="modal-close" bindtap="closeTuikuanModal">×</text></view>
|
||||
<textarea class="modal-input" placeholder="输入退款原因(最多50字)" maxlength="50" value="{{tuikuanLiyou}}" bindinput="shuruTuikuanLiyou"/>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeTuikuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoTuikuan">{{isTuikuanLoading ? '处理中' : '确认申请'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更换接单员确认弹窗 -->
|
||||
<view class="modal" wx:if="{{showGhDashouModal}}">
|
||||
<view class="modal-mask" bindtap="closeGhDashouModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">更换接单员<text class="modal-close" bindtap="closeGhDashouModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">确认更换接单员吗?当前订单将被撤销,并生成一条相同内容的新订单(接单员清空)。</text>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeGhDashouModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoGhDashou">{{isGhDashouLoading ? '处理中' : '确认更换'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请罚款弹窗 -->
|
||||
<view class="modal" wx:if="{{showFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">申请罚款<text class="modal-close" bindtap="closeFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">罚款原因</text>
|
||||
<textarea class="modal-input" placeholder="输入罚款原因(最多50字)" maxlength="50" value="{{fakuanLiyou}}" bindinput="inputFakuanLiyou"/>
|
||||
<text class="modal-tip">罚款金额</text>
|
||||
<input class="modal-input-num" type="digit" placeholder="0.00" value="{{fakuanJine}}" bindinput="inputFakuanJine"/>
|
||||
<view class="toggle-row" bindtap="toggleQiangdan">
|
||||
<text class="label">是否影响抢单</text>
|
||||
<view class="toggle {{yingxiangQiangdan ? 'toggle-on' : ''}}">
|
||||
<view class="toggle-knob"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fenhong-tip">此次罚款应得分红:¥{{jisuanFenhong()}}(利率 {{fenhongLilv*100}}%)</view>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoFakuan">{{isFakuanLoading ? '处理中' : '确认申请'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 结算确认弹窗 -->
|
||||
<view class="modal" wx:if="{{showJiesuanConfirm}}">
|
||||
<view class="modal-mask" bindtap="closeJiesuanConfirm"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">确认结算<text class="modal-close" bindtap="closeJiesuanConfirm">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">确认结算该订单吗?结算后状态变为“已完成”。</text>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeJiesuanConfirm">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoJiesuan">{{isJiesuanLoading ? '处理中' : '确认结算'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 新增:修改罚款弹窗 ========== -->
|
||||
<view class="modal" wx:if="{{showModifyFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeModifyFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">修改罚款<text class="modal-close" bindtap="closeModifyFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">罚款理由</text>
|
||||
<textarea class="modal-input" placeholder="输入罚款理由" maxlength="100" value="{{modifyFakuanLiyou}}" bindinput="inputModifyFakuanLiyou"/>
|
||||
<text class="modal-tip">罚款金额</text>
|
||||
<input class="modal-input-num" type="digit" placeholder="0.00" value="{{modifyFakuanJine}}" bindinput="inputModifyFakuanJine"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeModifyFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitModifyFakuan">确认修改</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销罚款弹窗 -->
|
||||
<view class="modal" wx:if="{{showCancelFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeCancelFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">撤销罚款<text class="modal-close" bindtap="closeCancelFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">驳回理由</text>
|
||||
<textarea class="modal-input" placeholder="输入驳回理由" maxlength="100" value="{{cancelFakuanLiyou}}" bindinput="inputCancelFakuanLiyou"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeCancelFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitCancelFakuan">确认撤销</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 再次申请罚款弹窗 -->
|
||||
<view class="modal" wx:if="{{showResubmitFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeResubmitFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">再次申请罚款<text class="modal-close" bindtap="closeResubmitFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">罚款理由</text>
|
||||
<textarea class="modal-input" placeholder="输入罚款理由" maxlength="100" value="{{resubmitFakuanLiyou}}" bindinput="inputResubmitFakuanLiyou"/>
|
||||
<text class="modal-tip">罚款金额</text>
|
||||
<input class="modal-input-num" type="digit" placeholder="0.00" value="{{resubmitFakuanJine}}" bindinput="inputResubmitFakuanJine"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeResubmitFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitResubmitFakuan">确认申请</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销退款申请弹窗 -->
|
||||
<view class="modal" wx:if="{{showCancelTuikuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeCancelTuikuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">撤销退款申请<text class="modal-close" bindtap="closeCancelTuikuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">撤销理由</text>
|
||||
<textarea class="modal-input" placeholder="输入撤销理由" maxlength="100" value="{{cancelTuikuanLiyou}}" bindinput="inputCancelTuikuanLiyou"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeCancelTuikuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitCancelTuikuan">确认撤销</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 修改退款理由弹窗 -->
|
||||
<view class="modal" wx:if="{{showModifyTuikuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeModifyTuikuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">修改退款理由<text class="modal-close" bindtap="closeModifyTuikuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">退款理由</text>
|
||||
<textarea class="modal-input" placeholder="输入新的退款理由" maxlength="100" value="{{modifyTuikuanLiyou}}" bindinput="inputModifyTuikuanLiyou"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeModifyTuikuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitModifyTuikuan">确认修改</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 全局通知组件 -->
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
<!-- 教程按钮(紧贴右侧,右上角,5秒后缩进) -->
|
||||
<view
|
||||
class="tutorial-btn {{tutorialBtnHidden ? 'tutorial-btn-hidden' : ''}}"
|
||||
bindtap="onTutorialBtnTap"
|
||||
hover-class="tutorial-btn-hover"
|
||||
>
|
||||
<text class="tutorial-btn-text">教程</text>
|
||||
</view>
|
||||
|
||||
<!-- 使用教程弹窗组件 -->
|
||||
<popup-notice id="popupNotice" />
|
||||
<!--pages/merchant-order-detail/merchant-order-detail.wxml - 商家订单详情页完整结构(正方形九宫格,圆形头像)-->
|
||||
<!-- 页面根容器 -->
|
||||
<view class="sjddxq-page">
|
||||
<!-- 加载状态区域,当 isLoading 为 true 时显示 -->
|
||||
<view class="loading-container" wx:if="{{isLoading}}">
|
||||
<!-- 旋转的加载图标 -->
|
||||
<view class="loading-spinner"></view>
|
||||
<!-- 加载文字提示 -->
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容区域,数据加载完成后显示 -->
|
||||
<view class="content-container" wx:if="{{!isLoading}}">
|
||||
<!-- 商品图片和介绍卡片 -->
|
||||
<view class="card shangpin-card">
|
||||
<!-- 商品图片,固定宽高180rpx,已是正方形 -->
|
||||
<image class="shangpin-img" src="{{jibenShuju.tupian}}" mode="aspectFill"/>
|
||||
<!-- 商品介绍文本区域 -->
|
||||
<view class="shangpin-text">
|
||||
<!-- 订单介绍文字 -->
|
||||
<text class="jieshao-text">{{jibenShuju.jieshao || '无描述'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息卡片,左侧带状态颜色边框 -->
|
||||
<view class="card info-card" style="border-left: 8rpx solid {{zhuangtaiYanseMap[jibenShuju.zhuangtai]}}">
|
||||
<view class="card-title">订单信息</view>
|
||||
<!-- 游戏昵称行 -->
|
||||
<view class="info-row" wx:if="{{jibenShuju.nicheng}}"><text class="label">游戏昵称</text><text class="value">{{jibenShuju.nicheng}}</text></view>
|
||||
<!-- 备注信息行 -->
|
||||
<view class="info-row" wx:if="{{jibenShuju.beizhu}}"><text class="label">备注</text><text class="value">{{jibenShuju.beizhu}}</text></view>
|
||||
<!-- 订单ID行,可复制 -->
|
||||
<view class="info-row"><text class="label">订单ID</text><view class="value-with-copy"><text class="value">{{jibenShuju.dingdan_id}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{jibenShuju.dingdan_id}}">📋</text></view></view>
|
||||
<!-- 创建时间行 -->
|
||||
<view class="info-row"><text class="label">创建时间</text><text class="value">{{jibenShuju.create_time}}</text></view>
|
||||
<!-- 订单金额行,红色高亮 -->
|
||||
<view class="info-row"><text class="label">订单金额</text><text class="value price-red">¥{{jibenShuju.jine}}</text></view>
|
||||
<!-- 订单状态行 -->
|
||||
<view class="info-row"><text class="label">状态</text><text class="value zhuangtai-text">{{zhuangtaiWenziMap[jibenShuju.zhuangtai]}}</text></view>
|
||||
<!-- 派单客服(有记录才展示) -->
|
||||
<view class="card dispatch-staff-card" wx:if="{{dispatchStaff}}">
|
||||
<view class="card-title">派单客服</view>
|
||||
<view class="dashou-header">
|
||||
<image class="dashou-avatar" src="{{dispatchStaffAvatar || '/images/default-avatar.png'}}" mode="aspectFill"/>
|
||||
<view class="dashou-detail">
|
||||
<text class="dashou-nicheng">{{dispatchStaff.dispatch_staff_name || '未知'}}</text>
|
||||
<view class="dashou-id-row">
|
||||
<text class="label">用户ID</text>
|
||||
<text class="value">{{dispatchStaff.dispatch_staff_uid}}</text>
|
||||
<text class="copy-btn" bindtap="fuzhiWenben" data-text="{{dispatchStaff.dispatch_staff_uid}}">📋</text>
|
||||
</view>
|
||||
<view class="dashou-id-row" wx:if="{{dispatchStaff.dispatch_role_name}}">
|
||||
<text class="label">角色</text>
|
||||
<text class="value">{{dispatchStaff.dispatch_role_name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 新增:退款操作按钮(仅退款中状态) -->
|
||||
<view class="refund-actions" wx:if="{{jibenShuju.zhuangtai == 4}}">
|
||||
<view class="action-btn action-cancel" bindtap="showCancelTuikuan">撤销退款申请</view>
|
||||
<view class="action-btn action-modify" bindtap="showModifyTuikuan">修改退款理由</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 接单员信息卡片,仅当接单员ID存在时显示 -->
|
||||
<view class="card dashou-card" wx:if="{{dashouInfo.yonghuid}}">
|
||||
<view class="card-title">服务接单员</view>
|
||||
<view class="dashou-header">
|
||||
<!-- 接单员头像,圆形 -->
|
||||
<image class="dashou-avatar" src="{{dashouInfo.avatar || '/images/default-avatar.png'}}" mode="aspectFill"/>
|
||||
<view class="dashou-detail">
|
||||
<!-- 接单员昵称 -->
|
||||
<text class="dashou-nicheng">{{dashouInfo.nicheng || '未知昵称'}}</text>
|
||||
<!-- 接单员用户ID行,可复制 -->
|
||||
<view class="dashou-id-row"><text class="label">用户ID</text><text class="value">{{dashouInfo.yonghuid}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{dashouInfo.yonghuid}}">📋</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务详情卡片,展示交付图片、留言、退款理由等 -->
|
||||
<view class="card service-card" wx:if="{{xiangxiShuju.dashou_jiaofu.length > 0 || xiangxiShuju.dashou_liuyan || xiangxiShuju.tuikuan_liyou || xiangxiShuju.shangjia_liuyan}}">
|
||||
<view class="card-title">服务详情</view>
|
||||
<!-- 交付图片区域:使用 grid-img-wrapper 实现正方形九宫格 -->
|
||||
<block wx:if="{{xiangxiShuju.dashou_jiaofu.length > 0}}">
|
||||
<view class="sub-title">交付图片</view>
|
||||
<view class="image-grid">
|
||||
<!-- 循环渲染每张交付图片 -->
|
||||
<block wx:for="{{xiangxiShuju.dashou_jiaofu}}" wx:key="index">
|
||||
<!-- 正方形容器 -->
|
||||
<view class="grid-img-wrapper">
|
||||
<!-- 图片绝对定位填充容器 -->
|
||||
<image class="grid-img" src="{{item}}" mode="aspectFill" bindtap="yulanTupian" data-url="{{item}}" data-urls="{{xiangxiShuju.dashou_jiaofu}}"/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 接单员留言 -->
|
||||
<block wx:if="{{xiangxiShuju.dashou_liuyan}}">
|
||||
<view class="sub-title">接单员留言</view>
|
||||
<view class="content-box">{{xiangxiShuju.dashou_liuyan}}</view>
|
||||
</block>
|
||||
<!-- 退款理由 -->
|
||||
<block wx:if="{{xiangxiShuju.tuikuan_liyou}}">
|
||||
<view class="sub-title">退款理由</view>
|
||||
<view class="content-box">{{xiangxiShuju.tuikuan_liyou}}</view>
|
||||
</block>
|
||||
<!-- 商家留言 -->
|
||||
<block wx:if="{{xiangxiShuju.shangjia_liuyan}}">
|
||||
<view class="sub-title">商家留言</view>
|
||||
<view class="content-box">{{xiangxiShuju.shangjia_liuyan}}</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 罚款信息卡片,仅当罚单数据存在时显示 -->
|
||||
<view class="card fakuan-card" wx:if="{{fadanData}}">
|
||||
<view class="card-title">罚款信息</view>
|
||||
<!-- 罚款金额,红色显示 -->
|
||||
<view class="info-row"><text class="label">罚款金额</text><text class="value price-red">¥{{fadanData.fakuanjine || '0.00'}}</text></view>
|
||||
<!-- 罚款理由 -->
|
||||
<view class="info-row"><text class="label">罚款理由</text><text class="value">{{fadanData.chufaliyou || '-'}}</text></view>
|
||||
<!-- 是否影响抢单 -->
|
||||
<view class="info-row"><text class="label">是否影响抢单</text><text class="value">{{fadanData.yingxiang_qiangdan == 1 ? '是' : '否'}}</text></view>
|
||||
<!-- 罚单状态 -->
|
||||
<view class="info-row"><text class="label">罚单状态</text><text class="value">{{fadanData.zhuangtai == 1 ? '待缴纳' : fadanData.zhuangtai == 2 ? '已缴纳' : fadanData.zhuangtai == 3 ? '申诉中' : '已驳回'}}</text></view>
|
||||
<!-- 如果有申诉理由,显示 -->
|
||||
<block wx:if="{{fadanData.shensuliyou}}"><view class="sub-title">申诉理由</view><view class="content-box">{{fadanData.shensuliyou}}</view></block>
|
||||
<!-- 如果有同意处罚理由,显示 -->
|
||||
<block wx:if="{{fadanData.tongyi_chufaliyou}}"><view class="sub-title">同意处罚理由</view><view class="content-box">{{fadanData.tongyi_chufaliyou}}</view></block>
|
||||
<!-- 如果有驳回理由,显示 -->
|
||||
<block wx:if="{{fadanData.bohuiliyou}}"><view class="sub-title">驳回理由</view><view class="content-box">{{fadanData.bohuiliyou}}</view></block>
|
||||
<!-- 创建时间 -->
|
||||
<view class="info-row"><text class="label">创建时间</text><text class="value">{{fadanData.create_time}}</text></view>
|
||||
<!-- 更新时间 -->
|
||||
<view class="info-row"><text class="label">更新时间</text><text class="value">{{fadanData.update_time}}</text></view>
|
||||
<!-- 新增:罚单操作按钮区域(根据状态显示) -->
|
||||
<view class="fakuan-actions" wx:if="{{fadanData && canPenaltyManage}}">
|
||||
<!-- 待缴纳(1) 或 申诉中(3):显示修改和撤销按钮 -->
|
||||
<block wx:if="{{fadanData.zhuangtai == 1 || fadanData.zhuangtai == 3}}">
|
||||
<view class="action-btn action-modify" bindtap="showModifyFakuan">修改罚款</view>
|
||||
<view class="action-btn action-cancel" bindtap="showCancelFakuan">撤销罚款</view>
|
||||
</block>
|
||||
<!-- 已驳回(4):显示再次申请按钮 -->
|
||||
<block wx:if="{{fadanData.zhuangtai == 4}}">
|
||||
<view class="action-btn action-resubmit" bindtap="showResubmitFakuan">再次申请罚款</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更换接单员后生成的新订单ID展示卡片 -->
|
||||
<view class="card new-order-card" wx:if="{{xinDingdanId}}">
|
||||
<view class="card-title">更换接单员成功</view>
|
||||
<view class="info-row"><text class="label">新订单ID</text><view class="value-with-copy"><text class="value">{{xinDingdanId}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{xinDingdanId}}">📋</text></view></view>
|
||||
</view>
|
||||
|
||||
<!-- 结算区域卡片,仅当订单状态为8(结算中)时显示 -->
|
||||
<view class="card jiesuan-card" wx:if="{{jibenShuju.zhuangtai == 8 && canSettle}}">
|
||||
<view class="card-title">订单结算</view>
|
||||
<view class="star-row">
|
||||
<text class="label">打星评分</text>
|
||||
<view class="stars">
|
||||
<!-- 循环渲染五颗星星 -->
|
||||
<block wx:for="{{xingxingData}}" wx:key="id">
|
||||
<!-- 点击星星触发评分 -->
|
||||
<text class="star {{item.active ? 'star-active' : ''}}" bindtap="dianjiXingxing" data-index="{{index+1}}">{{item.active ? '★' : '☆'}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 结算留言输入框 -->
|
||||
<textarea class="liuyan-input" placeholder="留言(选填,50字)" maxlength="50" value="{{jiesuanLiuyan}}" bindinput="shuruLiuyan"/>
|
||||
<!-- 结算按钮 -->
|
||||
<view class="jiesuan-btn" bindtap="showJiesuanConfirm">立即结算</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<!-- 第一行按钮:联系客服、联系接单员 -->
|
||||
<view class="bottom-btn-group">
|
||||
<view class="btn-outline" bindtap="goToKefu">联系客服</view>
|
||||
<view class="btn-outline" bindtap="goToChatWithDashou" wx:if="{{dashouInfo.yonghuid}}">联系接单员</view>
|
||||
</view>
|
||||
<!-- 第二行按钮:各种操作按钮,根据订单状态显示 -->
|
||||
<view class="bottom-btn-group">
|
||||
<!-- 撤销按钮:状态为1或7时显示 -->
|
||||
<view class="btn-warn" bindtap="showChexiaoBtn" wx:if="{{canCancel && (jibenShuju.zhuangtai == 1 || jibenShuju.zhuangtai == 7)}}">撤销订单</view>
|
||||
<!-- 【暂时隐藏】更换接单员按钮:恢复时取消下面整段注释即可 -->
|
||||
<!-- <view class="btn-primary" bindtap="showGhDashouConfirm" wx:if="{{canChangePlayer && (jibenShuju.zhuangtai == 2 || jibenShuju.zhuangtai == 8) && dashouInfo.yonghuid}}">更换接单员</view> -->
|
||||
<!-- 申请退款按钮:状态为2或8时显示 -->
|
||||
<view class="btn-danger" bindtap="showTuikuanBtn" wx:if="{{canRefund && (jibenShuju.zhuangtai == 2 || jibenShuju.zhuangtai == 8)}}">申请退款</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销订单弹窗 -->
|
||||
<view class="modal" wx:if="{{showChexiaoModal}}">
|
||||
<view class="modal-mask" bindtap="closeChexiaoModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">撤销订单<text class="modal-close" bindtap="closeChexiaoModal">×</text></view>
|
||||
<textarea class="modal-input" placeholder="输入撤销原因(最多50字)" maxlength="50" value="{{chexiaoLiyou}}" bindinput="shuruChexiaoLiyou"/>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeChexiaoModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoChexiao">{{isChexiaoLoading ? '处理中' : '确认撤销'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请退款弹窗 -->
|
||||
<view class="modal" wx:if="{{showTuikuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeTuikuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">申请退款<text class="modal-close" bindtap="closeTuikuanModal">×</text></view>
|
||||
<textarea class="modal-input" placeholder="输入退款原因(最多50字)" maxlength="50" value="{{tuikuanLiyou}}" bindinput="shuruTuikuanLiyou"/>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeTuikuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoTuikuan">{{isTuikuanLoading ? '处理中' : '确认申请'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更换接单员确认弹窗 -->
|
||||
<view class="modal" wx:if="{{showGhDashouModal}}">
|
||||
<view class="modal-mask" bindtap="closeGhDashouModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">更换接单员<text class="modal-close" bindtap="closeGhDashouModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">确认更换接单员吗?当前订单将被撤销,并生成一条相同内容的新订单(接单员清空)。</text>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeGhDashouModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoGhDashou">{{isGhDashouLoading ? '处理中' : '确认更换'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请罚款弹窗 -->
|
||||
<view class="modal" wx:if="{{showFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">申请罚款<text class="modal-close" bindtap="closeFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">罚款原因</text>
|
||||
<textarea class="modal-input" placeholder="输入罚款原因(最多50字)" maxlength="50" value="{{fakuanLiyou}}" bindinput="inputFakuanLiyou"/>
|
||||
<text class="modal-tip">罚款金额</text>
|
||||
<input class="modal-input-num" type="digit" placeholder="0.00" value="{{fakuanJine}}" bindinput="inputFakuanJine"/>
|
||||
<view class="toggle-row" bindtap="toggleQiangdan">
|
||||
<text class="label">是否影响抢单</text>
|
||||
<view class="toggle {{yingxiangQiangdan ? 'toggle-on' : ''}}">
|
||||
<view class="toggle-knob"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fenhong-tip">此次罚款应得分红:¥{{jisuanFenhong()}}(利率 {{fenhongLilv*100}}%)</view>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoFakuan">{{isFakuanLoading ? '处理中' : '确认申请'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 结算确认弹窗 -->
|
||||
<view class="modal" wx:if="{{showJiesuanConfirm}}">
|
||||
<view class="modal-mask" bindtap="closeJiesuanConfirm"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">确认结算<text class="modal-close" bindtap="closeJiesuanConfirm">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">确认结算该订单吗?结算后状态变为“已完成”。</text>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeJiesuanConfirm">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="tijiaoJiesuan">{{isJiesuanLoading ? '处理中' : '确认结算'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 新增:修改罚款弹窗 ========== -->
|
||||
<view class="modal" wx:if="{{showModifyFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeModifyFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">修改罚款<text class="modal-close" bindtap="closeModifyFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">罚款理由</text>
|
||||
<textarea class="modal-input" placeholder="输入罚款理由" maxlength="100" value="{{modifyFakuanLiyou}}" bindinput="inputModifyFakuanLiyou"/>
|
||||
<text class="modal-tip">罚款金额</text>
|
||||
<input class="modal-input-num" type="digit" placeholder="0.00" value="{{modifyFakuanJine}}" bindinput="inputModifyFakuanJine"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeModifyFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitModifyFakuan">确认修改</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销罚款弹窗 -->
|
||||
<view class="modal" wx:if="{{showCancelFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeCancelFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">撤销罚款<text class="modal-close" bindtap="closeCancelFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">驳回理由</text>
|
||||
<textarea class="modal-input" placeholder="输入驳回理由" maxlength="100" value="{{cancelFakuanLiyou}}" bindinput="inputCancelFakuanLiyou"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeCancelFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitCancelFakuan">确认撤销</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 再次申请罚款弹窗 -->
|
||||
<view class="modal" wx:if="{{showResubmitFakuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeResubmitFakuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">再次申请罚款<text class="modal-close" bindtap="closeResubmitFakuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">罚款理由</text>
|
||||
<textarea class="modal-input" placeholder="输入罚款理由" maxlength="100" value="{{resubmitFakuanLiyou}}" bindinput="inputResubmitFakuanLiyou"/>
|
||||
<text class="modal-tip">罚款金额</text>
|
||||
<input class="modal-input-num" type="digit" placeholder="0.00" value="{{resubmitFakuanJine}}" bindinput="inputResubmitFakuanJine"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeResubmitFakuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitResubmitFakuan">确认申请</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销退款申请弹窗 -->
|
||||
<view class="modal" wx:if="{{showCancelTuikuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeCancelTuikuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">撤销退款申请<text class="modal-close" bindtap="closeCancelTuikuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">撤销理由</text>
|
||||
<textarea class="modal-input" placeholder="输入撤销理由" maxlength="100" value="{{cancelTuikuanLiyou}}" bindinput="inputCancelTuikuanLiyou"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeCancelTuikuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitCancelTuikuan">确认撤销</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 修改退款理由弹窗 -->
|
||||
<view class="modal" wx:if="{{showModifyTuikuanModal}}">
|
||||
<view class="modal-mask" bindtap="closeModifyTuikuanModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-hd">修改退款理由<text class="modal-close" bindtap="closeModifyTuikuanModal">×</text></view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-tip">退款理由</text>
|
||||
<textarea class="modal-input" placeholder="输入新的退款理由" maxlength="100" value="{{modifyTuikuanLiyou}}" bindinput="inputModifyTuikuanLiyou"/>
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<view class="modal-btn cancel" bindtap="closeModifyTuikuanModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="submitModifyTuikuan">确认修改</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 全局通知组件 -->
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
<!-- 教程按钮(紧贴右侧,右上角,5秒后缩进) -->
|
||||
<view
|
||||
class="tutorial-btn {{tutorialBtnHidden ? 'tutorial-btn-hidden' : ''}}"
|
||||
bindtap="onTutorialBtnTap"
|
||||
hover-class="tutorial-btn-hover"
|
||||
>
|
||||
<text class="tutorial-btn-text">教程</text>
|
||||
</view>
|
||||
|
||||
<!-- 使用教程弹窗组件 -->
|
||||
<popup-notice id="popupNotice" />
|
||||
</view>
|
||||
@@ -3,6 +3,8 @@ const app = getApp();
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import { reconnectForRole } from '../../utils/role-tab-bar.js';
|
||||
import { getOrderStatusText } from '../../utils/api-helper.js';
|
||||
import { STAFF_API, isStaffMode, refreshStaffContext, syncStaffUi } from '../../utils/staff-api.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
@@ -49,12 +51,19 @@ Page(createPage({
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
wx.setNavigationBarTitle({ title: '我的派单' });
|
||||
wx.setNavigationBarTitle({ title: isStaffMode() ? '客服派单' : '我的派单' });
|
||||
syncStaffUi(this);
|
||||
if (isStaffMode()) {
|
||||
refreshStaffContext(request).then(() => syncStaffUi(this));
|
||||
}
|
||||
this.loadShangpinLeixing();
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' });
|
||||
if (!phoneOk) return;
|
||||
|
||||
this.registerNotificationComponent();
|
||||
this.loadPendingCount();
|
||||
if (wx.getStorageSync('uid')) {
|
||||
@@ -169,7 +178,7 @@ Page(createPage({
|
||||
};
|
||||
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq', // 商家订单接口(和原来一样)
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: params,
|
||||
header: { 'content-type': 'application/json' }
|
||||
@@ -247,7 +256,7 @@ Page(createPage({
|
||||
async loadPendingCount() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
zhuangtai_list: [8],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// pages/shangjia-chongzhi/index.js
|
||||
import request from '../../utils/request';
|
||||
import { guardMerchantOwnerAction } from '../../utils/staff-api.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -37,6 +38,10 @@ Page({
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
if (!guardMerchantOwnerAction('仅商家老板可充值')) {
|
||||
setTimeout(() => wx.navigateBack(), 1200);
|
||||
return;
|
||||
}
|
||||
this.initPage();
|
||||
},
|
||||
|
||||
|
||||
99
pages/merchant-staff-audit/merchant-staff-audit.js
Normal file
99
pages/merchant-staff-audit/merchant-staff-audit.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import request from '../../utils/request.js';
|
||||
import { STAFF_API } from '../../utils/staff-api.js';
|
||||
|
||||
const ORDER_AUDIT_ACTIONS = [
|
||||
'DISPATCH', 'LINK_GENERATE', 'SETTLE', 'CANCEL', 'REFUND_APPLY',
|
||||
'CHANGE_PLAYER', 'PENALTY_APPLY', 'PENALTY_MANAGE',
|
||||
];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
tab: 'audit',
|
||||
auditList: [],
|
||||
rankList: [],
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
loading: false,
|
||||
total: 0,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
if (options.tab === 'rank') this.setData({ tab: 'rank' });
|
||||
wx.setNavigationBarTitle({
|
||||
title: options.tab === 'rank' ? '客服排行' : '操作日志',
|
||||
});
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadData(true).finally(() => wx.stopPullDownRefresh());
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.tab === 'audit' && this.data.hasMore && !this.data.loading) {
|
||||
this.loadAudit(false);
|
||||
}
|
||||
},
|
||||
|
||||
switchTab(e) {
|
||||
const tab = e.currentTarget.dataset.tab;
|
||||
wx.setNavigationBarTitle({ title: tab === 'rank' ? '客服排行' : '操作日志' });
|
||||
this.setData({ tab, page: 1, hasMore: true }, () => this.loadData(true));
|
||||
},
|
||||
|
||||
loadData(reset) {
|
||||
if (this.data.tab === 'audit') return this.loadAudit(reset);
|
||||
return this.loadRank();
|
||||
},
|
||||
|
||||
async loadAudit(reset) {
|
||||
if (this.data.loading) return;
|
||||
const page = reset ? 1 : this.data.page;
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const res = await request({
|
||||
url: STAFF_API.auditList,
|
||||
method: 'POST',
|
||||
data: { page, page_size: 20 },
|
||||
});
|
||||
if (res.data && res.data.code === 0) {
|
||||
const d = res.data.data || {};
|
||||
const list = (d.list || []).map((item) => ({
|
||||
...item,
|
||||
canJumpOrder: ORDER_AUDIT_ACTIONS.indexOf(item.action) >= 0 && !!item.resource_id,
|
||||
}));
|
||||
this.setData({
|
||||
auditList: reset ? list : [...this.data.auditList, ...list],
|
||||
page: page + 1,
|
||||
hasMore: !!d.has_more,
|
||||
total: d.total || 0,
|
||||
loading: false,
|
||||
});
|
||||
} else {
|
||||
this.setData({ loading: false });
|
||||
wx.showToast({ title: res.data?.msg || '加载失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
async loadRank() {
|
||||
const res = await request({
|
||||
url: STAFF_API.rank,
|
||||
method: 'POST',
|
||||
data: { period: 'month', sort_by: 'dispatch_count' },
|
||||
});
|
||||
if (res.data && res.data.code === 0) {
|
||||
this.setData({ rankList: (res.data.data && res.data.data.list) || [] });
|
||||
}
|
||||
},
|
||||
|
||||
goOrderDetail(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item || !item.canJumpOrder || !item.resource_id) return;
|
||||
wx.navigateTo({
|
||||
url: `/pages/merchant-order-detail/merchant-order-detail?dingdan_id=${encodeURIComponent(item.resource_id)}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
5
pages/merchant-staff-audit/merchant-staff-audit.json
Normal file
5
pages/merchant-staff-audit/merchant-staff-audit.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "操作日志",
|
||||
"enablePullDownRefresh": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
39
pages/merchant-staff-audit/merchant-staff-audit.wxml
Normal file
39
pages/merchant-staff-audit/merchant-staff-audit.wxml
Normal file
@@ -0,0 +1,39 @@
|
||||
<view class="page">
|
||||
<view class="head-tip">仅记录派单、退款、模板等重要操作,不含普通查看</view>
|
||||
<view class="tabs">
|
||||
<view class="tab {{tab === 'audit' ? 'on' : ''}}" data-tab="audit" bindtap="switchTab">操作日志</view>
|
||||
<view class="tab {{tab === 'rank' ? 'on' : ''}}" data-tab="rank" bindtap="switchTab">客服排行</view>
|
||||
</view>
|
||||
<block wx:if="{{tab === 'audit'}}">
|
||||
<view class="summary" wx:if="{{total > 0}}">共 {{total}} 条重要操作</view>
|
||||
<view class="item {{item.canJumpOrder ? 'item-link' : ''}}" wx:for="{{auditList}}" wx:key="id"
|
||||
bindtap="goOrderDetail" data-item="{{item}}">
|
||||
<view class="item-hd flexb">
|
||||
<text class="act">{{item.action_label || item.action}}</text>
|
||||
<text class="amount" wx:if="{{item.amount}}">¥{{item.amount}}</text>
|
||||
</view>
|
||||
<view class="operator">
|
||||
<text class="tag {{item.operator_type === 'STAFF' ? 'tag-staff' : 'tag-owner'}}">
|
||||
{{item.role_name || (item.operator_type === 'STAFF' ? '客服' : '老板')}}
|
||||
</text>
|
||||
<text class="name">{{item.operator_name}}</text>
|
||||
<text class="uid">ID:{{item.operator_user_id}}</text>
|
||||
</view>
|
||||
<text class="meta">{{item.time}}</text>
|
||||
<text class="res" wx:if="{{item.resource_id}}">订单:{{item.resource_id}}</text>
|
||||
<text class="jump-tip" wx:if="{{item.canJumpOrder}}">点击查看订单详情 ›</text>
|
||||
<text class="remark" wx:if="{{item.remark}}">{{item.remark}}</text>
|
||||
</view>
|
||||
<view class="load-tip" wx:if="{{loading}}">加载中...</view>
|
||||
<view class="load-tip" wx:elif="{{hasMore && auditList.length}}">上拉加载更多</view>
|
||||
<view class="empty" wx:if="{{!loading && auditList.length === 0}}">暂无操作日志</view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="item" wx:for="{{rankList}}" wx:key="member_id">
|
||||
<text class="rank">#{{item.rank}} {{item.display_name}}</text>
|
||||
<text class="meta">派单 {{item.dispatch_count}} 单 · ¥{{item.dispatch_amount}}</text>
|
||||
<text class="meta">结单 {{item.settle_count}} 单 · ¥{{item.settle_amount}}</text>
|
||||
</view>
|
||||
<view class="empty" wx:if="{{rankList.length === 0}}">暂无排行数据</view>
|
||||
</block>
|
||||
</view>
|
||||
25
pages/merchant-staff-audit/merchant-staff-audit.wxss
Normal file
25
pages/merchant-staff-audit/merchant-staff-audit.wxss
Normal file
@@ -0,0 +1,25 @@
|
||||
.page { padding: 24rpx; background: #f5f5f5; min-height: 100vh; box-sizing: border-box; }
|
||||
.head-tip { font-size: 24rpx; color: #888; margin-bottom: 16rpx; line-height: 1.5; }
|
||||
.tabs { display: flex; margin-bottom: 24rpx; }
|
||||
.tab { flex: 1; text-align: center; padding: 20rpx; background: #fff; margin-right: 8rpx; border-radius: 8rpx; font-size: 28rpx; }
|
||||
.tab.on { background: #C9A962; color: #fff; }
|
||||
.summary { font-size: 24rpx; color: #666; margin-bottom: 16rpx; }
|
||||
.item { background: #fff; padding: 24rpx; margin-bottom: 16rpx; border-radius: 12rpx; }
|
||||
.item-hd { margin-bottom: 12rpx; }
|
||||
.act { font-size: 30rpx; color: #333; font-weight: 600; }
|
||||
.amount { font-size: 28rpx; color: #C9A962; }
|
||||
.operator { display: flex; align-items: center; flex-wrap: wrap; gap: 12rpx; margin-bottom: 8rpx; }
|
||||
.tag { font-size: 22rpx; padding: 4rpx 12rpx; border-radius: 6rpx; }
|
||||
.tag-staff { background: #e8f4ff; color: #1976d2; }
|
||||
.tag-owner { background: #fff3e0; color: #e65100; }
|
||||
.name { font-size: 26rpx; color: #333; }
|
||||
.uid { font-size: 22rpx; color: #999; }
|
||||
.meta { display: block; font-size: 24rpx; color: #999; margin-top: 6rpx; }
|
||||
.res { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; word-break: break-all; }
|
||||
.remark { display: block; font-size: 24rpx; color: #888; margin-top: 6rpx; }
|
||||
.rank { font-weight: 600; font-size: 28rpx; display: block; }
|
||||
.load-tip { text-align: center; color: #999; padding: 24rpx; font-size: 24rpx; }
|
||||
.empty { text-align: center; color: #999; padding: 80rpx; }
|
||||
.item-link { border-left: 6rpx solid #C9A962; }
|
||||
.jump-tip { display: block; font-size: 24rpx; color: #C9A962; margin-top: 8rpx; }
|
||||
.flexb { display: flex; justify-content: space-between; align-items: center; }
|
||||
236
pages/merchant-staff-role/merchant-staff-role.js
Normal file
236
pages/merchant-staff-role/merchant-staff-role.js
Normal file
@@ -0,0 +1,236 @@
|
||||
/** 商家老板:角色与权限管理 */
|
||||
import request from '../../utils/request.js';
|
||||
import { STAFF_API, isStaffMode, isMerchantOwnerOnly } from '../../utils/staff-api.js';
|
||||
|
||||
/** 与后端 constants.PERMISSION_DEFINITIONS 一致,接口为空时前端兜底 */
|
||||
const FALLBACK_PERMISSIONS = [
|
||||
{ perm_code: 'staff_manage', perm_name: '子客服管理' },
|
||||
{ perm_code: 'role_manage', perm_name: '角色管理' },
|
||||
{ perm_code: 'wallet_allocate', perm_name: '划额度' },
|
||||
{ perm_code: 'wallet_revoke', perm_name: '收回额度' },
|
||||
{ perm_code: 'wallet_view', perm_name: '查看额度' },
|
||||
{ perm_code: 'finance_view', perm_name: '查看商家余额' },
|
||||
{ perm_code: 'order_dispatch', perm_name: '派单发单' },
|
||||
{ perm_code: 'order_view_all', perm_name: '查看全部订单' },
|
||||
{ perm_code: 'order_view_self', perm_name: '只看自己派的单' },
|
||||
{ perm_code: 'order_detail', perm_name: '订单详情' },
|
||||
{ perm_code: 'order_settle', perm_name: '结单' },
|
||||
{ perm_code: 'order_refund_apply', perm_name: '申请退款' },
|
||||
{ perm_code: 'order_cancel', perm_name: '撤销订单' },
|
||||
{ perm_code: 'order_change_player', perm_name: '换打手' },
|
||||
{ perm_code: 'penalty_apply', perm_name: '申请罚单' },
|
||||
{ perm_code: 'penalty_manage', perm_name: '改撤罚单' },
|
||||
{ perm_code: 'penalty_view', perm_name: '查看罚单' },
|
||||
{ perm_code: 'staff_disable', perm_name: '封禁解封客服' },
|
||||
{ perm_code: 'template_manage', perm_name: '派单模板' },
|
||||
{ perm_code: 'audit_view', perm_name: '操作日志' },
|
||||
{ perm_code: 'rank_view', perm_name: '客服排行榜' },
|
||||
{ perm_code: 'im_chat', perm_name: '订单IM' },
|
||||
{ perm_code: 'stats_view', perm_name: '统计数据' },
|
||||
];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBar: 20,
|
||||
permScrollHeight: 360,
|
||||
loading: false,
|
||||
roles: [],
|
||||
rawPermissions: [],
|
||||
permissionOptions: [],
|
||||
selectedPerms: [],
|
||||
showEditPanel: false,
|
||||
showCreatePanel: false,
|
||||
editingRoleId: null,
|
||||
editingRoleName: '',
|
||||
newRoleName: '',
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
const h = sys.windowHeight || 667;
|
||||
this.setData({
|
||||
statusBar: sys.statusBarHeight || 20,
|
||||
permScrollHeight: Math.floor(h * 0.45),
|
||||
});
|
||||
if (!this.ensureOwnerAccess()) return;
|
||||
this.loadRoles();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (!isMerchantOwnerOnly()) return;
|
||||
if (!this.data.showEditPanel && !this.data.showCreatePanel) {
|
||||
this.loadRoles();
|
||||
}
|
||||
},
|
||||
|
||||
ensureOwnerAccess() {
|
||||
if (isStaffMode() || Number(wx.getStorageSync('shangjiastatus')) !== 1) {
|
||||
wx.showToast({ title: '仅商家老板可管理角色权限', icon: 'none' });
|
||||
setTimeout(() => wx.navigateBack(), 1200);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/** allPerms: [{perm_code, perm_name}] selectedCodes: ['order_dispatch', ...] */
|
||||
buildPermOptions(allPerms, selectedCodes) {
|
||||
const list = (allPerms && allPerms.length) ? allPerms : FALLBACK_PERMISSIONS;
|
||||
const set = new Set(selectedCodes || []);
|
||||
return list.map((p) => {
|
||||
const code = p.perm_code || p;
|
||||
const name = p.perm_name || code;
|
||||
return {
|
||||
perm_code: code,
|
||||
perm_name: name,
|
||||
checked: set.has(code),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
normalizeRoles(list) {
|
||||
return (list || []).map((r) => ({
|
||||
...r,
|
||||
permCount: (r.permissions && r.permissions.length) || 0,
|
||||
}));
|
||||
},
|
||||
|
||||
resolveAllPermissions(apiList) {
|
||||
if (apiList && apiList.length) return apiList;
|
||||
return FALLBACK_PERMISSIONS;
|
||||
},
|
||||
|
||||
async loadRoles() {
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const res = await request({ url: STAFF_API.roleList, method: 'POST' });
|
||||
if (res.data && res.data.code === 0) {
|
||||
const d = res.data.data || {};
|
||||
const rawPermissions = this.resolveAllPermissions(d.all_permissions);
|
||||
this.setData({
|
||||
roles: this.normalizeRoles(d.roles),
|
||||
rawPermissions,
|
||||
});
|
||||
return rawPermissions;
|
||||
}
|
||||
wx.showToast({ title: (res.data && res.data.msg) || '加载失败', icon: 'none' });
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
const fallback = FALLBACK_PERMISSIONS;
|
||||
this.setData({ rawPermissions: fallback });
|
||||
return fallback;
|
||||
},
|
||||
|
||||
goBack() { wx.navigateBack(); },
|
||||
|
||||
openCreate() {
|
||||
const raw = this.data.rawPermissions.length
|
||||
? this.data.rawPermissions
|
||||
: FALLBACK_PERMISSIONS;
|
||||
this.setData({
|
||||
showCreatePanel: true,
|
||||
showEditPanel: false,
|
||||
newRoleName: '',
|
||||
selectedPerms: [],
|
||||
permissionOptions: this.buildPermOptions(raw, []),
|
||||
});
|
||||
},
|
||||
|
||||
closeCreate() {
|
||||
this.setData({ showCreatePanel: false, permissionOptions: [], selectedPerms: [] });
|
||||
},
|
||||
|
||||
onNameInput(e) {
|
||||
this.setData({ newRoleName: e.detail.value });
|
||||
},
|
||||
|
||||
togglePermItem(e) {
|
||||
const code = e.currentTarget.dataset.code;
|
||||
if (!code) return;
|
||||
const opts = (this.data.permissionOptions || []).map((p) => (
|
||||
p.perm_code === code ? { ...p, checked: !p.checked } : p
|
||||
));
|
||||
this.setData({
|
||||
permissionOptions: opts,
|
||||
selectedPerms: opts.filter((x) => x.checked).map((x) => x.perm_code),
|
||||
});
|
||||
},
|
||||
|
||||
async editRole(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
let role = this.data.roles.find((r) => String(r.id) === String(id));
|
||||
if (!role) return;
|
||||
|
||||
let rawPermissions = this.data.rawPermissions;
|
||||
if (!rawPermissions.length) {
|
||||
rawPermissions = await this.loadRoles();
|
||||
role = this.data.roles.find((r) => String(r.id) === String(id));
|
||||
if (!role) return;
|
||||
}
|
||||
|
||||
const perms = role.permissions || [];
|
||||
this.setData({
|
||||
showEditPanel: true,
|
||||
showCreatePanel: false,
|
||||
editingRoleId: role.id,
|
||||
editingRoleName: role.role_name || '',
|
||||
selectedPerms: [...perms],
|
||||
permissionOptions: this.buildPermOptions(rawPermissions, perms),
|
||||
});
|
||||
},
|
||||
|
||||
closeEdit() {
|
||||
this.setData({
|
||||
showEditPanel: false,
|
||||
editingRoleId: null,
|
||||
editingRoleName: '',
|
||||
selectedPerms: [],
|
||||
permissionOptions: [],
|
||||
});
|
||||
},
|
||||
|
||||
async saveRolePerms() {
|
||||
const { editingRoleId, selectedPerms } = this.data;
|
||||
if (!editingRoleId) return;
|
||||
wx.showLoading({ title: '保存中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: STAFF_API.roleSetPerms,
|
||||
method: 'POST',
|
||||
data: { role_id: editingRoleId, permissions: selectedPerms },
|
||||
});
|
||||
wx.showToast({ title: (res.data && res.data.msg) || '已保存', icon: 'none' });
|
||||
if (res.data && res.data.code === 0) {
|
||||
this.closeEdit();
|
||||
this.loadRoles();
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
async createRole() {
|
||||
const name = (this.data.newRoleName || '').trim();
|
||||
if (!name) {
|
||||
wx.showToast({ title: '请输入角色名', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '创建中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: STAFF_API.roleCreate,
|
||||
method: 'POST',
|
||||
data: { role_name: name, permissions: this.data.selectedPerms },
|
||||
});
|
||||
wx.showToast({ title: (res.data && res.data.msg) || '完成', icon: 'none' });
|
||||
if (res.data && res.data.code === 0) {
|
||||
this.closeCreate();
|
||||
this.loadRoles();
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
});
|
||||
4
pages/merchant-staff-role/merchant-staff-role.json
Normal file
4
pages/merchant-staff-role/merchant-staff-role.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "角色与权限",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
75
pages/merchant-staff-role/merchant-staff-role.wxml
Normal file
75
pages/merchant-staff-role/merchant-staff-role.wxml
Normal file
@@ -0,0 +1,75 @@
|
||||
<view class="role-page">
|
||||
<view class="status-bar" style="height:{{statusBar}}px"></view>
|
||||
<view class="role-nav flexb">
|
||||
<text class="nav-back" bindtap="goBack">‹</text>
|
||||
<text class="nav-title">角色与权限</text>
|
||||
<text class="nav-link" bindtap="openCreate">新建</text>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y class="role-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view class="role-tip">可自由创建角色并勾选权限;预置角色也可改权限。</view>
|
||||
<view wx:if="{{!roles.length && !loading}}" class="role-empty">暂无角色,请先新建或联系管理员同步权限</view>
|
||||
<view wx:for="{{roles}}" wx:key="id" class="role-card" bindtap="editRole" data-id="{{item.id}}">
|
||||
<view class="flexb">
|
||||
<text class="role-name">{{item.role_name}}</text>
|
||||
<text class="role-tag">{{item.is_system ? '系统' : '自定义'}}</text>
|
||||
</view>
|
||||
<text class="role-sub">{{item.role_hint || ''}}</text>
|
||||
<text class="role-sub">已授权 {{item.permCount}} 项 · 点击编辑</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showEditPanel}}" class="role-mask">
|
||||
<view class="role-mask-bg" bindtap="closeEdit"></view>
|
||||
<view class="role-panel">
|
||||
<view class="panel-title">编辑角色权限</view>
|
||||
<text class="panel-role-name">{{editingRoleName}}</text>
|
||||
<scroll-view scroll-y class="perm-scroll" style="height:{{permScrollHeight}}px">
|
||||
<view
|
||||
wx:for="{{permissionOptions}}"
|
||||
wx:key="perm_code"
|
||||
class="perm-row flexb {{item.checked ? 'perm-on' : ''}}"
|
||||
bindtap="togglePermItem"
|
||||
data-code="{{item.perm_code}}"
|
||||
>
|
||||
<view class="perm-text">
|
||||
<text class="perm-name">{{item.perm_name}}</text>
|
||||
<text class="perm-code">{{item.perm_code}}</text>
|
||||
</view>
|
||||
<view class="perm-check">{{item.checked ? '✓' : ''}}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="panel-btns flexa">
|
||||
<button class="panel-btn cancel" bindtap="closeEdit" hover-class="btn-hover">取消</button>
|
||||
<button class="panel-btn ok" bindtap="saveRolePerms" hover-class="btn-hover">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showCreatePanel}}" class="role-mask">
|
||||
<view class="role-mask-bg" bindtap="closeCreate"></view>
|
||||
<view class="role-panel">
|
||||
<view class="panel-title">新建角色</view>
|
||||
<input class="name-input" placeholder="角色名称" value="{{newRoleName}}" bindinput="onNameInput"/>
|
||||
<scroll-view scroll-y class="perm-scroll" style="height:{{permScrollHeight}}px">
|
||||
<view
|
||||
wx:for="{{permissionOptions}}"
|
||||
wx:key="perm_code"
|
||||
class="perm-row flexb {{item.checked ? 'perm-on' : ''}}"
|
||||
bindtap="togglePermItem"
|
||||
data-code="{{item.perm_code}}"
|
||||
>
|
||||
<view class="perm-text">
|
||||
<text class="perm-name">{{item.perm_name}}</text>
|
||||
<text class="perm-code">{{item.perm_code}}</text>
|
||||
</view>
|
||||
<view class="perm-check">{{item.checked ? '✓' : ''}}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="panel-btns flexa">
|
||||
<button class="panel-btn cancel" bindtap="closeCreate" hover-class="btn-hover">取消</button>
|
||||
<button class="panel-btn ok" bindtap="createRole" hover-class="btn-hover">创建</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
221
pages/merchant-staff-role/merchant-staff-role.wxss
Normal file
221
pages/merchant-staff-role/merchant-staff-role.wxss
Normal file
@@ -0,0 +1,221 @@
|
||||
@import '../../styles/shangjia-xym-common.wxss';
|
||||
|
||||
.role-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #f7dc51 0%, #fff 28%, #fff8e1 100%);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.role-nav {
|
||||
padding: 0 24rpx 16rpx;
|
||||
}
|
||||
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
font-size: 48rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #343434;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
width: 60rpx;
|
||||
text-align: right;
|
||||
font-size: 28rpx;
|
||||
color: #c9a962;
|
||||
}
|
||||
|
||||
.role-scroll {
|
||||
height: calc(100vh - 120rpx);
|
||||
}
|
||||
|
||||
.role-tip {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
margin: 20rpx 24rpx;
|
||||
}
|
||||
|
||||
.role-empty {
|
||||
text-align: center;
|
||||
padding: 80rpx 24rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.role-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin: 0 24rpx 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.role-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.role-tag {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.role-sub {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 弹层:独立根节点,全屏 fixed */
|
||||
.role-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.role-mask-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.role-panel {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
padding: 32rpx 24rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||
z-index: 1001;
|
||||
max-height: 85vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.panel-role-name {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.perm-scroll {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.perm-row {
|
||||
padding: 20rpx 8rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
min-height: 88rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.perm-row.perm-on {
|
||||
background: #fffbf0;
|
||||
}
|
||||
|
||||
.perm-text {
|
||||
flex: 1;
|
||||
padding-right: 16rpx;
|
||||
}
|
||||
|
||||
.perm-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.perm-code {
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.perm-check {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: #c9a962;
|
||||
font-weight: 700;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.perm-on .perm-check {
|
||||
border-color: #c9a962;
|
||||
background: #c9a962;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.perm-empty {
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.name-input {
|
||||
border: 1rpx solid #eee;
|
||||
padding: 20rpx;
|
||||
margin: 16rpx 0;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.panel-btns {
|
||||
margin-top: 24rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.panel-btn {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.panel-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.panel-btn.cancel {
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.panel-btn.ok {
|
||||
background: #c9a962;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
import { createPage, ensureRoleOnCenterPage, lockPrimaryRole, parseSceneOptions, request } from '../../utils/base-page.js';
|
||||
import { ensureLogin } from '../../utils/login.js';
|
||||
import {
|
||||
STAFF_API, isStaffMode, isMerchantPortalUser, refreshStaffContext, syncStaffUi, getStaffContext,
|
||||
restoreStaffContextAfterAuth, applyStaffStatsToPage, guardMerchantOwnerAction, clearStaffSession,
|
||||
} from '../../utils/staff-api.js';
|
||||
import { clearCacheAndEnterNormal } from '../../utils/base-page.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
@@ -32,7 +37,15 @@ Page(createPage({
|
||||
|
||||
// 商家状态
|
||||
isShangjia: false,
|
||||
isStaffMode: false,
|
||||
staffRoleName: '',
|
||||
quotaAvailable: '0.00',
|
||||
canDispatch: true,
|
||||
canStaffManage: true,
|
||||
canAudit: true,
|
||||
showRechargeWithdraw: true,
|
||||
isAutoRegistering: false,
|
||||
staffBooting: false,
|
||||
|
||||
// 用户基础信息
|
||||
uid: '',
|
||||
@@ -57,7 +70,7 @@ Page(createPage({
|
||||
navBar: 44,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
async onLoad(options) {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
statusBar: sys.statusBarHeight || 20,
|
||||
@@ -65,6 +78,9 @@ Page(createPage({
|
||||
});
|
||||
const parsed = parseSceneOptions(options);
|
||||
this.setupImageUrls();
|
||||
if (wx.getStorageSync('token')) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
}
|
||||
this.checkRoleStatus();
|
||||
|
||||
const inviteCode = parsed.inviteCode;
|
||||
@@ -97,13 +113,33 @@ Page(createPage({
|
||||
this.checkColdStartPopup('shangjiaduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
this.registerNotificationComponent();
|
||||
if (this.data.isShangjia) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
this.refreshShangjiaInfo();
|
||||
this.loadPendingCount();
|
||||
this.loadPunishPending();
|
||||
try {
|
||||
const token = wx.getStorageSync('token');
|
||||
const isOwner = Number(wx.getStorageSync('shangjiastatus')) === 1;
|
||||
if (token && !isOwner) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
} else if (isStaffMode()) {
|
||||
await refreshStaffContext(request);
|
||||
}
|
||||
this.checkRoleStatus();
|
||||
syncStaffUi(this);
|
||||
if (this.data.isShangjia) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
if (isStaffMode()) {
|
||||
await this.loadStaffProfile();
|
||||
} else {
|
||||
this.refreshShangjiaInfo();
|
||||
}
|
||||
this.loadPendingCount();
|
||||
if (!isStaffMode()) {
|
||||
this.loadPunishPending();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('商家个人中心 onShow', err);
|
||||
this.setData({ isLoading: false, staffBooting: false });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -137,27 +173,102 @@ Page(createPage({
|
||||
});
|
||||
},
|
||||
|
||||
// 商家状态检查(覆盖基类 checkRoleStatus,增加 getShangjiaInfo 调用)
|
||||
// 商家状态检查(子客服与老板均可进入;子客服绝不走商家注册接口)
|
||||
checkRoleStatus() {
|
||||
try {
|
||||
const shangjiastatus = wx.getStorageSync('shangjiastatus');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (shangjiastatus) {
|
||||
this.setData({ isShangjia: true, uid: uid || '' });
|
||||
this.loadAvatar();
|
||||
this.getShangjiaInfo();
|
||||
const portal = isMerchantPortalUser();
|
||||
const staffFlag = Number(wx.getStorageSync('staffstatus')) === 1;
|
||||
|
||||
if (portal) {
|
||||
this.setData({ isShangjia: true, uid: uid || '', staffBooting: false, isLoading: false });
|
||||
syncStaffUi(this);
|
||||
if (isStaffMode()) {
|
||||
this.loadStaffProfile();
|
||||
} else if (Number(wx.getStorageSync('shangjiastatus')) === 1) {
|
||||
this.loadAvatar();
|
||||
this.getShangjiaInfo();
|
||||
} else if (staffFlag) {
|
||||
this.bootStaffPortal(uid);
|
||||
}
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
} else {
|
||||
this.setData({ isShangjia: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (staffFlag) {
|
||||
this.bootStaffPortal(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ isShangjia: false, staffBooting: false, isLoading: false });
|
||||
syncStaffUi(this);
|
||||
} catch (error) {
|
||||
console.error('读取商家状态失败', error);
|
||||
this.setData({ isShangjia: false });
|
||||
this.setData({ isShangjia: false, staffBooting: false, isLoading: false });
|
||||
syncStaffUi(this);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取商家信息
|
||||
/** 已绑定客服、尚未拉到 context 时恢复身份(不展示商家邀请码) */
|
||||
async bootStaffPortal(uid) {
|
||||
this.setData({ staffBooting: true, isLoading: true, uid: uid || '' });
|
||||
syncStaffUi(this);
|
||||
await restoreStaffContextAfterAuth();
|
||||
if (isMerchantPortalUser()) {
|
||||
this.setData({ isShangjia: true, staffBooting: false, isLoading: false });
|
||||
syncStaffUi(this);
|
||||
if (isStaffMode()) {
|
||||
await this.loadStaffProfile();
|
||||
}
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
return;
|
||||
}
|
||||
this.setData({ staffBooting: false, isLoading: false, isShangjia: false });
|
||||
syncStaffUi(this);
|
||||
wx.showToast({ title: '客服身份已失效,请联系商家老板', icon: 'none' });
|
||||
},
|
||||
|
||||
onInviteCodeInput(e) {
|
||||
this.setData({ inviteCode: (e.detail.value || '').trim() });
|
||||
},
|
||||
|
||||
goToStaffJoin() {
|
||||
wx.navigateTo({ url: '/pages/staff-join/staff-join' });
|
||||
},
|
||||
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '清除缓存',
|
||||
content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (r) => {
|
||||
if (!r.confirm) return;
|
||||
clearStaffSession();
|
||||
clearCacheAndEnterNormal(this);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
async loadStaffProfile() {
|
||||
const ctx = await refreshStaffContext(request);
|
||||
const profile = wx.getStorageSync('nicheng') || (ctx && ctx.display_name) || '商家客服';
|
||||
this.loadAvatar();
|
||||
this.setData({
|
||||
nicheng: (ctx && ctx.display_name) || profile,
|
||||
sjyue: (ctx && ctx.wallet && ctx.wallet.quota_available) || '0.00',
|
||||
chenghaoList: [],
|
||||
isLoading: false,
|
||||
});
|
||||
syncStaffUi(this);
|
||||
applyStaffStatsToPage(this, ctx);
|
||||
},
|
||||
|
||||
// 获取商家信息(仅商家老板;子客服禁止调用)
|
||||
async getShangjiaInfo() {
|
||||
if (isStaffMode() || Number(wx.getStorageSync('staffstatus')) === 1) {
|
||||
return this.loadStaffProfile();
|
||||
}
|
||||
await this.loadData({
|
||||
url: '/yonghu/shangjiaxinxi',
|
||||
method: 'POST',
|
||||
@@ -192,13 +303,17 @@ Page(createPage({
|
||||
},
|
||||
|
||||
refreshShangjiaInfo() {
|
||||
if (isStaffMode()) {
|
||||
this.throttledRefresh(() => this.loadStaffProfile());
|
||||
return;
|
||||
}
|
||||
this.throttledRefresh(() => this.getShangjiaInfo());
|
||||
},
|
||||
|
||||
async loadPendingCount() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: { zhuangtai_list: [8], page: 1, page_size: 1 },
|
||||
});
|
||||
@@ -273,8 +388,16 @@ Page(createPage({
|
||||
goToHome() { wx.redirectTo({ url: '/pages/merchant-home/merchant-home' }); },
|
||||
goToKefuKey() { wx.navigateTo({ url: '/pages/merchant-kefu-key/merchant-kefu-key' }); },
|
||||
goToKefuList() { wx.navigateTo({ url: '/pages/merchant-kefu-list/merchant-kefu-list' }); },
|
||||
goToRecharge() { wx.navigateTo({ url: '/pages/merchant-recharge/merchant-recharge' }); },
|
||||
goToWithdraw() { wx.navigateTo({ url: '/pages/withdraw/withdraw' }); },
|
||||
goToAuditLog() { wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit' }); },
|
||||
goToStaffRank() { wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit?tab=rank' }); },
|
||||
goToRecharge() {
|
||||
if (!guardMerchantOwnerAction('子客服无法充值,请联系商家老板')) return;
|
||||
wx.navigateTo({ url: '/pages/merchant-recharge/merchant-recharge' });
|
||||
},
|
||||
goToWithdraw() {
|
||||
if (!guardMerchantOwnerAction('子客服无法提现,请联系商家老板')) return;
|
||||
wx.navigateTo({ url: '/pages/withdraw/withdraw' });
|
||||
},
|
||||
goToRanking() { wx.navigateTo({ url: '/pages/fighter-rank/fighter-rank?type=shangjia' }); },
|
||||
goToMessages() { wx.navigateTo({ url: '/pages/merchant-msg/merchant-msg' }); },
|
||||
}, {
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
<!-- 商家端个人中心 - 逍遥梦布局,逻辑/接口不变 -->
|
||||
<block wx:if="{{!isShangjia}}">
|
||||
<block wx:if="{{staffBooting}}">
|
||||
<view class="sj-page user-page">
|
||||
<view class="sj-page-header">
|
||||
<view class="status-bar" style="height:{{statusBar}}px"></view>
|
||||
<view class="nav-bar flexm" style="height:{{navBar - statusBar}}px"><text class="sj-nav-title">我的</text></view>
|
||||
</view>
|
||||
<view class="unreg-area sj-mx20">
|
||||
<view class="sj-panel unreg-card">
|
||||
<text class="unreg-title">客服身份加载中</text>
|
||||
<text class="unreg-desc">请稍候,正在同步您的派单员权限…</text>
|
||||
</view>
|
||||
</view>
|
||||
<tab-bar/>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{!isShangjia && !isStaffMode}}">
|
||||
<view class="sj-page user-page">
|
||||
<view class="sj-page-header">
|
||||
<view class="status-bar" style="height:{{statusBar}}px"></view>
|
||||
@@ -8,8 +24,10 @@
|
||||
<view class="unreg-area sj-mx20">
|
||||
<view class="sj-panel unreg-card">
|
||||
<text class="unreg-title">商家入驻</text>
|
||||
<input class="unreg-input" placeholder="请输入邀请码" maxlength="100" value="{{inviteCode}}" bindinput="onInviteCodeInput"/>
|
||||
<view class="sj-btn-bg unreg-btn flexm" bindtap="onRegister">立即注册</view>
|
||||
<text class="unreg-desc">商家老板请填写商家邀请码;若您是客服/派单员,请走下方入口</text>
|
||||
<input class="unreg-input" placeholder="商家邀请码(老板专用)" maxlength="100" value="{{inviteCode}}" bindinput="onInviteCodeInput"/>
|
||||
<view class="sj-btn-bg unreg-btn flexm" bindtap="onRegister">商家注册</view>
|
||||
<view class="unreg-link staff-link" bindtap="goToStaffJoin">我是客服 / 派单员,绑定客服秘钥</view>
|
||||
<view class="unreg-link" bindtap="switchToNormal">返回点单端</view>
|
||||
<view class="unreg-link" bindtap="clearCache">清除缓存,重新登录</view>
|
||||
</view>
|
||||
@@ -36,7 +54,7 @@
|
||||
<view>
|
||||
<view class="nickname-row flex">
|
||||
<text class="nickname">{{nicheng || '商家'}}</text>
|
||||
<view class="sj-identity-tag">商家</view>
|
||||
<view class="sj-identity-tag">{{isStaffMode ? (staffRoleName || '商家客服') : '商家'}}</view>
|
||||
</view>
|
||||
<view class="sn-row flex" bindtap="copyUid">
|
||||
<text class="sn">ID: {{uid || '--'}}</text>
|
||||
@@ -56,11 +74,12 @@
|
||||
|
||||
<view class="sj-wallet-card flexb">
|
||||
<view>
|
||||
<view class="sj-w-label">商家余额(元)</view>
|
||||
<view class="sj-w-label">{{isStaffMode ? '可用派单额度(元)' : '商家余额(元)'}}</view>
|
||||
<view class="sj-w-num">¥{{sjyue}}</view>
|
||||
<view class="sj-w-sub">发单总量 {{fabu}} · 退款 {{tuikuan}}</view>
|
||||
<view class="sj-w-sub" wx:if="{{!isStaffMode}}">发单总量 {{fabu}} · 退款 {{tuikuan}}</view>
|
||||
<view class="sj-w-sub" wx:else>角色:{{staffRoleName}}</view>
|
||||
</view>
|
||||
<view class="wallet-btns flex">
|
||||
<view class="wallet-btns flex" wx:if="{{showRechargeWithdraw}}">
|
||||
<view class="sj-btn-outline" catchtap="goToRecharge">充值</view>
|
||||
<view class="sj-btn-outline" catchtap="goToWithdraw">去提现</view>
|
||||
</view>
|
||||
@@ -106,27 +125,35 @@
|
||||
<view class="sj-panel sj-mx20">
|
||||
<view class="sj-section-hd">交易与发单</view>
|
||||
<view class="sj-func-grid">
|
||||
<view class="sj-func-item" bindtap="goToPaiDan">
|
||||
<view class="sj-func-item" bindtap="goToPaiDan" wx:if="{{canDispatch}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconRelease}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">自定义单</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToJiSuPaiDan">
|
||||
<view class="sj-func-item" bindtap="goToJiSuPaiDan" wx:if="{{canDispatch}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconFast}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">常规单</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToKefuKey">
|
||||
<view class="sj-func-item" bindtap="goToKefuKey" wx:if="{{canStaffManage}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconService}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">客服邀请码</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToKefuList">
|
||||
<view class="sj-func-item" bindtap="goToKefuList" wx:if="{{canStaffManage}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconService}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">客服列表</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToMessages">
|
||||
<view class="sj-func-item" bindtap="goToAuditLog" wx:if="{{canAudit}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconOrders}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">操作日志</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToStaffRank" wx:if="{{canAudit}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconRank}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">客服排行</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToMessages" wx:if="{{!isStaffMode}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconPunish}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">处罚记录</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToRanking">
|
||||
<view class="sj-func-item" bindtap="goToRanking" wx:if="{{!isStaffMode}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconRank}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">商家排行</text>
|
||||
</view>
|
||||
|
||||
@@ -149,6 +149,20 @@ page {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.unreg-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.unreg-link.staff-link {
|
||||
color: #1976d2;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.unreg-input {
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
@@ -4,11 +4,14 @@ import { ensureLogin } from '../../utils/login';
|
||||
import {
|
||||
backfillUserProfileCache,
|
||||
getUserProfileForDisplay,
|
||||
getSessionToken,
|
||||
isRoleStatusActive,
|
||||
ensureRoleOnCenterPage,
|
||||
} from '../../utils/role-tab-bar';
|
||||
import { enterLockedRole } from '../../utils/primary-role.js';
|
||||
import { isStaffMode, getStaffContext, restoreStaffContextAfterAuth } from '../../utils/staff-api.js';
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
@@ -23,6 +26,8 @@ Page(createPage({
|
||||
zuzhangIcon: '', guanzhualongIcon: '', arrowRightIcon: '', qingchuIcon: '',
|
||||
dashouCertified: false,
|
||||
shangjiaCertified: false,
|
||||
staffCertified: false,
|
||||
staffTag: '入驻',
|
||||
bottomSafePadding: 0
|
||||
},
|
||||
|
||||
@@ -32,13 +37,26 @@ Page(createPage({
|
||||
this.setBottomSafe()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({
|
||||
redirect: true,
|
||||
returnPage: '/pages/mine/mine',
|
||||
});
|
||||
if (!phoneOk) return;
|
||||
|
||||
this.initIconUrls();
|
||||
this.registerNotificationComponent();
|
||||
this.checkAvatarPrompt();
|
||||
backfillUserProfileCache(app);
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
if (getSessionToken(app)) {
|
||||
restoreStaffContextAfterAuth().finally(() => {
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
});
|
||||
} else {
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
}
|
||||
},
|
||||
|
||||
setBottomSafe() {
|
||||
@@ -84,6 +102,8 @@ Page(createPage({
|
||||
|
||||
loadUserData() {
|
||||
const profile = getUserProfileForDisplay(app);
|
||||
const ctx = getStaffContext();
|
||||
const staffActive = isStaffMode();
|
||||
this.setData({
|
||||
avatarUrl: profile.avatarUrl,
|
||||
userNicheng: profile.nick,
|
||||
@@ -91,6 +111,8 @@ Page(createPage({
|
||||
showLoginBtn: !profile.isLoggedIn,
|
||||
dashouCertified: isRoleStatusActive(wx.getStorageSync('dashoustatus')),
|
||||
shangjiaCertified: isRoleStatusActive(wx.getStorageSync('shangjiastatus')),
|
||||
staffCertified: staffActive,
|
||||
staffTag: staffActive ? (ctx.role_name || '已入驻') : '入驻',
|
||||
});
|
||||
},
|
||||
|
||||
@@ -107,6 +129,7 @@ Page(createPage({
|
||||
handleWechatLogin() {
|
||||
wx.showLoading({ title: '登录中...', mask: true });
|
||||
ensureLogin({ silent: false, page: this })
|
||||
.then(() => restoreStaffContextAfterAuth())
|
||||
.then(() => {
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
@@ -122,8 +145,13 @@ Page(createPage({
|
||||
const type = e.currentTarget.dataset.type;
|
||||
if (!type || !['dashou', 'shangjia'].includes(type)) return;
|
||||
ensureLogin({ silent: false, page: this })
|
||||
.then(() => restoreStaffContextAfterAuth())
|
||||
.then(() => {
|
||||
this.loadUserData();
|
||||
if (type === 'shangjia' && isStaffMode()) {
|
||||
enterLockedRole('shangjia', app);
|
||||
return;
|
||||
}
|
||||
const statusKey = type === 'dashou' ? 'dashoustatus' : 'shangjiastatus';
|
||||
if (isRoleStatusActive(wx.getStorageSync(statusKey))) {
|
||||
ensureRoleOnCenterPage(this, type);
|
||||
@@ -136,5 +164,20 @@ Page(createPage({
|
||||
},
|
||||
|
||||
goToCustomService() { this.goToKefu() },
|
||||
|
||||
goToStaffJoin() {
|
||||
ensureLogin({ silent: false, page: this })
|
||||
.then(() => restoreStaffContextAfterAuth())
|
||||
.then(() => {
|
||||
this.loadUserData();
|
||||
if (isStaffMode()) {
|
||||
enterLockedRole('shangjia', app);
|
||||
return;
|
||||
}
|
||||
wx.navigateTo({ url: '/pages/staff-join/staff-join' });
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
goToGuanzhuA() { wx.navigateTo({ url: '/pages/manager-assign/manager-assign' }) },
|
||||
}))
|
||||
|
||||
@@ -88,6 +88,13 @@
|
||||
<text class="service-name">商家</text>
|
||||
<text class="auth-tag {{shangjiaCertified ? 'auth-tag-done' : ''}}">{{shangjiaCertified ? '已认证' : '去认证'}}</text>
|
||||
</view>
|
||||
<view class="service-unit" bindtap="goToStaffJoin">
|
||||
<view class="icon-bg icon-bg-sm">
|
||||
<image src="{{kefuIcon}}" mode="aspectFit" class="icon-dark icon-size-sm" />
|
||||
</view>
|
||||
<text class="service-name">商家客服</text>
|
||||
<text class="auth-tag {{staffCertified ? 'auth-tag-done' : ''}}">{{staffTag}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { submit } from '../../utils/phone-auth';
|
||||
import { submit, getPhoneAuthReturnPage } from '../../utils/phone-auth';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -27,8 +27,7 @@ Page({
|
||||
wx.showLoading({ title: '认证中...', mask: true });
|
||||
await submit(phoneCode, avatarFile);
|
||||
wx.hideLoading();
|
||||
// 成功,重启小程序,此时check返回false,用户无感进入
|
||||
wx.reLaunch({ url: '/pages/index/index' });
|
||||
wx.reLaunch({ url: getPhoneAuthReturnPage() });
|
||||
} catch (e) {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
@@ -37,4 +36,4 @@ Page({
|
||||
exit() {
|
||||
wx.exitMiniProgram();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
55
pages/staff-join/staff-join.js
Normal file
55
pages/staff-join/staff-join.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import request from '../../utils/request.js';
|
||||
import { STAFF_API, applyStaffFromLoginData } from '../../utils/staff-api.js';
|
||||
import { enterLockedRole, lockPrimaryRole } from '../../utils/primary-role.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
inviteCode: '',
|
||||
loading: false,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const code = options.code || options.inviteCode || '';
|
||||
if (code) this.setData({ inviteCode: decodeURIComponent(code) });
|
||||
},
|
||||
|
||||
onInput(e) {
|
||||
this.setData({ inviteCode: (e.detail.value || '').trim() });
|
||||
},
|
||||
|
||||
async onBind() {
|
||||
const inviteCode = this.data.inviteCode;
|
||||
if (!inviteCode) {
|
||||
wx.showToast({ title: '请输入邀请码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const res = await request({
|
||||
url: STAFF_API.bind,
|
||||
method: 'POST',
|
||||
data: { inviteCode },
|
||||
});
|
||||
const d = res.data || {};
|
||||
if (d.code === 0 && d.data && d.data.staff_context) {
|
||||
applyStaffFromLoginData({
|
||||
staffstatus: 1,
|
||||
staff_context: d.data.staff_context,
|
||||
});
|
||||
lockPrimaryRole('shangjia', app);
|
||||
wx.showToast({ title: '绑定成功', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
enterLockedRole('shangjia', app);
|
||||
}, 800);
|
||||
} else {
|
||||
wx.showToast({ title: d.msg || '绑定失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
});
|
||||
4
pages/staff-join/staff-join.json
Normal file
4
pages/staff-join/staff-join.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "客服入驻",
|
||||
"usingComponents": {}
|
||||
}
|
||||
13
pages/staff-join/staff-join.wxml
Normal file
13
pages/staff-join/staff-join.wxml
Normal file
@@ -0,0 +1,13 @@
|
||||
<view class="page">
|
||||
<view class="card">
|
||||
<text class="title">成为商家客服</text>
|
||||
<text class="desc">输入商家提供的一次性邀请码,绑定后可代商家派单(受权限与额度限制)</text>
|
||||
<input class="input" placeholder="例如 PD-ABC123" value="{{inviteCode}}" bindinput="onInput" />
|
||||
<button class="btn" loading="{{loading}}" bindtap="onBind">确认绑定</button>
|
||||
</view>
|
||||
<view class="tips">
|
||||
<text>· 已是商家账号无法绑定客服</text>
|
||||
<text>· 同时只能绑定一个商家</text>
|
||||
<text>· 被移除后可重新绑定或认证商家</text>
|
||||
</view>
|
||||
</view>
|
||||
8
pages/staff-join/staff-join.wxss
Normal file
8
pages/staff-join/staff-join.wxss
Normal file
@@ -0,0 +1,8 @@
|
||||
.page { min-height: 100vh; background: #f5f5f5; padding: 40rpx; box-sizing: border-box; }
|
||||
.card { background: #fff; border-radius: 16rpx; padding: 40rpx; }
|
||||
.title { display: block; font-size: 36rpx; font-weight: 600; color: #333; margin-bottom: 16rpx; }
|
||||
.desc { display: block; font-size: 26rpx; color: #666; line-height: 1.6; margin-bottom: 32rpx; }
|
||||
.input { border: 1px solid #ddd; border-radius: 12rpx; padding: 24rpx; font-size: 30rpx; margin-bottom: 32rpx; }
|
||||
.btn { background: #C9A962; color: #fff; border-radius: 12rpx; }
|
||||
.tips { margin-top: 32rpx; font-size: 24rpx; color: #999; line-height: 2; }
|
||||
.tips text { display: block; }
|
||||
@@ -1,6 +1,7 @@
|
||||
// pages/verify/verify.js
|
||||
import { createPage, request } from '../../utils/base-page.js'
|
||||
import { enterLockedRole } from '../../utils/primary-role.js'
|
||||
import { isStaffMode, restoreStaffContextAfterAuth } from '../../utils/staff-api.js'
|
||||
|
||||
const API_MAP = {
|
||||
dashou: '/yonghu/dashouzhuce',
|
||||
@@ -46,8 +47,17 @@ Page(createPage({
|
||||
this.checkStatus()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (this.data.authType) this.checkStatus()
|
||||
async onShow() {
|
||||
if (this.data.authType) {
|
||||
if (this.data.authType === 'shangjia' && wx.getStorageSync('token')) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
if (isStaffMode()) {
|
||||
enterLockedRole('shangjia', getApp());
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.checkStatus();
|
||||
}
|
||||
},
|
||||
|
||||
checkStatus() {
|
||||
|
||||
Reference in New Issue
Block a user