1 parent
a751708d6e
commit
7f4f279b3f
124 files changed
+15837
-21958
No files matched your search
@@ -1,414 +1,451 @@
|
||||
// app.js
|
||||
import GoEasy from './static/lib/goeasy-2.13.24.esm.min';
|
||||
const ChatCore = require('./utils/chat-core');
|
||||
import { check } from './utils/phone-auth';
|
||||
import { getPrimaryRole, lockPrimaryRole, migrateLegacyCenterRole, PRIMARY_DEFAULT_PAGES } from './utils/primary-role';
|
||||
|
||||
// 三端固定首页
|
||||
const roleDefaultPage = PRIMARY_DEFAULT_PAGES;
|
||||
|
||||
App({
|
||||
globalData: {
|
||||
|
||||
hasShownPopupOnColdStart: false,
|
||||
|
||||
apiBaseUrl: 'https://www.abas.asia/hqhd',
|
||||
ossImageUrl: '',
|
||||
morentouxiang: '',
|
||||
dashouguize: '',
|
||||
|
||||
shangpinliebiao: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinleixing: [],
|
||||
shangpinlunbo: [],
|
||||
shangpingonggao: '',
|
||||
lunbozhanwei: '/images/lunbozhanwei.jpg',
|
||||
|
||||
dingdanTiaoshu: {
|
||||
daifuwu: 0,
|
||||
fuwuzhong: 0,
|
||||
yiwancheng: 0,
|
||||
yituikuan: 0
|
||||
},
|
||||
|
||||
dashouqun: '',
|
||||
dashouqunid: '',
|
||||
guanshiqun: '',
|
||||
guanshiqunid: '',
|
||||
|
||||
appId:'wx0e4be86faac4a8d1',
|
||||
|
||||
shangjiastatus: 0,
|
||||
dashoustatus: 0,
|
||||
guanshistatus: 0,
|
||||
dashouNicheng: '',
|
||||
zhanghaoStatus: null,
|
||||
dashouzhuangtai: null,
|
||||
yongjin: null,
|
||||
zonge: null,
|
||||
yajin: null,
|
||||
chenghao: '',
|
||||
jinfen: null,
|
||||
clumber: [{
|
||||
huiyuanid: '',
|
||||
huiyuanming: '',
|
||||
daoqi: ''
|
||||
}],
|
||||
chengjiaoliang: null,
|
||||
zaixianZhuangtai: null,
|
||||
|
||||
guanshi: {
|
||||
nicheng: '',
|
||||
uid: '',
|
||||
touxiang: '',
|
||||
gszhstatus: '',
|
||||
yaoqingzongshu: 0,
|
||||
fenyongzonge: '0.00',
|
||||
fenyongtixian: '0.00'
|
||||
},
|
||||
|
||||
shangjia: {
|
||||
sjzhzhuangtai: '',
|
||||
sjyue: '',
|
||||
fadanzong: null,
|
||||
tuikuanzong: null,
|
||||
riliushui: '',
|
||||
yueliushui: ''
|
||||
},
|
||||
|
||||
liaotian_liebiao: [],
|
||||
xshenfen: 1,
|
||||
|
||||
goEasyConfig: null,
|
||||
kefuConfig: {
|
||||
link: '',
|
||||
enterpriseId: ''
|
||||
},
|
||||
|
||||
cosConfig: {
|
||||
bucket: '',
|
||||
region: 'ap-shanghai',
|
||||
uploadPathPrefix: 'order/'
|
||||
},
|
||||
|
||||
goEasyConnection: {
|
||||
status: 'disconnected',
|
||||
userId: '',
|
||||
identityType: '',
|
||||
lastConnectTime: 0,
|
||||
autoReconnect: true,
|
||||
reconnectAttempts: 0,
|
||||
maxReconnectAttempts: 5,
|
||||
heartbeatInterval: null,
|
||||
cacheKeys: {
|
||||
savedConnection: 'savedGoEasyConnection',
|
||||
userId: 'goEasyUserId',
|
||||
identityType: 'currentGoEasyIdentity',
|
||||
connectTime: 'goEasyConnectTime'
|
||||
},
|
||||
config: {
|
||||
heartbeatInterval: 20000,
|
||||
reconnectDelay: 3000,
|
||||
offlineTimeout: 30000,
|
||||
cacheValidityHours: 12
|
||||
}
|
||||
},
|
||||
|
||||
messageManager: {
|
||||
unreadTotal: 0,
|
||||
unreadMap: {},
|
||||
latestMessages: [],
|
||||
notificationQueue: [],
|
||||
notificationVisible: false,
|
||||
lastNotificationTime: 0,
|
||||
notificationCooldown: 3000,
|
||||
tabBarIndex: 2,
|
||||
showTabBarBadge: true,
|
||||
customTabBar: true,
|
||||
tabBarBadgeText: 0,
|
||||
soundEnabled: true,
|
||||
vibrationEnabled: true,
|
||||
doNotDisturb: false,
|
||||
doNotDisturbStart: '22:00',
|
||||
doNotDisturbEnd: '08:00',
|
||||
notificationStyle: {
|
||||
position: 'top',
|
||||
duration: 3000,
|
||||
backgroundColor: '#00f7ff',
|
||||
textColor: '#ffffff',
|
||||
borderRadius: '16rpx',
|
||||
boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)'
|
||||
},
|
||||
currentNotification: null,
|
||||
cacheKeys: {
|
||||
messageSettings: 'messageSettings',
|
||||
unreadTotal: 'messageUnreadTotal',
|
||||
latestMessages: 'latestMessages',
|
||||
notificationMuted: 'notificationMuted'
|
||||
}
|
||||
},
|
||||
|
||||
pageState: {
|
||||
currentPage: '',
|
||||
isInChatPage: false,
|
||||
currentChatId: '',
|
||||
lastPageUpdate: 0
|
||||
},
|
||||
|
||||
eventListeners: {},
|
||||
debugMode: false,
|
||||
currentUser: null,
|
||||
currentRole: 'normal',
|
||||
primaryRole: 'normal'
|
||||
},
|
||||
|
||||
// 核心启动流程
|
||||
async onLaunch() {
|
||||
// ① 隐藏返回首页按钮
|
||||
wx.hideHomeButton();
|
||||
wx.onAppRoute((res) => {
|
||||
wx.hideHomeButton();
|
||||
const path = (res && res.path) || '';
|
||||
if (path === 'pages/manager/manager' || path === 'pages/leader/leader') {
|
||||
migrateLegacyCenterRole(this);
|
||||
lockPrimaryRole('dashou', this);
|
||||
}
|
||||
});
|
||||
|
||||
// ② 恢复主端并跳转
|
||||
migrateLegacyCenterRole(this);
|
||||
const savedRole = getPrimaryRole(this);
|
||||
lockPrimaryRole(savedRole, this);
|
||||
|
||||
const targetPage = roleDefaultPage[savedRole] || '/pages/index/index';
|
||||
if (savedRole !== 'normal') {
|
||||
setTimeout(() => {
|
||||
wx.reLaunch({ url: targetPage });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// ③ 应用本地缓存配置
|
||||
const cachedConfig = this.readConfigFromStorage();
|
||||
if (cachedConfig) {
|
||||
this.applyDynamicConfig(cachedConfig);
|
||||
}
|
||||
|
||||
// ④ 初始化
|
||||
this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化
|
||||
ChatCore.initGlobalMessageSystem(this);
|
||||
this.initCurrentUser();
|
||||
|
||||
// ⑤ 建立连接
|
||||
const saved = this.getSavedConnection();
|
||||
console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
|
||||
'userId:', saved ? saved.userId : '无');
|
||||
setTimeout(() => {
|
||||
if (this.startImWhenReady) this.startImWhenReady();
|
||||
else if (this.ensureConnection) this.ensureConnection();
|
||||
}, 300);
|
||||
|
||||
// ⑥ 获取远程配置
|
||||
this.fetchConfigSafely()
|
||||
.then(latestConfig => {
|
||||
this.saveConfigToStorage(latestConfig);
|
||||
this.applyDynamicConfig(latestConfig);
|
||||
this.initGoEasyWithConfig();
|
||||
this.initCurrentUser();
|
||||
if (this.startImWhenReady) this.startImWhenReady();
|
||||
else if (this.connectForCurrentRole) this.connectForCurrentRole();
|
||||
console.log('远程配置更新完成');
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn('远程配置获取失败,继续使用本地缓存', err);
|
||||
});
|
||||
|
||||
// ⑦ 手机号认证检查
|
||||
try {
|
||||
const needAuth = await check();
|
||||
if (needAuth) {
|
||||
wx.reLaunch({ url: '/pages/phone-auth/phone-auth' });
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// 连接管理方法
|
||||
getSavedConnection() {
|
||||
try {
|
||||
const saved = wx.getStorageSync(
|
||||
this.globalData.goEasyConnection.cacheKeys.savedConnection
|
||||
);
|
||||
return saved || null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// 配置缓存读写
|
||||
CONFIG_CACHE_KEY: 'app_dynamic_config',
|
||||
|
||||
readConfigFromStorage() {
|
||||
try {
|
||||
const data = wx.getStorageSync(this.CONFIG_CACHE_KEY);
|
||||
if (data && typeof data === 'object') {
|
||||
return data;
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
},
|
||||
|
||||
saveConfigToStorage(config) {
|
||||
try {
|
||||
if (config && typeof config === 'object') {
|
||||
wx.setStorageSync(this.CONFIG_CACHE_KEY, config);
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// 获取远程配置
|
||||
fetchConfigSafely() {
|
||||
return new Promise((resolve) => {
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
|
||||
resolve(res.data.data);
|
||||
} else {
|
||||
console.warn('远程配置接口返回异常,使用已有配置');
|
||||
resolve(this.readConfigFromStorage() || {});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.warn('远程配置网络请求失败,使用已有配置', err);
|
||||
resolve(this.readConfigFromStorage() || {});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 获取远程配置
|
||||
fetchDynamicConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
resolve(res.data.data);
|
||||
} else {
|
||||
reject(new Error(res.data?.msg || '接口返回错误'));
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
applyDynamicConfig(config) {
|
||||
if (!config || typeof config !== 'object') return;
|
||||
|
||||
if (config.cos) {
|
||||
this.globalData.ossImageUrl = config.cos.ossImageUrl || '';
|
||||
this.globalData.cosConfig = {
|
||||
bucket: config.cos.bucket || '',
|
||||
region: config.cos.region || 'ap-shanghai',
|
||||
uploadPathPrefix: config.cos.uploadPathPrefix || 'order/'
|
||||
};
|
||||
}
|
||||
|
||||
if (config.goEasy) {
|
||||
this.globalData.goEasyConfig = {
|
||||
host: config.goEasy.host || 'hangzhou.goeasy.io',
|
||||
appkey: config.goEasy.appkey || ''
|
||||
};
|
||||
}
|
||||
|
||||
if (config.otherConfig) {
|
||||
this.globalData.morentouxiang = config.otherConfig.morentouxiang || '';
|
||||
this.globalData.dashouguize = config.otherConfig.dashouguize || '';
|
||||
}
|
||||
|
||||
if (config.kefu) {
|
||||
this.globalData.kefuConfig = {
|
||||
link: config.kefu.link || '',
|
||||
enterpriseId: config.kefu.enterpriseId || ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 仅在有效 appkey 时初始化
|
||||
initGoEasyWithConfig() {
|
||||
const cfg = this.globalData.goEasyConfig;
|
||||
if (!cfg || !cfg.appkey) {
|
||||
console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复');
|
||||
return;
|
||||
}
|
||||
|
||||
// 避免重复初始化
|
||||
if (wx.goEasy && wx.GoEasy) {
|
||||
console.log('GoEasy 已初始化,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
wx.goEasy = GoEasy.getInstance({
|
||||
host: cfg.host || 'hangzhou.goeasy.io',
|
||||
appkey: cfg.appkey,
|
||||
modules: ['im', 'pubsub']
|
||||
});
|
||||
wx.GoEasy = GoEasy;
|
||||
console.log('GoEasy 初始化成功');
|
||||
if (this.startImWhenReady && wx.getStorageSync('uid')) {
|
||||
setTimeout(() => this.startImWhenReady(), 100);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('GoEasy 初始化失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
initCurrentUser() {
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (uid) {
|
||||
this.globalData.currentUser = {
|
||||
id: uid,
|
||||
name: '用户' + uid.substring(0, 6),
|
||||
avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
emitEvent(eventName, data) {
|
||||
if (this.globalData.eventListeners[eventName]) {
|
||||
this.globalData.eventListeners[eventName].forEach(callback => {
|
||||
try { callback(data); } catch (error) { console.error(error); }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** 切换角色并通知刷新 */
|
||||
setCurrentRole(role) {
|
||||
if (!role) return;
|
||||
this.globalData.currentRole = role;
|
||||
wx.setStorageSync('currentRole', role);
|
||||
this.emitEvent('currentRoleChanged', { role });
|
||||
},
|
||||
|
||||
on(eventName, callback) {
|
||||
if (!this.globalData.eventListeners[eventName]) {
|
||||
this.globalData.eventListeners[eventName] = [];
|
||||
}
|
||||
this.globalData.eventListeners[eventName].push(callback);
|
||||
},
|
||||
|
||||
off(eventName, callback) {
|
||||
if (this.globalData.eventListeners[eventName]) {
|
||||
const index = this.globalData.eventListeners[eventName].indexOf(callback);
|
||||
if (index > -1) {
|
||||
this.globalData.eventListeners[eventName].splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// app.js
|
||||
import GoEasy from './static/lib/goeasy-2.13.24.esm.min';
|
||||
const ChatCore = require('./utils/chat-core');
|
||||
import { ensurePhoneAuth } from './utils/phone-auth';
|
||||
import { getPrimaryRole, lockPrimaryRole, migrateLegacyCenterRole, PRIMARY_DEFAULT_PAGES } from './utils/primary-role';
|
||||
import { setClubId, getConfiguredClubId, buildClubHeaders, getClubId } from './utils/club-context';
|
||||
import { CLUB_ID, WX_APP_ID } from './config/club-config';
|
||||
import { applyMiniappAssetsFromConfig, warmupPindaoConfig } from './utils/miniapp-icons.js';
|
||||
import { refreshDashouMembership } from './utils/dashou-profile.js';
|
||||
|
||||
// 三端固定首页
|
||||
const roleDefaultPage = PRIMARY_DEFAULT_PAGES;
|
||||
|
||||
App({
|
||||
globalData: {
|
||||
|
||||
hasShownPopupOnColdStart: false,
|
||||
|
||||
apiBaseUrl: 'https://www.abas.asia/hqhd',
|
||||
ossImageUrl: '',
|
||||
morentouxiang: '',
|
||||
dashouguize: '',
|
||||
|
||||
shangpinliebiao: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinleixing: [],
|
||||
shangpinlunbo: [],
|
||||
shangpingonggao: '',
|
||||
lunbozhanwei: '/images/lunbozhanwei.jpg',
|
||||
|
||||
dingdanTiaoshu: {
|
||||
daifuwu: 0,
|
||||
fuwuzhong: 0,
|
||||
yiwancheng: 0,
|
||||
yituikuan: 0
|
||||
},
|
||||
|
||||
dashouqun: '',
|
||||
dashouqunid: '',
|
||||
guanshiqun: '',
|
||||
guanshiqunid: '',
|
||||
|
||||
appId: WX_APP_ID,
|
||||
clubId: CLUB_ID,
|
||||
|
||||
shangjiastatus: 0,
|
||||
dashoustatus: 0,
|
||||
guanshistatus: 0,
|
||||
dashouNicheng: '',
|
||||
zhanghaoStatus: null,
|
||||
dashouzhuangtai: null,
|
||||
yongjin: null,
|
||||
zonge: null,
|
||||
yajin: null,
|
||||
chenghao: '',
|
||||
jinfen: null,
|
||||
clumber: [],
|
||||
chengjiaoliang: null,
|
||||
zaixianZhuangtai: null,
|
||||
|
||||
guanshi: {
|
||||
nicheng: '',
|
||||
uid: '',
|
||||
touxiang: '',
|
||||
gszhstatus: '',
|
||||
yaoqingzongshu: 0,
|
||||
fenyongzonge: '0.00',
|
||||
fenyongtixian: '0.00'
|
||||
},
|
||||
|
||||
shangjia: {
|
||||
sjzhzhuangtai: '',
|
||||
sjyue: '',
|
||||
fadanzong: null,
|
||||
tuikuanzong: null,
|
||||
riliushui: '',
|
||||
yueliushui: ''
|
||||
},
|
||||
|
||||
liaotian_liebiao: [],
|
||||
xshenfen: 1,
|
||||
|
||||
goEasyConfig: null,
|
||||
kefuConfig: {
|
||||
link: '',
|
||||
enterpriseId: ''
|
||||
},
|
||||
|
||||
cosConfig: {
|
||||
bucket: '',
|
||||
region: 'ap-shanghai',
|
||||
uploadPathPrefix: 'order/'
|
||||
},
|
||||
|
||||
goEasyConnection: {
|
||||
status: 'disconnected',
|
||||
userId: '',
|
||||
identityType: '',
|
||||
lastConnectTime: 0,
|
||||
autoReconnect: true,
|
||||
reconnectAttempts: 0,
|
||||
maxReconnectAttempts: 5,
|
||||
heartbeatInterval: null,
|
||||
cacheKeys: {
|
||||
savedConnection: 'savedGoEasyConnection',
|
||||
userId: 'goEasyUserId',
|
||||
identityType: 'currentGoEasyIdentity',
|
||||
connectTime: 'goEasyConnectTime'
|
||||
},
|
||||
config: {
|
||||
heartbeatInterval: 20000,
|
||||
reconnectDelay: 3000,
|
||||
offlineTimeout: 30000,
|
||||
cacheValidityHours: 12
|
||||
}
|
||||
},
|
||||
|
||||
messageManager: {
|
||||
unreadTotal: 0,
|
||||
unreadMap: {},
|
||||
latestMessages: [],
|
||||
notificationQueue: [],
|
||||
notificationVisible: false,
|
||||
lastNotificationTime: 0,
|
||||
notificationCooldown: 3000,
|
||||
tabBarIndex: 2,
|
||||
showTabBarBadge: true,
|
||||
customTabBar: true,
|
||||
tabBarBadgeText: 0,
|
||||
soundEnabled: true,
|
||||
vibrationEnabled: true,
|
||||
doNotDisturb: false,
|
||||
doNotDisturbStart: '22:00',
|
||||
doNotDisturbEnd: '08:00',
|
||||
notificationStyle: {
|
||||
position: 'top',
|
||||
duration: 3000,
|
||||
backgroundColor: '#00f7ff',
|
||||
textColor: '#ffffff',
|
||||
borderRadius: '16rpx',
|
||||
boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)'
|
||||
},
|
||||
currentNotification: null,
|
||||
cacheKeys: {
|
||||
messageSettings: 'messageSettings',
|
||||
unreadTotal: 'messageUnreadTotal',
|
||||
latestMessages: 'latestMessages',
|
||||
notificationMuted: 'notificationMuted'
|
||||
}
|
||||
},
|
||||
|
||||
pageState: {
|
||||
currentPage: '',
|
||||
isInChatPage: false,
|
||||
currentChatId: '',
|
||||
lastPageUpdate: 0
|
||||
},
|
||||
|
||||
eventListeners: {},
|
||||
debugMode: false,
|
||||
currentUser: null,
|
||||
currentRole: 'normal',
|
||||
primaryRole: 'normal'
|
||||
},
|
||||
|
||||
// 核心启动流程
|
||||
async onLaunch() {
|
||||
setClubId(getConfiguredClubId(), this);
|
||||
this.globalData.clubId = CLUB_ID;
|
||||
// ① 隐藏返回首页按钮
|
||||
wx.hideHomeButton();
|
||||
wx.onAppRoute((res) => {
|
||||
wx.hideHomeButton();
|
||||
const path = (res && res.path) || '';
|
||||
if (path === 'pages/manager/manager' || path === 'pages/leader/leader') {
|
||||
migrateLegacyCenterRole(this);
|
||||
lockPrimaryRole('dashou', this);
|
||||
}
|
||||
});
|
||||
|
||||
// ② 已登录时先恢复子客服身份,再决定主端 Tab(避免客服被当成未入驻)
|
||||
if (wx.getStorageSync('token')) {
|
||||
try {
|
||||
const { restoreStaffContextAfterAuth } = require('./utils/staff-api.js');
|
||||
await restoreStaffContextAfterAuth();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
migrateLegacyCenterRole(this);
|
||||
const savedRole = getPrimaryRole(this);
|
||||
lockPrimaryRole(savedRole, this);
|
||||
|
||||
// ②b 冷启动:有 token 时先问后端是否需要手机号认证
|
||||
if (wx.getStorageSync('token')) {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true });
|
||||
if (!phoneOk) return;
|
||||
}
|
||||
|
||||
// ②c 打手端冷启动:同步会员/押金等到 globalData(抢单页依赖 clumber)
|
||||
if (wx.getStorageSync('token') && savedRole === 'dashou') {
|
||||
refreshDashouMembership(this).catch(() => {});
|
||||
}
|
||||
|
||||
const targetPage = roleDefaultPage[savedRole] || '/pages/index/index';
|
||||
if (savedRole !== 'normal') {
|
||||
setTimeout(() => {
|
||||
wx.reLaunch({ url: targetPage });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// ③ 应用本地缓存配置
|
||||
const cachedConfig = this.readConfigFromStorage();
|
||||
if (cachedConfig) {
|
||||
this.applyDynamicConfig(cachedConfig);
|
||||
}
|
||||
|
||||
// ④ 初始化
|
||||
this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化
|
||||
ChatCore.initGlobalMessageSystem(this);
|
||||
this.initCurrentUser();
|
||||
if (wx.getStorageSync('token') && this.emitEvent) {
|
||||
this.emitEvent('staffContextChanged', {});
|
||||
}
|
||||
|
||||
// ⑤ 建立连接
|
||||
const saved = this.getSavedConnection();
|
||||
console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
|
||||
'userId:', saved ? saved.userId : '无');
|
||||
setTimeout(() => {
|
||||
if (this.startImWhenReady) this.startImWhenReady();
|
||||
else if (this.ensureConnection) this.ensureConnection();
|
||||
}, 300);
|
||||
|
||||
// ⑥ 获取远程配置
|
||||
this.fetchConfigSafely()
|
||||
.then(latestConfig => {
|
||||
this.saveConfigToStorage(latestConfig);
|
||||
this.applyDynamicConfig(latestConfig);
|
||||
warmupPindaoConfig(this);
|
||||
try {
|
||||
const { fetchGonggaoLunbo } = require('./utils/display-config');
|
||||
fetchGonggaoLunbo(this, 'accept_order').catch(() => {});
|
||||
} catch (e) {}
|
||||
this.initGoEasyWithConfig();
|
||||
this.initCurrentUser();
|
||||
if (this.startImWhenReady) this.startImWhenReady();
|
||||
else if (this.connectForCurrentRole) this.connectForCurrentRole();
|
||||
console.log('远程配置更新完成');
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn('远程配置获取失败,继续使用本地缓存', err);
|
||||
});
|
||||
},
|
||||
|
||||
/** 从后台切回前台时再次向后端校验 */
|
||||
async onShow() {
|
||||
if (!wx.getStorageSync('token')) return;
|
||||
await ensurePhoneAuth({ redirect: true });
|
||||
},
|
||||
|
||||
// 连接管理方法
|
||||
getSavedConnection() {
|
||||
try {
|
||||
const saved = wx.getStorageSync(
|
||||
this.globalData.goEasyConnection.cacheKeys.savedConnection
|
||||
);
|
||||
return saved || null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// 配置缓存读写
|
||||
CONFIG_CACHE_KEY_PREFIX: 'app_dynamic_config_',
|
||||
|
||||
getConfigCacheKey() {
|
||||
const cid = getClubId(this) || CLUB_ID;
|
||||
return this.CONFIG_CACHE_KEY_PREFIX + cid;
|
||||
},
|
||||
|
||||
readConfigFromStorage() {
|
||||
try {
|
||||
const data = wx.getStorageSync(this.getConfigCacheKey());
|
||||
if (data && typeof data === 'object') {
|
||||
return data;
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
},
|
||||
|
||||
saveConfigToStorage(config) {
|
||||
try {
|
||||
if (config && typeof config === 'object') {
|
||||
wx.setStorageSync(this.getConfigCacheKey(), config);
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// 获取远程配置
|
||||
fetchConfigSafely() {
|
||||
return new Promise((resolve) => {
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: buildClubHeaders({ 'content-type': 'application/json' }),
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
|
||||
resolve(res.data.data);
|
||||
} else {
|
||||
console.warn('远程配置接口返回异常,使用已有配置');
|
||||
resolve(this.readConfigFromStorage() || {});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.warn('远程配置网络请求失败,使用已有配置', err);
|
||||
resolve(this.readConfigFromStorage() || {});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 获取远程配置
|
||||
fetchDynamicConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: buildClubHeaders({ 'content-type': 'application/json' }),
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
resolve(res.data.data);
|
||||
} else {
|
||||
reject(new Error(res.data?.msg || '接口返回错误'));
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
applyDynamicConfig(config) {
|
||||
if (!config || typeof config !== 'object') return;
|
||||
|
||||
if (config.cos) {
|
||||
this.globalData.ossImageUrl = config.cos.ossImageUrl || '';
|
||||
this.globalData.cosConfig = {
|
||||
bucket: config.cos.bucket || '',
|
||||
region: config.cos.region || 'ap-shanghai',
|
||||
uploadPathPrefix: config.cos.uploadPathPrefix || 'order/'
|
||||
};
|
||||
}
|
||||
|
||||
if (config.goEasy) {
|
||||
this.globalData.goEasyConfig = {
|
||||
host: config.goEasy.host || 'hangzhou.goeasy.io',
|
||||
appkey: config.goEasy.appkey || ''
|
||||
};
|
||||
}
|
||||
|
||||
if (config.otherConfig) {
|
||||
this.globalData.morentouxiang = config.otherConfig.morentouxiang || '';
|
||||
this.globalData.dashouguize = config.otherConfig.dashouguize || '';
|
||||
}
|
||||
|
||||
if (config.kefu) {
|
||||
this.globalData.kefuConfig = {
|
||||
link: config.kefu.link || '',
|
||||
enterpriseId: config.kefu.enterpriseId || ''
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
applyMiniappAssetsFromConfig(this, config);
|
||||
} catch (e) {
|
||||
console.warn('miniapp assets apply failed', e);
|
||||
}
|
||||
},
|
||||
|
||||
// 仅在有效 appkey 时初始化
|
||||
initGoEasyWithConfig() {
|
||||
const cfg = this.globalData.goEasyConfig;
|
||||
if (!cfg || !cfg.appkey) {
|
||||
console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复');
|
||||
return;
|
||||
}
|
||||
|
||||
// 避免重复初始化
|
||||
if (wx.goEasy && wx.GoEasy) {
|
||||
console.log('GoEasy 已初始化,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
wx.goEasy = GoEasy.getInstance({
|
||||
host: cfg.host || 'hangzhou.goeasy.io',
|
||||
appkey: cfg.appkey,
|
||||
modules: ['im', 'pubsub']
|
||||
});
|
||||
wx.GoEasy = GoEasy;
|
||||
console.log('GoEasy 初始化成功');
|
||||
if (this.startImWhenReady && wx.getStorageSync('uid')) {
|
||||
setTimeout(() => this.startImWhenReady(), 100);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('GoEasy 初始化失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
initCurrentUser() {
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (uid) {
|
||||
this.globalData.currentUser = {
|
||||
id: uid,
|
||||
name: '用户' + uid.substring(0, 6),
|
||||
avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
emitEvent(eventName, data) {
|
||||
if (this.globalData.eventListeners[eventName]) {
|
||||
this.globalData.eventListeners[eventName].forEach(callback => {
|
||||
try { callback(data); } catch (error) { console.error(error); }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** 切换角色并通知刷新 */
|
||||
setCurrentRole(role) {
|
||||
if (!role) return;
|
||||
this.globalData.currentRole = role;
|
||||
wx.setStorageSync('currentRole', role);
|
||||
this.emitEvent('currentRoleChanged', { role });
|
||||
},
|
||||
|
||||
on(eventName, callback) {
|
||||
if (!this.globalData.eventListeners[eventName]) {
|
||||
this.globalData.eventListeners[eventName] = [];
|
||||
}
|
||||
this.globalData.eventListeners[eventName].push(callback);
|
||||
},
|
||||
|
||||
off(eventName, callback) {
|
||||
if (this.globalData.eventListeners[eventName]) {
|
||||
const index = this.globalData.eventListeners[eventName].indexOf(callback);
|
||||
if (index > -1) {
|
||||
this.globalData.eventListeners[eventName].splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -13,6 +13,9 @@
|
||||
"pages/merchant-home/merchant-home",
|
||||
"pages/merchant-kefu-key/merchant-kefu-key",
|
||||
"pages/merchant-kefu-list/merchant-kefu-list",
|
||||
"pages/staff-join/staff-join",
|
||||
"pages/merchant-staff-audit/merchant-staff-audit",
|
||||
"pages/merchant-staff-role/merchant-staff-role",
|
||||
"pages/manager/manager",
|
||||
"pages/orders/orders",
|
||||
"pages/order-detail/order-detail",
|
||||
@@ -23,14 +26,17 @@
|
||||
"pages/fighter-msg/fighter-msg",
|
||||
"pages/fighter-orders/fighter-orders",
|
||||
"pages/accept-order/accept-order",
|
||||
"pages/dashou-exam/dashou-exam",
|
||||
"pages/withdraw/withdraw",
|
||||
"pages/invite-fighter/invite-fighter",
|
||||
"pages/my-fighter/my-fighter",
|
||||
"pages/recharge-log/recharge-log",
|
||||
"pages/manager-rank/manager-rank",
|
||||
"pages/merchant-dispatch/merchant-dispatch",
|
||||
"pages/merchant-regular-dispatch/merchant-regular-dispatch",
|
||||
"pages/merchant-orders/merchant-orders",
|
||||
"pages/merchant-msg/merchant-msg",
|
||||
"pages/merchant-penalty/merchant-penalty",
|
||||
"pages/merchant-rank/merchant-rank",
|
||||
"pages/merchant-recharge/merchant-recharge",
|
||||
"pages/merchant-order-detail/merchant-order-detail",
|
||||
@@ -44,7 +50,6 @@
|
||||
"pages/manager-assign/manager-assign",
|
||||
"pages/leader-bonus-log/leader-bonus-log",
|
||||
"pages/invite-manager/invite-manager",
|
||||
"pages/order-pool2/order-pool2",
|
||||
"pages/group-chat/group-chat",
|
||||
"pages/cs-chat/cs-chat",
|
||||
"pages/verify/verify",
|
||||
@@ -59,22 +64,33 @@
|
||||
"pages/phone-auth/phone-auth",
|
||||
"components/chenghao-tag/chenghao-tag",
|
||||
"pages/assessor/assessor",
|
||||
"pages/assess-score/assess-score",
|
||||
"pages/assess-log/assess-log",
|
||||
"pages/assess-center/assess-center",
|
||||
"pages/assess-gold/assess-gold",
|
||||
"pages/escort-orders/escort-orders",
|
||||
"pages/withdraw/components/mode1/mode1",
|
||||
"pages/withdraw/components/mode2/mode2"
|
||||
|
||||
|
||||
],
|
||||
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/assess-center",
|
||||
"name": "assess",
|
||||
"pages": ["assess-center"]
|
||||
},
|
||||
{
|
||||
"root": "pages/assess-score",
|
||||
"pages": ["assess-score"]
|
||||
},
|
||||
{
|
||||
"root": "pages/assess-log",
|
||||
"pages": ["assess-log"]
|
||||
},
|
||||
{
|
||||
"root": "pages/assess-gold",
|
||||
"pages": ["assess-gold"]
|
||||
}
|
||||
],
|
||||
"usingComponents": {
|
||||
"popup-notice": "/components/popup-notice/popup-notice",
|
||||
"tab-bar": "/tab-bar/index"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationStyle": "default",
|
||||
@@ -85,26 +101,33 @@
|
||||
"tabBar": {
|
||||
"custom": true,
|
||||
"list": [
|
||||
|
||||
|
||||
{
|
||||
"pagePath": "pages/order-pool/order-pool",
|
||||
"pagePath": "pages/accept-order/accept-order",
|
||||
"text": "接单池",
|
||||
"iconPath": "/images/order-pool.png",
|
||||
"selectedIconPath": "/images/order-pool.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/order-pool2/order-pool2",
|
||||
"text": "接单池",
|
||||
"iconPath": "/images/order-pool.png",
|
||||
"selectedIconPath": "/images/order-pool.png"
|
||||
"pagePath": "pages/fighter-orders/fighter-orders",
|
||||
"text": "订单",
|
||||
"iconPath": "/images/orders.png",
|
||||
"selectedIconPath": "/images/orders.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/messages/messages",
|
||||
"text": "消息",
|
||||
"iconPath": "/images/messages.png",
|
||||
"selectedIconPath": "/images/messages.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/fighter/fighter",
|
||||
"text": "我的",
|
||||
"iconPath": "/images/mine.png",
|
||||
"selectedIconPath": "/images/mine.png"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
"style": "v2",
|
||||
"sitemapLocation": "sitemap.json",
|
||||
"lazyCodeLoading": "requiredComponents"
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,106 @@
|
||||
// components/chenghao-tag/chenghao-tag.js
|
||||
const app = getApp();
|
||||
|
||||
const PILL_SHAPES = ['pill', 'rectangle', 'rounded'];
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
mingcheng: { type: String, value: '' },
|
||||
texiaoJson: { type: Object, value: {} }
|
||||
texiaoJson: { type: Object, value: {} },
|
||||
},
|
||||
data: {
|
||||
// 默认值:宽152,高52,六边形,无背景图,无动画,白色字
|
||||
width: 152,
|
||||
height: 52,
|
||||
shapeClass: 'liubianxing', // 保证至少不是矩形
|
||||
inlineStyle: '',
|
||||
shapeClass: 'tag-pill',
|
||||
animationClass: '',
|
||||
bgStyle: '',
|
||||
textColor: '#FFFFFF',
|
||||
textSize: 22,
|
||||
imageUrl: ''
|
||||
imageUrl: '',
|
||||
isPill: true,
|
||||
},
|
||||
observers: {
|
||||
'texiaoJson, mingcheng': function () {
|
||||
this._applyConfig(this.properties.texiaoJson);
|
||||
},
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const cfg = this.properties.texiaoJson || {};
|
||||
|
||||
// 1. 尺寸(稍大一些,一行能放3~4个)
|
||||
const width = cfg.width || 152;
|
||||
const height = cfg.height || 52;
|
||||
this._applyConfig(this.properties.texiaoJson);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
_applyConfig(raw) {
|
||||
const cfg = raw || {};
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
|
||||
// 2. 背景处理:优先背景图,否则用渐变/纯色
|
||||
// 无 shape:身份装饰标签 / 自定义色圆角标;有 shape:考核称号等特殊形状
|
||||
const isPill = !cfg.shape || PILL_SHAPES.includes(cfg.shape);
|
||||
|
||||
if (isPill) {
|
||||
const height = Number(cfg.height) || 44;
|
||||
const minWidth = Number(cfg.width) || 72;
|
||||
const solidColor = cfg.bg_color || cfg.background || cfg.color;
|
||||
let bgStyle = '';
|
||||
if (cfg.bg_gradient) {
|
||||
bgStyle = `background: ${cfg.bg_gradient};`;
|
||||
} else if (solidColor) {
|
||||
bgStyle = `background: ${solidColor};`;
|
||||
} else {
|
||||
bgStyle = 'background: linear-gradient(135deg, #FFD700, #FF8C00);';
|
||||
}
|
||||
const flash = cfg.flash || cfg.animation === 'shine' || cfg.animation === 'glow';
|
||||
let animationClass = cfg.animation || '';
|
||||
if (flash && !animationClass) {
|
||||
animationClass = 'pill-flash';
|
||||
}
|
||||
const borderColor = cfg.borderColor || cfg.border_color;
|
||||
const borderStyle = borderColor ? `border: 2rpx solid ${borderColor};` : '';
|
||||
const textColor = cfg.text_color || cfg.textColor || '#FFFFFF';
|
||||
this.setData({
|
||||
isPill: true,
|
||||
shapeClass: 'tag-pill',
|
||||
animationClass,
|
||||
inlineStyle: `min-width: ${minWidth}rpx; height: ${height}rpx; padding: 0 18rpx; ${bgStyle} ${borderStyle}`,
|
||||
textColor,
|
||||
textSize: cfg.text_size || 22,
|
||||
imageUrl: '',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const width = Number(cfg.width) || 152;
|
||||
const height = Number(cfg.height) || 52;
|
||||
let bgStyle = '';
|
||||
let imageUrl = '';
|
||||
if (cfg.image_url) {
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
imageUrl = cfg.image_url.startsWith('http')
|
||||
? cfg.image_url
|
||||
imageUrl = cfg.image_url.startsWith('http')
|
||||
? cfg.image_url
|
||||
: ossImageUrl + cfg.image_url;
|
||||
} else if (cfg.bg_gradient) {
|
||||
bgStyle = `background: ${cfg.bg_gradient};`;
|
||||
} else if (cfg.bg_color) {
|
||||
bgStyle = `background: ${cfg.bg_color};`;
|
||||
} else {
|
||||
// 默认金橙渐变
|
||||
bgStyle = `background: linear-gradient(135deg, #FFD700, #FF8C00);`;
|
||||
bgStyle = 'background: linear-gradient(135deg, #FFD700, #FF8C00);';
|
||||
}
|
||||
|
||||
// 3. 形状(后端传入 shape 字段,默认 liubianxing)
|
||||
const shapeClass = cfg.shape || 'liubianxing';
|
||||
|
||||
// 4. 动画
|
||||
const animationClass = cfg.animation || '';
|
||||
|
||||
// 5. 文字
|
||||
const textColor = cfg.text_color || '#FFFFFF';
|
||||
const textSize = cfg.text_size || 22;
|
||||
|
||||
this.setData({
|
||||
width, height,
|
||||
bgStyle, imageUrl,
|
||||
shapeClass, animationClass,
|
||||
textColor, textSize
|
||||
isPill: false,
|
||||
inlineStyle: `width: ${width}rpx; height: ${height}rpx; ${bgStyle}`,
|
||||
shapeClass: cfg.shape || 'liubianxing',
|
||||
animationClass: cfg.animation || '',
|
||||
textColor: cfg.text_color || '#FFFFFF',
|
||||
textSize: cfg.text_size || 22,
|
||||
imageUrl,
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 背景图加载失败时回退为纯色背景
|
||||
},
|
||||
|
||||
onImageError() {
|
||||
this.setData({ imageUrl: '' });
|
||||
// 如果 bgStyle 为空,给个默认背景
|
||||
if (!this.data.bgStyle) {
|
||||
this.setData({ bgStyle: 'background: linear-gradient(135deg, #FFD700, #FF8C00);' });
|
||||
if (!this.data.inlineStyle.includes('background')) {
|
||||
this.setData({
|
||||
inlineStyle: this.data.inlineStyle + ' background: linear-gradient(135deg, #FFD700, #FF8C00);',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,20 +1,17 @@
|
||||
<!-- components/chenghao-tag/chenghao-tag.wxml -->
|
||||
<view
|
||||
class="tag-root {{shapeClass}} {{animationClass}}"
|
||||
style="width: {{width}}rpx; height: {{height}}rpx; {{bgStyle}}"
|
||||
<view
|
||||
class="tag-root {{shapeClass}} {{animationClass}}"
|
||||
style="{{inlineStyle}}"
|
||||
>
|
||||
<!-- 背景图(如果有) -->
|
||||
<image
|
||||
wx:if="{{imageUrl}}"
|
||||
class="tag-bg-image"
|
||||
src="{{imageUrl}}"
|
||||
<image
|
||||
wx:if="{{imageUrl && !isPill}}"
|
||||
class="tag-bg-image"
|
||||
src="{{imageUrl}}"
|
||||
mode="aspectFill"
|
||||
binderror="onImageError"
|
||||
></image>
|
||||
|
||||
<!-- 文字层 -->
|
||||
<text
|
||||
class="tag-text"
|
||||
/>
|
||||
<text
|
||||
class="tag-text"
|
||||
style="color: {{textColor}}; font-size: {{textSize}}rpx;"
|
||||
>{{mingcheng}}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -12,6 +12,18 @@
|
||||
margin: 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 身份装饰 / 自定义色:圆角标 */
|
||||
.tag-root.tag-pill {
|
||||
border-radius: 20rpx;
|
||||
clip-path: none;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.tag-pill .tag-text {
|
||||
padding: 0;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
/* ========== 背景图 ========== */
|
||||
.tag-bg-image {
|
||||
@@ -87,4 +99,19 @@
|
||||
@keyframes glow-anim {
|
||||
0%, 100% { box-shadow: 0 0 8rpx rgba(255, 215, 0, 0.4); }
|
||||
50% { box-shadow: 0 0 20rpx rgba(255, 215, 0, 0.8), 0 0 40rpx rgba(255, 215, 0, 0.4); }
|
||||
}
|
||||
|
||||
/* 身份标签闪光 */
|
||||
.tag-root.pill-flash::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
|
||||
transform: skewX(-20deg);
|
||||
animation: shine-anim 2.2s infinite;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Component({
|
||||
properties: {
|
||||
visible: { type: Boolean, value: false },
|
||||
title: { type: String, value: '频道' },
|
||||
channelNo: { type: String, value: '' },
|
||||
images: { type: Array, value: [] },
|
||||
},
|
||||
|
||||
methods: {
|
||||
preventMove() {},
|
||||
onClose() {
|
||||
this.triggerEvent('close');
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<view class="pindao-mask" wx:if="{{visible}}" catchtouchmove="preventMove">
|
||||
<view class="pindao-panel" catchtap="preventMove">
|
||||
<view class="pindao-head flexb">
|
||||
<text class="pindao-title">{{title}}</text>
|
||||
<view class="pindao-close" bindtap="onClose">×</view>
|
||||
</view>
|
||||
<view class="pindao-channel" wx:if="{{channelNo}}">
|
||||
<text class="pindao-channel-label">频道号</text>
|
||||
<text class="pindao-channel-no">{{channelNo}}</text>
|
||||
</view>
|
||||
<scroll-view class="pindao-scroll" scroll-y enhanced show-scrollbar="{{false}}">
|
||||
<view wx:if="{{images.length === 0}}" class="pindao-empty">暂无频道内容</view>
|
||||
<image
|
||||
wx:for="{{images}}"
|
||||
wx:key="index"
|
||||
class="pindao-img"
|
||||
src="{{item}}"
|
||||
mode="widthFix"
|
||||
lazy-load="{{false}}"
|
||||
show-menu-by-longpress
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,88 @@
|
||||
.pindao-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pindao-panel {
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pindao-head {
|
||||
padding: 28rpx 32rpx 16rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pindao-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.pindao-close {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
line-height: 52rpx;
|
||||
text-align: center;
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.pindao-channel {
|
||||
padding: 0 32rpx 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pindao-channel-label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.pindao-channel-no {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #c9a962;
|
||||
}
|
||||
|
||||
.pindao-scroll {
|
||||
flex: 1;
|
||||
max-height: 60vh;
|
||||
padding: 0 24rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pindao-img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.pindao-empty {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 48rpx 0;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.flexb {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 小程序所属俱乐部配置 — 每个小程序工程编译前改这里即可。
|
||||
* 星阙 = xq;星之界 = xzj;与 club 表 club_id 一致。
|
||||
* 不要依赖后端猜测:前端必须明确所属 club。
|
||||
*/
|
||||
export const CLUB_ID = 'xq';
|
||||
|
||||
/** 与 club 表 wx_appid 一致,登录时可传给后端反查 club */
|
||||
export const WX_APP_ID = 'wx0e4be86faac4a8d1';
|
||||
|
||||
export const CLUB_NAME = '星阙电竞';
|
||||
+243
-110
@@ -1,8 +1,14 @@
|
||||
// pages/qiangdan/qiangdan.js
|
||||
const app = getApp();
|
||||
import request from '../../utils/request.js';
|
||||
import { normalizeOrderTags } from '../../utils/order-tags.js';
|
||||
import { refreshDashouMembership, userHasHuiyuan } from '../../utils/dashou-profile.js';
|
||||
import { getDefaultAvatarUrl } from '../../utils/avatar.js';
|
||||
import { warmupPindaoConfig } from '../../utils/miniapp-icons.js';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { reconnectForRole } from '../../utils/role-tab-bar.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
import { fetchGonggaoLunbo, isGonggaoCacheValid } from '../../utils/display-config.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -29,7 +35,7 @@ Page({
|
||||
// 4. 刷新控制字段
|
||||
scrollViewRefreshing: false,
|
||||
lastRefreshTime: 0,
|
||||
refreshCooldown: 2000,
|
||||
refreshCooldown: 0,
|
||||
isLoadingMore: false,
|
||||
|
||||
// 5. 切换类型冷却
|
||||
@@ -46,13 +52,59 @@ Page({
|
||||
|
||||
// 商品轮播展示(只读 globalData,无额外接口)
|
||||
lunboList: [],
|
||||
gonggao: '',
|
||||
|
||||
examRequired: false,
|
||||
examPassed: false,
|
||||
_examChecked: false,
|
||||
},
|
||||
|
||||
async onLoad(options) {
|
||||
this.syncShopBannerFromGlobal();
|
||||
async onLoad() {
|
||||
if (app.globalData._acceptOrderSessionReady && app.globalData._acceptOrderPageCache) {
|
||||
const cache = app.globalData._acceptOrderPageCache;
|
||||
this.setData({ ...cache });
|
||||
if ((!cache.lunboList || cache.lunboList.length === 0) && !cache.gonggao && isGonggaoCacheValid(app)) {
|
||||
const lunboList = this.processLunboUrls(app.globalData.shangpinlunbo);
|
||||
const gonggao = app.globalData.shangpingonggao || '';
|
||||
this.setData({ lunboList, gonggao });
|
||||
this.persistPageCache({ lunboList, gonggao });
|
||||
}
|
||||
this.loadGlobalStatus();
|
||||
this.registerNotificationComponent();
|
||||
await this.syncDashouProfileFromServer();
|
||||
this._skipShowRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadGlobalStatus();
|
||||
await this.loadShangpinLeixing();
|
||||
PopupService.checkAndShow(this, 'jiedan');
|
||||
const banner = await this.loadGonggaoAndLunbo(false);
|
||||
await this.syncDashouProfileFromServer();
|
||||
await this.loadShangpinLeixing(true, false, false);
|
||||
this.persistPageCache(banner);
|
||||
app.globalData._acceptOrderSessionReady = true;
|
||||
|
||||
if (!app.globalData._acceptOrderPopupDone) {
|
||||
PopupService.checkAndShow(this, 'jiedan');
|
||||
app.globalData._acceptOrderPopupDone = true;
|
||||
}
|
||||
this._skipShowRefresh = true;
|
||||
},
|
||||
|
||||
persistPageCache(extra = {}) {
|
||||
const d = this.data;
|
||||
app.globalData._acceptOrderPageCache = {
|
||||
shangpinleixing: d.shangpinleixing,
|
||||
xuanzhongLeixingId: d.xuanzhongLeixingId,
|
||||
dingdanList: d.dingdanList,
|
||||
page: d.page,
|
||||
hasMore: d.hasMore,
|
||||
bankuaiBiaoqian: d.bankuaiBiaoqian,
|
||||
xuanzhongBiaoqianId: d.xuanzhongBiaoqianId,
|
||||
lunboList: extra.lunboList ?? d.lunboList,
|
||||
gonggao: extra.gonggao ?? d.gonggao,
|
||||
lastRefreshTime: d.lastRefreshTime,
|
||||
ossImageUrl: d.ossImageUrl,
|
||||
};
|
||||
},
|
||||
|
||||
onHide: function () {
|
||||
@@ -62,63 +114,84 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncShopBannerFromGlobal();
|
||||
async onShow() {
|
||||
if (!app.globalData._dashouPhoneChecked) {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
|
||||
if (!phoneOk) return;
|
||||
app.globalData._dashouPhoneChecked = true;
|
||||
}
|
||||
|
||||
this.registerNotificationComponent();
|
||||
this.loadGlobalStatus();
|
||||
await this.syncDashouProfileFromServer();
|
||||
warmupPindaoConfig(app);
|
||||
|
||||
if (wx.getStorageSync('uid')) {
|
||||
reconnectForRole('dashou');
|
||||
if (app.startImWhenReady) app.startImWhenReady();
|
||||
this.refreshDashouProfileSilent();
|
||||
}
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
this.loadDingdanList(true);
|
||||
|
||||
await this.checkExamStatus(false);
|
||||
|
||||
if (this._skipShowRefresh) {
|
||||
this._skipShowRefresh = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (app.globalData._acceptOrderSessionReady && !this._silentRefreshRunning) {
|
||||
this._silentRefreshRunning = true;
|
||||
this.silentRefreshOnShow().finally(() => {
|
||||
this._silentRefreshRunning = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** 同步点单端已加载的商品轮播图,仅展示用 */
|
||||
syncShopBannerFromGlobal() {
|
||||
const oss = app.globalData.ossImageUrl || '';
|
||||
const lunboList = (app.globalData.shangpinlunbo || []).map((url) => {
|
||||
/** 进入页面静默刷新(无 loading 遮罩、不清空列表) */
|
||||
async silentRefreshOnShow() {
|
||||
try {
|
||||
const banner = await this.loadGonggaoAndLunbo(true);
|
||||
await this.syncDashouProfileFromServer();
|
||||
const leixingId = this.data.xuanzhongLeixingId;
|
||||
if (leixingId) {
|
||||
await this.loadDingdanList(true, true);
|
||||
await this.loadBankuaiBiaoqian();
|
||||
}
|
||||
this.loadGlobalStatus();
|
||||
this.persistPageCache(banner);
|
||||
} catch (e) {
|
||||
console.error('静默刷新失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
/** 公告 + 轮播(首次/下拉强制拉接口,其余读 globalData 或页面缓存) */
|
||||
processLunboUrls(urlList) {
|
||||
const oss = app.globalData.ossImageUrl || this.data.ossImageUrl || '';
|
||||
return (urlList || []).map((url) => {
|
||||
if (!url) return '';
|
||||
return url.startsWith('http') ? url : oss + url;
|
||||
}).filter(Boolean);
|
||||
this.setData({ lunboList });
|
||||
},
|
||||
|
||||
async refreshDashouProfileSilent() {
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/dashouxinxi', method: 'POST' });
|
||||
if (!res || res.data.code != 200) return;
|
||||
const data = res.data.data || {};
|
||||
const patch = {
|
||||
dashouNicheng: data.dashounicheng || '',
|
||||
zhanghaoStatus: data.zhanghaostatus || '',
|
||||
dashouzhuangtai: data.dashouzhuangtai || '',
|
||||
yajin: data.yajin || 0,
|
||||
jifen: data.jifen || 0,
|
||||
clumber: data.clumber || [],
|
||||
};
|
||||
Object.assign(app.globalData, {
|
||||
dashouNicheng: patch.dashouNicheng,
|
||||
zhanghaoStatus: patch.zhanghaoStatus,
|
||||
dashouzhuangtai: patch.dashouzhuangtai,
|
||||
yajin: patch.yajin,
|
||||
jinfen: patch.jifen,
|
||||
clumber: patch.clumber,
|
||||
});
|
||||
if (data.dashoustatus !== undefined) {
|
||||
wx.setStorageSync('dashoustatus', data.dashoustatus);
|
||||
app.globalData.dashoustatus = data.dashoustatus;
|
||||
async loadGonggaoAndLunbo(forceFetch = false) {
|
||||
let lunboRaw = app.globalData.shangpinlunbo || [];
|
||||
let gonggaoText = app.globalData.shangpingonggao || '';
|
||||
|
||||
if (forceFetch || !isGonggaoCacheValid(app)) {
|
||||
try {
|
||||
const data = await fetchGonggaoLunbo(app, 'accept_order');
|
||||
if (data) {
|
||||
lunboRaw = data.shangpinlunbo || app.globalData.shangpinlunbo || [];
|
||||
gonggaoText = data.shangpingonggao || app.globalData.shangpingonggao || '';
|
||||
}
|
||||
} catch (e) {
|
||||
lunboRaw = app.globalData.shangpinlunbo || [];
|
||||
gonggaoText = app.globalData.shangpingonggao || '';
|
||||
}
|
||||
this.setData({
|
||||
zhuanghaoStatus: patch.zhanghaoStatus,
|
||||
dashouzhuangtai: patch.dashouzhuangtai,
|
||||
huiyuanList: patch.clumber,
|
||||
yajin: patch.yajin,
|
||||
jifen: patch.jifen,
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
const lunboList = this.processLunboUrls(lunboRaw);
|
||||
const gonggao = gonggaoText;
|
||||
this.setData({ lunboList, gonggao });
|
||||
return { lunboList, gonggao };
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
@@ -144,9 +217,22 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
/** 拉取最新打手信息(会员/押金/积分),并刷新订单卡片上的会员展示 */
|
||||
async syncDashouProfileFromServer() {
|
||||
if (!wx.getStorageSync('token')) return;
|
||||
await refreshDashouMembership(app);
|
||||
this.loadGlobalStatus();
|
||||
if (this.data.dingdanList && this.data.dingdanList.length) {
|
||||
this.setData({
|
||||
dingdanList: this.data.dingdanList.map((item) => this.processDingdanItem(item)),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 加载商品类型
|
||||
async loadShangpinLeixing() {
|
||||
wx.showLoading({ title: '加载商品类型...' });
|
||||
async loadShangpinLeixing(autoLoadOrders = false, preserveSelection = false, showLoading = true) {
|
||||
if (showLoading) wx.showLoading({ title: '加载商品类型...' });
|
||||
const prevId = this.data.xuanzhongLeixingId;
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/dsqdhqddlx',
|
||||
@@ -163,14 +249,16 @@ Page({
|
||||
else if (Array.isArray(data)) list = data;
|
||||
}
|
||||
const processedList = this.processTupianUrl(list);
|
||||
const firstId = processedList[0]?.id || null;
|
||||
const keepPrev = preserveSelection && prevId && processedList.some((i) => i.id === prevId);
|
||||
const selectedId = keepPrev ? prevId : firstId;
|
||||
this.setData({
|
||||
shangpinleixing: processedList,
|
||||
xuanzhongLeixingId: processedList[0]?.id || null
|
||||
xuanzhongLeixingId: selectedId,
|
||||
});
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
this.loadDingdanList(true);
|
||||
// 🆕 加载板块标签
|
||||
this.loadBankuaiBiaoqian();
|
||||
if (autoLoadOrders && selectedId) {
|
||||
await this.loadDingdanList(true);
|
||||
await this.loadBankuaiBiaoqian();
|
||||
}
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '加载商品类型失败', icon: 'none' });
|
||||
@@ -178,7 +266,7 @@ Page({
|
||||
} catch (error) {
|
||||
wx.showToast({ title: '网络错误,加载失败', icon: 'none' });
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
if (showLoading) wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -245,8 +333,8 @@ Page({
|
||||
xuanzhongBiaoqianId: 0 // 重置标签筛选
|
||||
});
|
||||
await this.loadDingdanList(true);
|
||||
// 🆕 切换类型后加载新标签
|
||||
await this.loadBankuaiBiaoqian();
|
||||
this.persistPageCache();
|
||||
} catch (error) {
|
||||
console.error('切换类型失败:', error);
|
||||
} finally {
|
||||
@@ -266,19 +354,24 @@ Page({
|
||||
hasMore: true
|
||||
});
|
||||
await this.loadDingdanList(true);
|
||||
this.persistPageCache();
|
||||
},
|
||||
|
||||
// 加载订单列表
|
||||
async loadDingdanList(isRefresh = false) {
|
||||
if (this.data.isLoading || !this.data.xuanzhongLeixingId) return;
|
||||
async loadDingdanList(isRefresh = false, silent = false) {
|
||||
if ((!silent && this.data.isLoading) || !this.data.xuanzhongLeixingId) return;
|
||||
if (silent && this._listRequesting) return;
|
||||
|
||||
const loadPage = isRefresh ? 1 : this.data.page;
|
||||
if (!isRefresh && !this.data.hasMore) return;
|
||||
|
||||
this.setData({
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh
|
||||
});
|
||||
if (!silent) {
|
||||
this.setData({
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh,
|
||||
});
|
||||
}
|
||||
this._listRequesting = true;
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
@@ -298,6 +391,7 @@ Page({
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
});
|
||||
this._listRequesting = false;
|
||||
|
||||
if (res.data.code === 200 || res.data.code === 0) {
|
||||
const newList = res.data.data.list || [];
|
||||
@@ -313,6 +407,7 @@ Page({
|
||||
page: loadPage,
|
||||
hasMore: hasMore
|
||||
});
|
||||
this.persistPageCache();
|
||||
|
||||
if (isRefresh) {
|
||||
this.setData({ lastRefreshTime: Date.now() });
|
||||
@@ -327,20 +422,28 @@ Page({
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
});
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
this._listRequesting = false;
|
||||
if (!silent) {
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 处理单条订单数据(包含标签、会员判断)
|
||||
processDingdanItem(item) {
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const leixing = this.data.shangpinleixing.find((l) => l.id == item.leixing_id);
|
||||
let fullTupianUrl = '';
|
||||
|
||||
if (item.tupian) {
|
||||
fullTupianUrl = !item.tupian.startsWith('http') ? ossUrl + item.tupian : item.tupian;
|
||||
// 卡片左上角优先用订单类型图,没有再退回默认图(不用商品图/头像顶替类型)
|
||||
if (leixing && leixing.full_tupian_url) {
|
||||
fullTupianUrl = leixing.full_tupian_url;
|
||||
} else if (leixing && leixing.tupian_url) {
|
||||
fullTupianUrl = leixing.tupian_url.startsWith('http')
|
||||
? leixing.tupian_url
|
||||
: ossUrl + leixing.tupian_url;
|
||||
} else {
|
||||
const leixing = this.data.shangpinleixing.find(l => l.id === item.leixing_id);
|
||||
fullTupianUrl = leixing ? leixing.full_tupian_url : '/images/default-order.png';
|
||||
fullTupianUrl = '/images/default-order.png';
|
||||
}
|
||||
|
||||
const isZhiding = item.zhuangtai === 7;
|
||||
@@ -353,25 +456,32 @@ Page({
|
||||
}
|
||||
const zhidingNicheng = item.zhiding_nicheng || '';
|
||||
|
||||
// 🆕 判断用户是否有订单所需的会员(仅当订单要求会员类型且存在huiyuan_id)
|
||||
// 订单要求指定会员时,核对用户是否持有该会员类型
|
||||
let hasRequiredMember = true;
|
||||
if (item.yaoqiuleixing == 1 && item.huiyuan_id) {
|
||||
const userHasIt = this.data.huiyuanList.some(h => h.huiyuanid == item.huiyuan_id);
|
||||
hasRequiredMember = userHasIt;
|
||||
hasRequiredMember = userHasHuiyuan(this.data.huiyuanList, item.huiyuan_id);
|
||||
}
|
||||
|
||||
let shangjiaAvatar = getDefaultAvatarUrl(app);
|
||||
const sjAvatar = (item.sj_avatar || '').trim();
|
||||
if (sjAvatar) {
|
||||
shangjiaAvatar = sjAvatar.startsWith('http') ? sjAvatar : ossUrl + sjAvatar;
|
||||
}
|
||||
|
||||
const tags = normalizeOrderTags(item)
|
||||
|
||||
return {
|
||||
...item,
|
||||
full_tupian_url: fullTupianUrl,
|
||||
leixing_icon_url: fullTupianUrl,
|
||||
shangjia_avatar_full: shangjiaAvatar,
|
||||
isZhiding: isZhiding,
|
||||
isPingtai: item.pingtai == 1,
|
||||
isShangjia: item.pingtai == 2,
|
||||
zhiding_avatar_full: zhidingAvatar,
|
||||
zhiding_nicheng: zhidingNicheng,
|
||||
// 🆕 三层标签(后端返回,直接保留)
|
||||
xuqiu_biaoqian: item.xuqiu_biaoqian || [],
|
||||
dashou_biaoqian: item.dashou_biaoqian || [],
|
||||
shangjia_biaoqian: item.shangjia_biaoqian || [],
|
||||
...tags,
|
||||
shangjia_youzhi: !!item.shangjia_youzhi,
|
||||
// 🆕 是否有查看价格的权限
|
||||
hasRequiredMember: hasRequiredMember,
|
||||
};
|
||||
@@ -409,11 +519,50 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
/** 抢单前考试状态(仅更新状态,不自动跳转考试页) */
|
||||
async checkExamStatus() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/jituan/dashou-exam/status',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
});
|
||||
const body = res?.data;
|
||||
if (body && (body.code === 200 || body.code === 0) && body.data) {
|
||||
const d = body.data;
|
||||
this.setData({
|
||||
examRequired: !!d.exam_required,
|
||||
examPassed: !!d.exam_passed,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('考试状态检查失败', e);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// 抢单按钮(原封不动)
|
||||
async onQiangdanTap(e) {
|
||||
const dingdanItem = e.currentTarget.dataset.item;
|
||||
if (!dingdanItem) return;
|
||||
|
||||
await this.checkExamStatus();
|
||||
|
||||
if (this.data.examRequired && !this.data.examPassed) {
|
||||
wx.showModal({
|
||||
title: '须通过接单考试',
|
||||
content: '抢单前需先通过接单考试,是否现在去考试?',
|
||||
confirmText: '去考试',
|
||||
cancelText: '暂不',
|
||||
success: (r) => {
|
||||
if (r.confirm) {
|
||||
wx.navigateTo({ url: '/pages/dashou-exam/dashou-exam' });
|
||||
}
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验1: 打手身份
|
||||
if (!this.data.dashoustatus || this.data.dashoustatus != 1) {
|
||||
wx.showToast({ title: '请先开启接单员身份', icon: 'none' });
|
||||
@@ -438,7 +587,7 @@ Page({
|
||||
}
|
||||
// 校验5: 会员要求
|
||||
if (dingdanItem.yaoqiuleixing == 1) {
|
||||
const hasHuiyuan = this.data.huiyuanList.some(h => h.huiyuanid == dingdanItem.huiyuan_id);
|
||||
const hasHuiyuan = userHasHuiyuan(this.data.huiyuanList, dingdanItem.huiyuan_id);
|
||||
if (!hasHuiyuan) {
|
||||
wx.showToast({
|
||||
title: '未开通对应会员,无法抢单',
|
||||
@@ -504,6 +653,7 @@ Page({
|
||||
item => item.dingdan_id !== dingdanItem.dingdan_id
|
||||
);
|
||||
that.setData({ dingdanList: newList });
|
||||
that.persistPageCache();
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: qiangdanRes.data.msg || '抢单失败',
|
||||
@@ -535,42 +685,14 @@ Page({
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
const now = Date.now();
|
||||
const lastTime = this.data.lastRefreshTime;
|
||||
const cooldown = this.data.refreshCooldown;
|
||||
|
||||
if (now - lastTime < cooldown) {
|
||||
wx.showToast({
|
||||
title: `操作太快,请${Math.ceil((cooldown - (now - lastTime)) / 1000)}秒后再试`,
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.data.isLoading || this.data.isLoadingMore) return;
|
||||
|
||||
if (this.data.hasMore && !this.data.isLoading && this.data.xuanzhongLeixingId) {
|
||||
this.setData({ page: this.data.page + 1 });
|
||||
this.loadDingdanList(false);
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
const now = Date.now();
|
||||
const lastTime = this.data.lastRefreshTime;
|
||||
const cooldown = this.data.refreshCooldown;
|
||||
|
||||
if (now - lastTime < cooldown) {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
wx.showToast({
|
||||
title: `操作太快,请${Math.ceil((cooldown - (now - lastTime)) / 1000)}秒后再试`,
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
async onPullDownRefresh() {
|
||||
if (this.data.isLoading) {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
return;
|
||||
@@ -578,14 +700,25 @@ Page({
|
||||
|
||||
this.setData({
|
||||
scrollViewRefreshing: true,
|
||||
lastRefreshTime: now,
|
||||
lastRefreshTime: Date.now(),
|
||||
page: 1,
|
||||
hasMore: true
|
||||
hasMore: true,
|
||||
});
|
||||
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
this.loadDingdanList(true);
|
||||
} else {
|
||||
try {
|
||||
const banner = await this.loadGonggaoAndLunbo(true);
|
||||
await this.syncDashouProfileFromServer();
|
||||
await this.loadShangpinLeixing(false, true, false);
|
||||
const leixingId = this.data.xuanzhongLeixingId;
|
||||
if (leixingId) {
|
||||
await this.loadBankuaiBiaoqian();
|
||||
await this.loadDingdanList(true);
|
||||
}
|
||||
this.loadGlobalStatus();
|
||||
this.persistPageCache(banner);
|
||||
} catch (e) {
|
||||
console.error('下拉刷新失败:', e);
|
||||
} finally {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"navigationBarTitleText": "抢单大厅",
|
||||
"navigationBarBackgroundColor": "#f7dc51",
|
||||
"backgroundColor": "#f7dc51",
|
||||
"backgroundColorTop": "#f7dc51",
|
||||
"navigationBarTextStyle": "black",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "dark",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- pages/qiangdan/qiangdan.wxml -->
|
||||
<!-- pages/qiangdan/qiangdan.wxml -->
|
||||
|
||||
<view class="qiangdan-page">
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
|
||||
|
||||
|
||||
<!-- 公告置顶 -->
|
||||
<view class="xym-gonggao-bar" wx:if="{{gonggao}}">
|
||||
<image class="xym-gonggao-ico" src="/images/notice.png" mode="aspectFit"/>
|
||||
<text class="xym-gonggao-txt">{{gonggao}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 商品轮播(只读 globalData,与点单端同源) -->
|
||||
|
||||
<view class="xym-lunbo-container" wx:if="{{lunboList.length > 0}}">
|
||||
@@ -74,7 +80,7 @@
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
<view class="xym-section-gap" wx:if="{{lunboList.length > 0}}"></view>
|
||||
|
||||
<!-- 商品类型选择 -->
|
||||
|
||||
@@ -190,16 +196,6 @@
|
||||
|
||||
<view class="order-con">
|
||||
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-strip myflex light">
|
||||
|
||||
<text class="zhiding-label">指定</text>
|
||||
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="order-row myflex">
|
||||
|
||||
<image class="o-logo" src="{{item.full_tupian_url}}" mode="aspectFill"/>
|
||||
@@ -212,6 +208,20 @@
|
||||
|
||||
</view>
|
||||
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip light">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view class="reward-badge platform-reward" wx:if="{{item.hasRequiredMember}}">
|
||||
|
||||
<text class="reward-yen sm">¥</text>
|
||||
@@ -226,9 +236,9 @@
|
||||
|
||||
</view>
|
||||
|
||||
<view wx:if="{{item.beizhu}}" class="remark-block gold" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
<view class="remark-block gold" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
|
||||
<text class="remark-label">备注:</text>{{item.beizhu}}
|
||||
<text class="remark-label">备注:</text>{{item.beizhu || '暂无'}}
|
||||
|
||||
</view>
|
||||
|
||||
@@ -236,10 +246,10 @@
|
||||
|
||||
<text class="xuqiu-label">需求标签</text>
|
||||
|
||||
<scroll-view class="biaoqian-inline-scroll" scroll-x>
|
||||
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
|
||||
<scroll-view class="biaoqian-inline-scroll" scroll-x enable-flex>
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
</view>
|
||||
@@ -260,6 +270,81 @@
|
||||
|
||||
|
||||
|
||||
<!-- 优质商家订单(金牌卡片) -->
|
||||
<view wx:elif="{{item.isShangjia && item.shangjia_youzhi}}" class="xym-order-item gold-card" data-item="{{item}}">
|
||||
<view class="gold-head flexb">
|
||||
<view class="kehuduan-banner-wrap">
|
||||
<image class="gold-banner" src="https://bintao.xmxym88.com/xcx/bintao/82.png" mode="widthFix"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gold-body-wrap">
|
||||
<view class="order-con gold-inner">
|
||||
<view class="order-row myflex">
|
||||
<image class="o-logo" src="{{item.leixing_icon_url}}" mode="aspectFill"/>
|
||||
<view class="o-col">
|
||||
<view class="o-goods line2" data-type="jieshao" data-content="{{item.jieshao}}" bindtap="onViewDetail">{{item.jieshao || '暂无介绍'}}</view>
|
||||
<view class="tag-row myflex">
|
||||
<text class="tag-pill tag-youzhi">优质商家</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="reward-badge normal-reward gold-reward" wx:if="{{item.hasRequiredMember}}">
|
||||
<text class="reward-yen sm">¥</text>
|
||||
<text class="reward-num dark">{{item.dashou_fencheng || 0}}</text>
|
||||
</view>
|
||||
<view class="reward-badge member-tip" wx:else>
|
||||
<text class="reward-tip">未开通会员</text>
|
||||
</view>
|
||||
<view class="remark-block dark" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
<text class="remark-label dark-t">商家备注:</text>{{item.beizhu || '暂无'}}
|
||||
</view>
|
||||
<view wx:if="{{item.shangjia_biaoqian.length > 0 || item.shangjia_identity_biaoqian.length > 0 || item.xuqiu_biaoqian.length > 0}}" class="xuqiu-tags-row">
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex>
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.shangjia_identity_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="merchant-plain flexb">
|
||||
<view class="merchant-user myflex">
|
||||
<image class="m-avatar" src="{{item.shangjia_avatar_full}}" mode="aspectFill"/>
|
||||
<view class="m-info">
|
||||
<view class="m-name m-name-light line1">{{item.sjnicheng || '未知商家'}}</view>
|
||||
<view class="m-sn m-sn-light">发布于 {{item.creat_time}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="qiangdan flexa btn-bg btn-qiang" data-item="{{item}}" bindtap="onQiangdanTap">抢单</view>
|
||||
</view>
|
||||
<view class="o-foot flexb kehuduan-foot gold-foot-text-only">
|
||||
<text class="o-amt-grey">商家派单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家订单(逍遥梦卡片布局) -->
|
||||
|
||||
<view wx:elif="{{item.isShangjia}}" class="xym-order-item xym-shangjia-order" data-item="{{item}}">
|
||||
@@ -276,19 +361,23 @@
|
||||
|
||||
<view class="order-con">
|
||||
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-strip myflex">
|
||||
|
||||
<text class="zhiding-label">指定</text>
|
||||
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
|
||||
</view>
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view class="merchant-line myflex">
|
||||
|
||||
<image class="m-avatar-sm" src="{{item.full_tupian_url}}" mode="aspectFill"/>
|
||||
<image class="m-avatar-sm" src="{{item.shangjia_avatar_full}}" mode="aspectFill"/>
|
||||
|
||||
<text class="m-name-sm">{{item.sjnicheng || '未知商家'}}</text>
|
||||
|
||||
@@ -318,24 +407,33 @@
|
||||
|
||||
</view>
|
||||
|
||||
<view wx:if="{{item.beizhu}}" class="remark-block dark" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
<view class="remark-block dark" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
|
||||
<text class="remark-label">商家备注:</text>{{item.beizhu}}
|
||||
<text class="remark-label">商家备注:</text>{{item.beizhu || '暂无'}}
|
||||
|
||||
</view>
|
||||
|
||||
<view wx:if="{{item.shangjia_biaoqian.length > 0 || item.xuqiu_biaoqian.length > 0}}" class="xuqiu-tags-row">
|
||||
<view wx:if="{{item.shangjia_biaoqian.length > 0 || item.shangjia_identity_biaoqian.length > 0 || item.xuqiu_biaoqian.length > 0}}" class="xuqiu-tags-row">
|
||||
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x>
|
||||
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex>
|
||||
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x style="margin-top:8rpx">
|
||||
<scroll-view wx:if="{{item.shangjia_identity_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
/* pages/qiangdan/qiangdan.wxss - 高级机甲风格重构版(对称优化) */
|
||||
/* pages/qiangdan/qiangdan.wxss - 逍遥梦抢单页 */
|
||||
@import '../../styles/dashou-xym-theme.wxss';
|
||||
@import '../../styles/dashou-xym-order-card.wxss';
|
||||
|
||||
page {
|
||||
background-color: #f7dc51;
|
||||
}
|
||||
|
||||
.qiangdan-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
@@ -126,6 +132,7 @@
|
||||
flex: 1;
|
||||
height: 0;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.scroll-bottom-spacer {
|
||||
@@ -763,5 +770,3 @@
|
||||
margin-left: auto;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
@import '../../styles/dashou-xym-order-card.wxss';
|
||||
@@ -1,6 +1,7 @@
|
||||
// pages/category/category.js
|
||||
const app = getApp()
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
import { buildClubHeaders } from '../../utils/club-context.js';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
@@ -114,9 +115,7 @@ Page(createPage({
|
||||
wx.request({
|
||||
url: app.globalData.apiBaseUrl + '/shangpin/shangpinhuoqu/',
|
||||
method: 'POST',
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
header: buildClubHeaders({ 'content-type': 'application/json' }),
|
||||
success(res) {
|
||||
if (res.statusCode === 200 && res.data) {
|
||||
const data = res.data
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import { refreshDashouMembership } from '../../utils/dashou-profile.js';
|
||||
import { isApiSuccess, getApiMsg } from '../../utils/api-helper.js';
|
||||
|
||||
const LABELS = ['A', 'B', 'C', 'D', 'E', 'F'];
|
||||
|
||||
function toSlotNum(slot) {
|
||||
const n = Number(slot);
|
||||
return Number.isFinite(n) ? n : slot;
|
||||
}
|
||||
|
||||
function normalizeSlots(slots) {
|
||||
return (slots || []).map(toSlotNum);
|
||||
}
|
||||
|
||||
function slotsEqual(a, b) {
|
||||
const sa = normalizeSlots(a).sort((x, y) => x - y);
|
||||
const sb = normalizeSlots(b).sort((x, y) => x - y);
|
||||
return sa.length === sb.length && sa.every((v, i) => v == sb[i]);
|
||||
}
|
||||
|
||||
function shuffle(arr) {
|
||||
const a = [...arr];
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[a[i], a[j]] = [a[j], a[i]];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
loading: true,
|
||||
alreadyPassed: false,
|
||||
finished: false,
|
||||
blocked: false,
|
||||
blockTitle: '',
|
||||
blockReason: '',
|
||||
needRecharge: false,
|
||||
questions: [],
|
||||
currentIndex: 0,
|
||||
currentQuestion: null,
|
||||
questionType: 1,
|
||||
total: 0,
|
||||
wrongCount: 0,
|
||||
maxWrong: 0,
|
||||
correctCount: 0,
|
||||
selectedSlots: [],
|
||||
showExplanation: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.initExam();
|
||||
},
|
||||
|
||||
showBlocked(title, reason, needRecharge = false) {
|
||||
this.setData({
|
||||
loading: false,
|
||||
blocked: true,
|
||||
blockTitle: title,
|
||||
blockReason: reason,
|
||||
needRecharge: !!needRecharge,
|
||||
});
|
||||
},
|
||||
|
||||
/** 进入页先查 status:不满足条件留在本页提示,不自动退出 */
|
||||
async initExam() {
|
||||
this.setData({ loading: true, blocked: false, needRecharge: false });
|
||||
try {
|
||||
await refreshDashouMembership(getApp());
|
||||
const res = await request({ url: '/jituan/dashou-exam/status', method: 'POST' });
|
||||
const body = res?.data;
|
||||
if (!isApiSuccess(body)) {
|
||||
this.showBlocked('无法开始考试', getApiMsg(body) || '无法获取考试状态,请稍后重试');
|
||||
return;
|
||||
}
|
||||
const d = body.data || {};
|
||||
if (!d.exam_enabled) {
|
||||
this.showBlocked(
|
||||
'未开启考试',
|
||||
'本俱乐部尚未开启接单考试。若后台已开启,请确认配置的是当前小程序对应俱乐部。'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (d.exam_passed) {
|
||||
this.setData({ loading: false, alreadyPassed: true });
|
||||
return;
|
||||
}
|
||||
const cfg = d.config || {};
|
||||
if (cfg.require_member && !d.has_member) {
|
||||
this.showBlocked('须先开通会员', '本俱乐部要求开通会员后才能参加考试', true);
|
||||
return;
|
||||
}
|
||||
if ((d.pool_count || 0) < 1) {
|
||||
this.showBlocked(
|
||||
'暂无可用考题',
|
||||
'考试已开启,但题库没有「已上架」的题目。请在后台确认题目已上架,且每题至少两个选项并标记正确答案。'
|
||||
);
|
||||
return;
|
||||
}
|
||||
await this.loadBundle();
|
||||
} catch (e) {
|
||||
this.showBlocked('网络错误', '请检查网络后下拉重新进入本页');
|
||||
}
|
||||
},
|
||||
|
||||
async loadBundle() {
|
||||
this.setData({ loading: true, finished: false, showExplanation: false, blocked: false });
|
||||
try {
|
||||
const res = await request({ url: '/jituan/dashou-exam/bundle', method: 'POST' });
|
||||
const body = res?.data;
|
||||
if (!isApiSuccess(body)) {
|
||||
this.showBlocked('加载考题失败', getApiMsg(body) || '请稍后重试或联系管理员');
|
||||
return;
|
||||
}
|
||||
const d = body.data || {};
|
||||
if (d.already_passed) {
|
||||
this.setData({ alreadyPassed: true, loading: false });
|
||||
return;
|
||||
}
|
||||
const cfg = d.config || {};
|
||||
const prepared = (d.questions || []).map((q) => this.prepareQuestion(q)).filter((q) => (q.displayOptions || []).length > 0);
|
||||
if (!prepared.length) {
|
||||
this.showBlocked(
|
||||
'题目配置不完整',
|
||||
'已抽到的题目缺少有效选项,请在后台检查题目是否至少两个选项并标记正确答案。'
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.setData({
|
||||
loading: false,
|
||||
questions: prepared,
|
||||
total: prepared.length,
|
||||
currentIndex: 0,
|
||||
wrongCount: 0,
|
||||
correctCount: 0,
|
||||
maxWrong: cfg.max_wrong || 0,
|
||||
currentQuestion: prepared[0],
|
||||
questionType: prepared[0].question_type || 1,
|
||||
selectedSlots: [],
|
||||
});
|
||||
} catch (e) {
|
||||
this.showBlocked('网络错误', '拉取考题失败,请稍后重试');
|
||||
}
|
||||
},
|
||||
|
||||
prepareQuestion(q) {
|
||||
const oss = getApp().globalData.ossImageUrl || '';
|
||||
const displayImages = (q.images || []).map((u) => (u.startsWith('http') ? u : oss + u));
|
||||
const shuffled = shuffle(q.options || []).map((opt, idx) => ({
|
||||
slot: toSlotNum(opt.slot),
|
||||
text: opt.text,
|
||||
label: LABELS[idx] || String(idx + 1),
|
||||
selected: false,
|
||||
}));
|
||||
return {
|
||||
...q,
|
||||
question_type: Number(q.question_type) || 1,
|
||||
displayImages,
|
||||
displayOptions: shuffled,
|
||||
correctSlots: normalizeSlots(q.correct_slots || []),
|
||||
};
|
||||
},
|
||||
|
||||
applySelectedToOptions(selectedSlots) {
|
||||
const q = this.data.currentQuestion;
|
||||
if (!q) return;
|
||||
const set = normalizeSlots(selectedSlots);
|
||||
const displayOptions = (q.displayOptions || []).map((opt) => ({
|
||||
...opt,
|
||||
selected: set.some((s) => s == opt.slot),
|
||||
}));
|
||||
this.setData({
|
||||
selectedSlots: set,
|
||||
currentQuestion: { ...q, displayOptions },
|
||||
});
|
||||
},
|
||||
|
||||
onSelectOption(e) {
|
||||
const slot = toSlotNum(e.currentTarget.dataset.slot);
|
||||
const q = this.data.currentQuestion;
|
||||
if (!q || slot === undefined || slot === null || slot === '') return;
|
||||
if (q.question_type === 2) {
|
||||
const set = [...this.data.selectedSlots];
|
||||
const i = set.findIndex((s) => s == slot);
|
||||
if (i >= 0) set.splice(i, 1);
|
||||
else set.push(slot);
|
||||
if (this.data.showExplanation) {
|
||||
this.setData({ showExplanation: false });
|
||||
}
|
||||
this.applySelectedToOptions(set);
|
||||
return;
|
||||
}
|
||||
this.applySelectedToOptions([slot]);
|
||||
if (this.data.showExplanation) {
|
||||
this.setData({ showExplanation: false });
|
||||
}
|
||||
setTimeout(() => this.gradeAnswer([slot]), 120);
|
||||
},
|
||||
|
||||
confirmMulti() {
|
||||
if (this.data.showExplanation) {
|
||||
this.setData({ showExplanation: false });
|
||||
this.applySelectedToOptions([]);
|
||||
return;
|
||||
}
|
||||
if (!this.data.selectedSlots.length) {
|
||||
wx.showToast({ title: '请选择答案', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.gradeAnswer([...this.data.selectedSlots]);
|
||||
},
|
||||
|
||||
/** 前端本地判分(bundle 已含正确答案与解析) */
|
||||
gradeAnswer(selected) {
|
||||
const q = this.data.currentQuestion;
|
||||
const correct = q.correctSlots || [];
|
||||
const isRight = slotsEqual(selected, correct);
|
||||
|
||||
if (!isRight) {
|
||||
const wrongCount = this.data.wrongCount + 1;
|
||||
this.setData({ wrongCount, showExplanation: true });
|
||||
if (wrongCount > this.data.maxWrong) {
|
||||
wx.showModal({
|
||||
title: '答题失败',
|
||||
content: `${q.explanation || '本题答错了'}。错题过多,将重新开始本轮考试。`,
|
||||
showCancel: false,
|
||||
success: () => this.loadBundle(),
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: '答错了,请查看解析', icon: 'none' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const correctCount = this.data.correctCount + 1;
|
||||
const next = this.data.currentIndex + 1;
|
||||
if (next >= this.data.total) {
|
||||
this.submitPassed(correctCount);
|
||||
return;
|
||||
}
|
||||
const nextQ = this.data.questions[next];
|
||||
this.setData({
|
||||
correctCount,
|
||||
currentIndex: next,
|
||||
currentQuestion: nextQ,
|
||||
questionType: nextQ.question_type,
|
||||
selectedSlots: [],
|
||||
showExplanation: false,
|
||||
});
|
||||
},
|
||||
|
||||
async submitPassed(correctCount) {
|
||||
try {
|
||||
const res = await request({ url: '/jituan/dashou-exam/mark-passed', method: 'POST' });
|
||||
const body = res?.data;
|
||||
if (isApiSuccess(body)) {
|
||||
this.setData({ finished: true, correctCount });
|
||||
} else {
|
||||
wx.showToast({ title: getApiMsg(body) || '提交失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '提交失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
goRecharge() {
|
||||
wx.navigateTo({ url: '/pages/fighter-recharge/fighter-recharge' });
|
||||
},
|
||||
|
||||
retryInit() {
|
||||
this.initExam();
|
||||
},
|
||||
|
||||
goBack() {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
wx.navigateBack();
|
||||
} else {
|
||||
wx.switchTab({ url: '/pages/accept-order/accept-order' });
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "接单考试",
|
||||
"navigationBarBackgroundColor": "#f7dc51",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<view class="exam-page">
|
||||
<view wx:if="{{loading}}" class="center-tip">正在检查考试资格...</view>
|
||||
|
||||
<view wx:elif="{{blocked}}" class="center-box">
|
||||
<text class="tip-title">{{blockTitle}}</text>
|
||||
<text class="tip-desc">{{blockReason}}</text>
|
||||
<view class="btn-row">
|
||||
<view wx:if="{{needRecharge}}" class="btn-primary block" bindtap="goRecharge">去开通会员</view>
|
||||
<view class="btn-outline block" bindtap="retryInit">重试</view>
|
||||
<view class="btn-outline block" bindtap="goBack">返回</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{alreadyPassed}}" class="center-box">
|
||||
<text class="tip-title">已通过考试</text>
|
||||
<text class="tip-desc">无需重复考试,可直接抢单</text>
|
||||
<view class="btn-primary block" bindtap="goBack">返回</view>
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{finished}}" class="center-box">
|
||||
<text class="tip-title">考试通过</text>
|
||||
<text class="tip-desc">恭喜通过,现在可以去抢单了</text>
|
||||
<view class="btn-primary block" bindtap="goBack">去抢单</view>
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{currentQuestion}}" class="exam-body">
|
||||
<view class="exam-card">
|
||||
<view class="progress">第 {{currentIndex + 1}} / {{total}} 题</view>
|
||||
<view class="progress-sub">已错 {{wrongCount}} 题 · 最多可错 {{maxWrong}} 题</view>
|
||||
|
||||
<view class="q-type-tag">{{questionType === 2 ? '多选题' : '单选题'}}</view>
|
||||
<view class="q-stem">{{currentQuestion.stem}}</view>
|
||||
|
||||
<view class="q-images" wx:if="{{currentQuestion.displayImages && currentQuestion.displayImages.length}}">
|
||||
<image wx:for="{{currentQuestion.displayImages}}" wx:key="*this" class="q-img" src="{{item}}" mode="widthFix" />
|
||||
</view>
|
||||
|
||||
<view class="options">
|
||||
<view
|
||||
wx:for="{{currentQuestion.displayOptions}}"
|
||||
wx:key="slot"
|
||||
class="opt-item {{item.selected ? 'opt-active' : ''}}"
|
||||
data-slot="{{item.slot}}"
|
||||
bindtap="onSelectOption"
|
||||
>
|
||||
<view class="opt-radio {{item.selected ? 'opt-radio-on' : ''}}">
|
||||
<view wx:if="{{item.selected}}" class="opt-radio-dot"></view>
|
||||
</view>
|
||||
<view class="opt-body">
|
||||
<text class="opt-label">{{item.label}}</text>
|
||||
<text class="opt-text">{{item.text}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showExplanation}}" id="explain-box" class="explain-box">
|
||||
<text class="explain-title">解析</text>
|
||||
<text class="explain-text">{{currentQuestion.explanation || '暂无解析'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="foot-actions" wx:if="{{questionType === 2}}">
|
||||
<view class="btn-primary foot-btn" bindtap="confirmMulti">{{showExplanation ? '重新作答' : '确认本题'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,228 @@
|
||||
.exam-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #fff8e1 0%, #fff3d6 100%);
|
||||
padding: 24rpx;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.center-tip,
|
||||
.center-box {
|
||||
padding: 80rpx 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tip-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.tip-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
display: block;
|
||||
margin-bottom: 32rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(90deg, #f7dc51, #ffc0a3);
|
||||
padding: 22rpx 48rpx;
|
||||
border-radius: 48rpx;
|
||||
font-weight: 600;
|
||||
font-size: 30rpx;
|
||||
color: #492f00;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-primary.block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
padding: 20rpx 48rpx;
|
||||
border-radius: 48rpx;
|
||||
border: 2rpx solid #ddd;
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.btn-outline.block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.exam-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: calc(100vh - 48rpx);
|
||||
}
|
||||
|
||||
.exam-card {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(73, 47, 0, 0.06);
|
||||
}
|
||||
|
||||
.progress {
|
||||
font-size: 28rpx;
|
||||
color: #492f00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.progress-sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.q-type-tag {
|
||||
display: inline-block;
|
||||
font-size: 22rpx;
|
||||
color: #b8860b;
|
||||
background: #fff8e1;
|
||||
border: 1rpx solid #f7dc51;
|
||||
border-radius: 8rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.q-stem {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 24rpx;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.q-images {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.q-img {
|
||||
width: 100%;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.options {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.opt-item {
|
||||
background: #fafafa;
|
||||
border: 2rpx solid #eee;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.opt-active {
|
||||
border-color: #f7dc51;
|
||||
background: #fffdf0;
|
||||
}
|
||||
|
||||
.opt-radio {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #ccc;
|
||||
flex-shrink: 0;
|
||||
margin-top: 4rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.opt-radio-on {
|
||||
border-color: #f7dc51;
|
||||
background: #fff8e1;
|
||||
}
|
||||
|
||||
.opt-radio-dot {
|
||||
width: 18rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 50%;
|
||||
background: #f7dc51;
|
||||
}
|
||||
|
||||
.opt-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.opt-label {
|
||||
font-weight: 700;
|
||||
color: #492f00;
|
||||
min-width: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.opt-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.explain-box {
|
||||
background: #fffbf0;
|
||||
border-left: 6rpx solid #e6a23c;
|
||||
padding: 20rpx 24rpx;
|
||||
margin-top: 24rpx;
|
||||
border-radius: 0 12rpx 12rpx 0;
|
||||
}
|
||||
|
||||
.explain-title {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.explain-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.foot-actions {
|
||||
margin-top: 24rpx;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.foot-btn {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
min-height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1.2;
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
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>
|
||||
|
||||
+683
-683
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -49,7 +49,14 @@ Page(createPage({
|
||||
|
||||
onLoad() {
|
||||
wx.setNavigationBarTitle({ title: '我的接单' });
|
||||
this.loadShangpinLeixing();
|
||||
this.loadShangpinLeixing().then(() => {
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
return this.loadCurrentStatusOrders(true);
|
||||
}
|
||||
}).finally(() => {
|
||||
this._ordersSessionReady = true;
|
||||
this._skipShowRefresh = true;
|
||||
});
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
@@ -59,8 +66,15 @@ Page(createPage({
|
||||
reconnectForRole('dashou');
|
||||
if (app.startImWhenReady) app.startImWhenReady();
|
||||
}
|
||||
if (this.data.currentStatusKey && this.data.xuanzhongLeixingId) {
|
||||
this.loadCurrentStatusOrders(true);
|
||||
if (this._skipShowRefresh) {
|
||||
this._skipShowRefresh = false;
|
||||
return;
|
||||
}
|
||||
if (this._ordersSessionReady && this.data.xuanzhongLeixingId && !this._silentRefreshRunning) {
|
||||
this._silentRefreshRunning = true;
|
||||
this.loadCurrentStatusOrders(true, true).finally(() => {
|
||||
this._silentRefreshRunning = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -141,17 +155,21 @@ Page(createPage({
|
||||
},
|
||||
|
||||
// 加载订单(核心)
|
||||
async loadCurrentStatusOrders(isRefresh = false) {
|
||||
async loadCurrentStatusOrders(isRefresh = false, silent = false) {
|
||||
const key = this.data.currentStatusKey;
|
||||
const tabData = this.data.dsDingdanShuju[key];
|
||||
if (tabData.isLoading) return;
|
||||
if (tabData.isLoading && !silent) return;
|
||||
if (silent && this._listRequesting) return;
|
||||
const page = isRefresh ? 1 : tabData.page;
|
||||
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${key}.isLoading`]: true,
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh
|
||||
});
|
||||
if (!silent) {
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${key}.isLoading`]: true,
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh,
|
||||
});
|
||||
}
|
||||
this._listRequesting = true;
|
||||
|
||||
try {
|
||||
const apiUrl = this.data.orderType === 'peihu'
|
||||
@@ -208,14 +226,20 @@ Page(createPage({
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载订单失败', err);
|
||||
wx.showToast({ title: err.message || '加载失败', icon: 'none' });
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${key}.isLoading`]: false,
|
||||
isLoading: false,
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
});
|
||||
this.refreshCurrentListView();
|
||||
if (!silent) {
|
||||
wx.showToast({ title: err.message || '加载失败', icon: 'none' });
|
||||
}
|
||||
if (!silent) {
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${key}.isLoading`]: false,
|
||||
isLoading: false,
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
});
|
||||
this.refreshCurrentListView();
|
||||
}
|
||||
} finally {
|
||||
this._listRequesting = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"tab-bar": "/tab-bar/index"
|
||||
},
|
||||
"navigationBarTitleText": "我的接单",
|
||||
"enablePullDownRefresh": false,
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
/**
|
||||
* 排行榜 POST /yonghu/phbhqsj
|
||||
* 参数: shenfen(dashou|guanshi|zuzhang|shangjia), riqi(今日|本周|本月|总榜|昨日|上周|上月)
|
||||
* 排行榜
|
||||
* 默认 POST /yonghu/phbhqsj
|
||||
* 星之界(xzj) POST /yonghu/xzjphbhqsj(邀请人数排序等专用规则)
|
||||
*/
|
||||
import request from '../../utils/request.js'
|
||||
import { resolveAvatarUrl, getDefaultAvatarUrl } from '../../utils/avatar.js'
|
||||
import { CLUB_ID } from '../../config/club-config.js'
|
||||
|
||||
const IS_XZJ = CLUB_ID === 'xzj'
|
||||
const RANK_API = IS_XZJ ? '/yonghu/xzjphbhqsj' : '/yonghu/phbhqsj'
|
||||
|
||||
const ROLE_LIST = [
|
||||
{ key: 'dashou', label: '接单员' },
|
||||
@@ -27,6 +32,7 @@ const ROLE_META = {
|
||||
title: '接单员排行榜',
|
||||
sortField: 'chengjiao_zonge',
|
||||
sortLabel: '分红总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiedan_zongliang', label: '接单量', type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '接单额', type: 'money' },
|
||||
@@ -37,6 +43,7 @@ const ROLE_META = {
|
||||
title: '管事排行榜',
|
||||
sortField: 'shouru_zonge',
|
||||
sortLabel: '收入总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'yaoqing_dashou_shu', label: '邀请', type: 'int' },
|
||||
{ field: 'chongzhi_dashou_shu', label: '充值', type: 'int' },
|
||||
@@ -46,6 +53,7 @@ const ROLE_META = {
|
||||
title: '组长排行榜',
|
||||
sortField: 'shouru_zonge',
|
||||
sortLabel: '收入总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'yaoqing_guanshi_shu', label: '邀请', type: 'int' },
|
||||
{ field: 'fenyong_jine', label: '分佣', type: 'money' },
|
||||
@@ -55,6 +63,7 @@ const ROLE_META = {
|
||||
title: '商家排行榜',
|
||||
sortField: 'jiesuan_jine',
|
||||
sortLabel: '成交总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiesuan_dingdan_shu', label: '成交量', type: 'int' },
|
||||
{ field: 'paifa_dingdan_shu', label: '派单量', type: 'int' },
|
||||
@@ -63,6 +72,55 @@ const ROLE_META = {
|
||||
},
|
||||
}
|
||||
|
||||
/** 星之界专用展示与排序字段 */
|
||||
const XZJ_ROLE_META = {
|
||||
dashou: {
|
||||
title: '接单员排行榜',
|
||||
sortField: 'chengjiao_zonge',
|
||||
sortLabel: '成交金额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiedan_zongliang', label: '接单量', type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '接单额', type: 'money' },
|
||||
{ field: 'chengjiao_zongliang', label: '成交量', type: 'int' },
|
||||
],
|
||||
},
|
||||
guanshi: {
|
||||
title: '管事排行榜',
|
||||
sortField: 'yaoqing_dashou_shu',
|
||||
sortLabel: '邀请人数',
|
||||
mainType: 'int',
|
||||
metrics: [
|
||||
{ field: 'wuxiao_yaoqing_dashou_shu', label: '无效邀请', type: 'int' },
|
||||
{ field: 'chongzhi_dashou_shu', label: '有效人数', type: 'int' },
|
||||
{ field: 'shouru_zonge', label: '收入金额', type: 'money' },
|
||||
],
|
||||
},
|
||||
zuzhang: {
|
||||
title: '组长排行榜',
|
||||
sortField: 'yaoqing_guanshi_shu',
|
||||
sortLabel: '邀请人数',
|
||||
mainType: 'int',
|
||||
metrics: [
|
||||
{ field: 'wuxiao_yaoqing_guanshi_shu', label: '无效邀请', type: 'int' },
|
||||
{ field: 'youxiao_guanshi_shu', label: '有效人数', type: 'int' },
|
||||
{ field: 'shouru_zonge', label: '收入金额', type: 'money' },
|
||||
],
|
||||
},
|
||||
shangjia: {
|
||||
title: '商家排行榜',
|
||||
sortField: 'paifa_jine',
|
||||
sortLabel: '派单流水',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'paifa_dingdan_shu', label: '派单量', type: 'int' },
|
||||
{ field: 'paifa_jine', label: '派单流水', type: 'money' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const ACTIVE_ROLE_META = IS_XZJ ? XZJ_ROLE_META : ROLE_META
|
||||
|
||||
function isInvalidAvatarPath(path) {
|
||||
if (path === null || path === undefined) return true
|
||||
if (typeof path !== 'string') return true
|
||||
@@ -99,7 +157,7 @@ Page({
|
||||
|
||||
onLoad(options) {
|
||||
const type = options.type || options.rankType || 'dashou'
|
||||
const validRole = ROLE_META[type] ? type : 'dashou'
|
||||
const validRole = ACTIVE_ROLE_META[type] ? type : 'dashou'
|
||||
this.initPage()
|
||||
this.applyRole(validRole, false)
|
||||
this.fetchRankList()
|
||||
@@ -162,7 +220,7 @@ Page({
|
||||
},
|
||||
|
||||
applyRole(role, reload = true) {
|
||||
const meta = ROLE_META[role]
|
||||
const meta = ACTIVE_ROLE_META[role]
|
||||
wx.setNavigationBarTitle({ title: meta.title })
|
||||
this.setData({ currentRole: role, roleMeta: meta, sortLabel: meta.sortLabel })
|
||||
if (reload) this.fetchRankList()
|
||||
@@ -196,7 +254,7 @@ Page({
|
||||
},
|
||||
|
||||
normalizeItem(raw, index, role) {
|
||||
const meta = ROLE_META[role]
|
||||
const meta = ACTIVE_ROLE_META[role]
|
||||
const app = getApp()
|
||||
const uid = raw.yonghuid || raw.uid || ''
|
||||
const nicheng = raw.nicheng || raw.nick || '用户'
|
||||
@@ -209,12 +267,19 @@ Page({
|
||||
value: m.type === 'money' ? this.formatMoney(v) : this.formatInt(v),
|
||||
}
|
||||
})
|
||||
const sortRaw = raw[meta.sortField]
|
||||
const mainType = meta.mainType || 'money'
|
||||
const mainValue = mainType === 'money'
|
||||
? this.formatMoney(sortRaw)
|
||||
: this.formatInt(sortRaw)
|
||||
return {
|
||||
mingci: raw.mingci || index + 1,
|
||||
uid,
|
||||
nicheng,
|
||||
avatar,
|
||||
mainValue: this.formatMoney(raw[meta.sortField]),
|
||||
mainType,
|
||||
mainPrefix: mainType === 'money' ? '¥' : '',
|
||||
mainValue,
|
||||
mainLabel: meta.sortLabel,
|
||||
metrics,
|
||||
}
|
||||
@@ -224,7 +289,7 @@ Page({
|
||||
this.setData({ isLoading: true, isEmpty: false })
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/phbhqsj',
|
||||
url: RANK_API,
|
||||
method: 'POST',
|
||||
data: { shenfen: this.data.currentRole, riqi: this.data.currentDate },
|
||||
})
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<text class="p-name">{{topThree[1].nicheng}}</text>
|
||||
<text class="p-uid">ID {{topThree[1].uid}}</text>
|
||||
<view class="p-amount">
|
||||
<text class="p-money">¥{{topThree[1].mainValue}}</text>
|
||||
<text class="p-money">{{topThree[1].mainPrefix}}{{topThree[1].mainValue}}</text>
|
||||
<text class="p-label">{{topThree[1].mainLabel}}</text>
|
||||
</view>
|
||||
<view class="p-metrics">
|
||||
@@ -70,7 +70,7 @@
|
||||
<text class="p-name p-name-first">{{topThree[0].nicheng}}</text>
|
||||
<text class="p-uid">ID {{topThree[0].uid}}</text>
|
||||
<view class="p-amount">
|
||||
<text class="p-money p-money-first">¥{{topThree[0].mainValue}}</text>
|
||||
<text class="p-money p-money-first">{{topThree[0].mainPrefix}}{{topThree[0].mainValue}}</text>
|
||||
<text class="p-label">{{topThree[0].mainLabel}}</text>
|
||||
</view>
|
||||
<view class="p-metrics">
|
||||
@@ -88,7 +88,7 @@
|
||||
<text class="p-name">{{topThree[2].nicheng}}</text>
|
||||
<text class="p-uid">ID {{topThree[2].uid}}</text>
|
||||
<view class="p-amount">
|
||||
<text class="p-money">¥{{topThree[2].mainValue}}</text>
|
||||
<text class="p-money">{{topThree[2].mainPrefix}}{{topThree[2].mainValue}}</text>
|
||||
<text class="p-label">{{topThree[2].mainLabel}}</text>
|
||||
</view>
|
||||
<view class="p-metrics">
|
||||
@@ -120,7 +120,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="lc-score">
|
||||
<text class="lc-money">¥{{item.mainValue}}</text>
|
||||
<text class="lc-money">{{item.mainPrefix}}{{item.mainValue}}</text>
|
||||
<text class="lc-label">{{item.mainLabel}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -47,6 +47,12 @@
|
||||
<view class="vip-badge" wx:if="{{item.isBought}}">
|
||||
<text class="badge-text">已拥有</text>
|
||||
</view>
|
||||
<view class="vip-badge trial-badge" wx:if="{{item.can_buy_trial}}">
|
||||
<text class="badge-text">可购体验</text>
|
||||
</view>
|
||||
<view class="vip-badge trial-off-badge" wx:elif="{{item.trial_enabled}}">
|
||||
<text class="badge-text">体验已用/不可购</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-title">
|
||||
<text class="title-text">{{item.mingzi}}</text>
|
||||
@@ -56,7 +62,11 @@
|
||||
<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-trial-price" wx:if="{{item.trial_enabled || item.can_buy_trial}}">
|
||||
<text class="trial-price-label">体验</text>
|
||||
<text class="trial-price-value">¥{{item.trial_price}}/{{item.trial_days}}天</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-features">
|
||||
@@ -107,20 +117,27 @@
|
||||
<!-- 🔥 将“机甲能源”改为“价格” -->
|
||||
<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>
|
||||
<text class="buy-desc trial-warn" wx:elif="{{!currentHuiyuan.trial_enabled}}">
|
||||
本俱乐部尚未为该会员开启体验版,请联系管理员在后台配置
|
||||
</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 +145,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>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* pages/dashou-chongzhi/index.wxss - 完整保留原有样式,仅删除导航栏相关,调整间距和字体 */
|
||||
/* pages/dashou-chongzhi/index.wxss - 逍遥梦主题覆盖 + 机甲基础样式 */
|
||||
@import '../../styles/dashou-xym-recharge-theme.wxss';
|
||||
|
||||
/* ==================== 基础样式 ==================== */
|
||||
.page-container {
|
||||
@@ -676,6 +677,32 @@
|
||||
box-shadow: 0 0 15rpx rgba(255, 107, 157, 0.4);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.vip-badge.trial-badge {
|
||||
top: 60rpx;
|
||||
background: linear-gradient(135deg, #ffb347, #ff7043);
|
||||
}
|
||||
|
||||
.vip-badge.trial-off-badge {
|
||||
top: 60rpx;
|
||||
background: rgba(120, 120, 120, 0.85);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.vip-trial-price {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #ffb347;
|
||||
}
|
||||
|
||||
.trial-price-label {
|
||||
margin-right: 8rpx;
|
||||
color: #a0c8ff;
|
||||
}
|
||||
|
||||
.trial-price-value {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.vip-title {
|
||||
margin-bottom: 25rpx;
|
||||
@@ -855,11 +882,35 @@
|
||||
/* 购买区域 */
|
||||
.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;
|
||||
}
|
||||
|
||||
.trial-warn {
|
||||
margin-top: 6rpx;
|
||||
color: #ff8a8a;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.buy-info {
|
||||
display: flex;
|
||||
@@ -928,6 +979,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);
|
||||
}
|
||||
@@ -1702,6 +1758,4 @@
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
@import '../../styles/dashou-xym-recharge-theme.wxss';
|
||||
}
|
||||
+168
-38
@@ -2,6 +2,18 @@
|
||||
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'
|
||||
import {
|
||||
ICON_KEYS,
|
||||
resolveMiniappIcon,
|
||||
resolveConfiguredIcon,
|
||||
refreshPindaoConfig,
|
||||
getPindaoConfig,
|
||||
resolvePindaoImages,
|
||||
warmupPindaoConfig,
|
||||
PINDAO_ICON_FALLBACK,
|
||||
} from '../../utils/miniapp-icons.js'
|
||||
|
||||
const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC'
|
||||
|
||||
@@ -41,6 +53,7 @@ Page(createPage({
|
||||
zuzhangInviterCache: null,
|
||||
chenghaoList: [],
|
||||
guanshiChenghaoList: [],
|
||||
identityTagList: [],
|
||||
gszhstatus: '',
|
||||
yaoqingzongshu: 0,
|
||||
fenyongzonge: '0.00',
|
||||
@@ -65,13 +78,25 @@ Page(createPage({
|
||||
|
||||
statusBar: 20,
|
||||
navBar: 44,
|
||||
pindaoVisible: false,
|
||||
pindaoTitle: '频道',
|
||||
pindaoChannelNo: '',
|
||||
examEnabled: false,
|
||||
examPassed: false,
|
||||
pindaoImages: [],
|
||||
|
||||
scrollViewRefreshing: false,
|
||||
showRechargeBanners: false,
|
||||
showPindaoEntry: true,
|
||||
},
|
||||
|
||||
_buildImgUrls(ossImageUrl) {
|
||||
const app = getApp();
|
||||
const dsBase = ossImageUrl + 'beijing/dashouduan/';
|
||||
const iconBase = ossImageUrl + 'beijing/dashouduan/dashoutubiaobeijing/';
|
||||
const gsBase = ossImageUrl + 'beijing/guanshiduan/';
|
||||
const khBase = ossImageUrl + 'beijing/kaohe/';
|
||||
const icon = (key, fallback) => resolveMiniappIcon(app, key, fallback);
|
||||
return {
|
||||
pageBg: dsBase + 'page-bg.jpg',
|
||||
userCardBg: dsBase + 'user-card-bg.png',
|
||||
@@ -83,7 +108,7 @@ Page(createPage({
|
||||
listBg: dsBase + 'list-card-bg.png',
|
||||
topBg: iconBase + 'top-bg.jpg',
|
||||
cardBg: iconBase + 'card-bg.jpg',
|
||||
iconRefresh: gsBase + 'icon-refresh.png',
|
||||
iconRefresh: icon(ICON_KEYS.ICON_REFRESH, gsBase + 'icon-refresh.png'),
|
||||
iconCopy: gsBase + 'icon-copy.png',
|
||||
iconVip: dsBase + 'icon-vip.png',
|
||||
iconArrowLight: dsBase + 'icon-arrow-light.png',
|
||||
@@ -118,6 +143,7 @@ Page(createPage({
|
||||
iconSwitch: '/images/_exit.png',
|
||||
iconKefu: ossImageUrl + 'beijing/tubiao/grzx_kefu.jpg',
|
||||
iconKuaishou: ossImageUrl + 'beijing/tubiao/grzx_guanzhualong.jpg',
|
||||
iconPindao: icon(ICON_KEYS.MINE_PINDAO, ossImageUrl + PINDAO_ICON_FALLBACK),
|
||||
iconKaoheDafen: khBase + 'daofen.png',
|
||||
iconKaoheJilu: khBase + 'jilu.png',
|
||||
iconKaoheZhongxin: khBase + 'zhongxin.png',
|
||||
@@ -128,8 +154,8 @@ Page(createPage({
|
||||
kefuBannerBg: dsBase + 'kefu-banner-bg.png',
|
||||
totalAssetBg: dsBase + 'total-asset-bg.png',
|
||||
authPanelBg: dsBase + 'auth-panel-bg.png',
|
||||
bannerVip: 'https://bintao-1308403501.cos.ap-guangzhou.myqcloud.com/uploads/images/20260512123427531240452.png',
|
||||
bannerDeposit: 'https://bintao-1308403501.cos.ap-guangzhou.myqcloud.com/uploads/images/202605121234283091d3980.png',
|
||||
bannerVip: resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_RECHARGE_MEMBER),
|
||||
bannerDeposit: resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_RECHARGE_DEPOSIT),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -177,9 +203,28 @@ Page(createPage({
|
||||
}
|
||||
}
|
||||
this.checkColdStartPopup('dashouduan');
|
||||
this.syncConfiguredAssets();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
syncConfiguredAssets() {
|
||||
const app = getApp();
|
||||
const bannerVip = resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_RECHARGE_MEMBER);
|
||||
const bannerDeposit = resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_RECHARGE_DEPOSIT);
|
||||
const imgUrls = { ...this.data.imgUrls, bannerVip, bannerDeposit };
|
||||
this.setData({
|
||||
imgUrls,
|
||||
showRechargeBanners: !!(bannerVip || bannerDeposit),
|
||||
showPindaoEntry: this.data.isDashou || this.data.isGuanshi || this.data.isZuzhang || this.data.isKaoheguan,
|
||||
});
|
||||
},
|
||||
|
||||
async onShow() {
|
||||
if (!app.globalData._dashouPhoneChecked) {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
|
||||
if (!phoneOk) return;
|
||||
app.globalData._dashouPhoneChecked = true;
|
||||
}
|
||||
|
||||
migrateLegacyCenterRole(getApp());
|
||||
lockPrimaryRole('dashou', getApp());
|
||||
wx.setStorageSync('isJinpai', 0);
|
||||
@@ -190,16 +235,17 @@ Page(createPage({
|
||||
this.setData({ inviterCache, zuzhangInviterCache });
|
||||
|
||||
this.registerNotificationComponent();
|
||||
warmupPindaoConfig(app);
|
||||
this.checkRoleStatuses();
|
||||
this.syncConfiguredAssets();
|
||||
if (isCenterPageActive(this, 'isDashou', 'dashoustatus')) {
|
||||
if (!this.data.isDashou) this.setData({ isDashou: true });
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
setTimeout(() => this.refreshAllInfo(false), 300);
|
||||
this.loadExamStatus();
|
||||
} else if (isRoleStatusActive(wx.getStorageSync('guanshistatus')) ||
|
||||
isRoleStatusActive(wx.getStorageSync('zuzhangstatus')) ||
|
||||
isRoleStatusActive(wx.getStorageSync('kaoheguanstatus'))) {
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
setTimeout(() => this.refreshAllInfo(false), 300);
|
||||
}
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
@@ -277,12 +323,17 @@ 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: '去认证' });
|
||||
}
|
||||
if (!d.isZuzhang) {
|
||||
list.push({ type: 'zuzhang', name: '组长', icon: img.iconZuzhangAuth, tag: '去认证' });
|
||||
}
|
||||
// 组长由后台添加,前端不再展示「组长认证」入口
|
||||
if (!d.isDashou) {
|
||||
list.push({ type: 'dashou', name: '接单员', icon: img.iconDashouAuth, tag: '去认证' });
|
||||
}
|
||||
@@ -293,35 +344,37 @@ Page(createPage({
|
||||
},
|
||||
|
||||
async refreshAllInfo(showToast = true) {
|
||||
this.throttledRefresh(async () => {
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
this.checkRoleStatuses();
|
||||
const results = await Promise.allSettled([
|
||||
this._fetchDashouInfoSilent(),
|
||||
this.fetchChenghaoList(),
|
||||
this._fetchGuanshiInfoSilent(),
|
||||
this.fetchGuanshiChenghaoList(),
|
||||
this._fetchZuzhangInfoSilent(),
|
||||
this._fetchKaoheguanInfoSilent(),
|
||||
]);
|
||||
this.checkRoleStatuses();
|
||||
const dashouOk = results[0].status === 'fulfilled';
|
||||
if (showToast) {
|
||||
wx.showToast({
|
||||
title: dashouOk ? '刷新成功' : '刷新失败',
|
||||
icon: dashouOk ? 'success' : 'none',
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (showToast) {
|
||||
wx.showToast({ title: '刷新失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this.setData({ isLoading: false });
|
||||
if (this._profileRefreshing) return;
|
||||
this._profileRefreshing = true;
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
this.checkRoleStatuses();
|
||||
const results = await Promise.allSettled([
|
||||
this._fetchDashouInfoSilent(),
|
||||
this.fetchChenghaoList(),
|
||||
this._fetchGuanshiInfoSilent(),
|
||||
this.fetchGuanshiChenghaoList(),
|
||||
this._fetchZuzhangInfoSilent(),
|
||||
this._fetchKaoheguanInfoSilent(),
|
||||
this.loadIdentityTags(),
|
||||
]);
|
||||
this.checkRoleStatuses();
|
||||
const dashouOk = results[0].status === 'fulfilled';
|
||||
if (showToast) {
|
||||
wx.showToast({
|
||||
title: dashouOk ? '刷新成功' : '刷新失败',
|
||||
icon: dashouOk ? 'success' : 'none',
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
}, 3000);
|
||||
} catch (e) {
|
||||
if (showToast) {
|
||||
wx.showToast({ title: '刷新失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this._profileRefreshing = false;
|
||||
this.setData({ isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
refreshDashouInfo() {
|
||||
@@ -332,6 +385,34 @@ Page(createPage({
|
||||
this.refreshAllInfo(true);
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.setData({ scrollViewRefreshing: true });
|
||||
Promise.all([
|
||||
this.refreshAllInfo(false),
|
||||
this.loadExamStatus(),
|
||||
]).finally(() => {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
async loadIdentityTags() {
|
||||
try {
|
||||
const res = await request({ url: '/jituan/identity-tag/my-tags', method: 'POST' });
|
||||
if (res && res.data && (res.data.code === 0 || res.data.code === 200)) {
|
||||
const d = res.data.data || {};
|
||||
const merged = [
|
||||
...(d.dashou || []),
|
||||
...(d.zuzhang || []),
|
||||
...(d.guanshi || []),
|
||||
];
|
||||
this.setData({ identityTagList: merged });
|
||||
}
|
||||
} catch (e) {
|
||||
/* 静默 */
|
||||
}
|
||||
},
|
||||
|
||||
async _fetchDashouInfoSilent() {
|
||||
const res = await request({ url: '/yonghu/dashouxinxi', method: 'POST' });
|
||||
if (res && res.data.code == 200) {
|
||||
@@ -1015,6 +1096,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;
|
||||
@@ -1038,6 +1127,21 @@ Page(createPage({
|
||||
wx.navigateTo({ url: '/pages/verify/verify?type=shangjia' });
|
||||
},
|
||||
goToReceiveOrder() { wx.navigateTo({ url: '/pages/accept-order/accept-order' }) },
|
||||
goToDashouExam() { wx.navigateTo({ url: '/pages/dashou-exam/dashou-exam' }) },
|
||||
|
||||
async loadExamStatus() {
|
||||
try {
|
||||
const res = await request({ url: '/jituan/dashou-exam/status', method: 'POST' });
|
||||
const body = res?.data;
|
||||
if (body && (body.code === 200 || body.code === 0) && body.data) {
|
||||
const d = body.data;
|
||||
this.setData({
|
||||
examEnabled: !!d.exam_enabled,
|
||||
examPassed: !!d.exam_passed,
|
||||
});
|
||||
}
|
||||
} catch (e) { /* 静默 */ }
|
||||
},
|
||||
goToMyOrders() { wx.navigateTo({ url: '/pages/fighter-orders/fighter-orders' }) },
|
||||
goToMyPunishment() { wx.navigateTo({ url: '/pages/penalty/penalty/penalty' }) },
|
||||
goToRecharge() { wx.navigateTo({ url: '/pages/fighter-recharge/fighter-recharge' }) },
|
||||
@@ -1045,7 +1149,33 @@ Page(createPage({
|
||||
const type = e?.currentTarget?.dataset?.type || 'dashou';
|
||||
wx.navigateTo({ url: `/pages/fighter-rank/fighter-rank?type=${type}` });
|
||||
},
|
||||
goToGuanzhuKs() { wx.navigateTo({ url: '/pages/manager-assign/manager-assign' }) },
|
||||
goToGuanzhuKs() {
|
||||
wx.navigateTo({ url: '/pages/manager-assign/manager-assign' });
|
||||
},
|
||||
|
||||
openPindaoModal() {
|
||||
const app = getApp();
|
||||
const cfg = getPindaoConfig(app);
|
||||
const images = resolvePindaoImages(app);
|
||||
this.setData({
|
||||
pindaoVisible: true,
|
||||
pindaoTitle: cfg.title || '频道',
|
||||
pindaoChannelNo: cfg.channel_no || '',
|
||||
pindaoImages: images,
|
||||
});
|
||||
refreshPindaoConfig(app).then(({ cfg: latest, images: latestImages }) => {
|
||||
if (!this.data.pindaoVisible) return;
|
||||
this.setData({
|
||||
pindaoTitle: latest.title || '频道',
|
||||
pindaoChannelNo: latest.channel_no || '',
|
||||
pindaoImages: latestImages,
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
closePindaoModal() {
|
||||
this.setData({ pindaoVisible: false });
|
||||
},
|
||||
goToKaoheDafen() { wx.navigateTo({ url: '/pages/assess-score/assess-score' }) },
|
||||
goToKaoheJilu() { wx.navigateTo({ url: '/pages/assess-log/assess-log' }) },
|
||||
goToKaoheZhongxin() { wx.navigateTo({ url: '/pages/assess-center/assess-center' }) },
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag",
|
||||
"tab-bar": "/tab-bar/index"
|
||||
"tab-bar": "/tab-bar/index",
|
||||
"pindao-modal": "/components/pindao-modal/pindao-modal"
|
||||
},
|
||||
"navigationStyle": "custom",
|
||||
"backgroundColor": "#fff8e1",
|
||||
|
||||
+47
-14
@@ -26,14 +26,23 @@
|
||||
<view class="user-page">
|
||||
<view class="page-header">
|
||||
<view class="status-bar" style="height:{{statusBar}}px"></view>
|
||||
<view class="nav-bar" style="height:{{navBar - statusBar}}px"><text class="nav-title">我的</text></view>
|
||||
<view class="nav-bar" style="height:{{navBar - statusBar}}px">
|
||||
<text class="nav-title">我的</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="refresh-float" bindtap="onTapRefresh">
|
||||
<image class="refresh-float-ico" src="{{imgUrls.iconRefresh}}" mode="aspectFit"/>
|
||||
</view>
|
||||
|
||||
<scroll-view class="content" scroll-y enhanced show-scrollbar="{{false}}">
|
||||
<scroll-view
|
||||
class="content"
|
||||
scroll-y
|
||||
enhanced
|
||||
show-scrollbar="{{false}}"
|
||||
refresher-enabled="{{true}}"
|
||||
refresher-threshold="80"
|
||||
refresher-default-style="black"
|
||||
refresher-background="#fff8e1"
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
>
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info flexb">
|
||||
<view class="flex">
|
||||
@@ -55,11 +64,14 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<image class="setting-ico" src="{{imgUrls.iconEdit}}" mode="aspectFit" bindtap="goToModifyInfo"/>
|
||||
<view class="user-header-actions">
|
||||
<image class="header-action-ico" src="{{imgUrls.iconRefresh}}" mode="aspectFit" bindtap="onTapRefresh"/>
|
||||
<image class="setting-ico" src="{{imgUrls.iconEdit}}" mode="aspectFit" bindtap="goToModifyInfo"/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 称号 -->
|
||||
<view class="badge-zone" wx:if="{{chenghaoList.length > 0 || (isGuanshi && guanshiChenghaoList.length > 0)}}">
|
||||
<view class="badge-zone" wx:if="{{chenghaoList.length > 0 || (isGuanshi && guanshiChenghaoList.length > 0) || identityTagList.length > 0}}">
|
||||
<scroll-view scroll-x class="badge-scroll" wx:if="{{chenghaoList.length > 0}}">
|
||||
<view class="badge-tag-wrap" wx:for="{{chenghaoList}}" wx:key="index">
|
||||
<chenghao-tag mingcheng="{{item.mingcheng}}" texiaoJson="{{item.texiao_json}}"/>
|
||||
@@ -70,6 +82,11 @@
|
||||
<chenghao-tag mingcheng="{{item.mingcheng}}" texiaoJson="{{item.texiao_json}}"/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view scroll-x class="badge-scroll identity-tag-scroll" wx:if="{{identityTagList.length > 0}}">
|
||||
<view class="badge-tag-wrap" wx:for="{{identityTagList}}" wx:key="id">
|
||||
<chenghao-tag mingcheng="{{item.mingcheng}}" texiaoJson="{{item.texiao_json}}"/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 佣金/资产卡(逍遥梦 wallet-card) -->
|
||||
@@ -85,10 +102,14 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员充值 + 保证金(均跳转同一充值页) -->
|
||||
<view class="banner-row flex">
|
||||
<image class="banner-img" src="{{imgUrls.bannerVip}}" mode="widthFix" bindtap="goToRecharge"/>
|
||||
<image class="banner-img" src="{{imgUrls.bannerDeposit}}" mode="widthFix" bindtap="goToRecharge"/>
|
||||
<!-- 会员充值 + 保证金(无后台图时显示文字按钮) -->
|
||||
<view class="banner-row flex" wx:if="{{showRechargeBanners}}">
|
||||
<image wx:if="{{imgUrls.bannerVip}}" class="banner-img" src="{{imgUrls.bannerVip}}" mode="widthFix" bindtap="goToRecharge"/>
|
||||
<image wx:if="{{imgUrls.bannerDeposit}}" class="banner-img" src="{{imgUrls.bannerDeposit}}" mode="widthFix" bindtap="goToRecharge"/>
|
||||
</view>
|
||||
<view class="recharge-text-row flex" wx:else>
|
||||
<view class="recharge-text-btn" catchtap="goToRecharge">充值会员</view>
|
||||
<view class="recharge-text-btn" catchtap="goToRecharge">充值保证金</view>
|
||||
</view>
|
||||
|
||||
<!-- 资产统计(逍遥梦 freeze-row) -->
|
||||
@@ -123,7 +144,7 @@
|
||||
<!-- 接单服务(对应逍遥梦「我的订单」四宫格) -->
|
||||
<view class="panel shadow">
|
||||
<view class="panel-title flexb"><text class="lg">接单服务</text></view>
|
||||
<view class="order-nav flexa">
|
||||
<view class="order-nav">
|
||||
<view class="nav-item flexmc" bindtap="goToReceiveOrder">
|
||||
<image class="nav-icon" src="{{imgUrls.iconGame}}" mode="aspectFit"/><text class="sm">抢单大厅</text>
|
||||
</view>
|
||||
@@ -136,6 +157,11 @@
|
||||
<view class="nav-item flexmc" bindtap="goToKaohe">
|
||||
<image class="nav-icon" src="{{imgUrls.iconMedal}}" mode="aspectFit"/><text class="sm">考核金牌</text>
|
||||
</view>
|
||||
<view class="nav-item flexmc" bindtap="goToDashouExam">
|
||||
<image class="nav-icon" src="{{imgUrls.iconBook}}" mode="aspectFit"/>
|
||||
<text class="sm">接单考试</text>
|
||||
<text wx:if="{{examEnabled}}" class="exam-tag {{examPassed ? 'exam-tag-pass' : 'exam-tag-pending'}}">{{examPassed ? '已通过' : '待考试'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -195,7 +221,6 @@
|
||||
<view class="func-item flexmc" bindtap="goToKaoheDafen"><image class="func-icon" src="{{imgUrls.iconKaoheDafen}}" mode="aspectFit"/><text class="func-txt">考核打分</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToKaoheJilu"><image class="func-icon" src="{{imgUrls.iconKaoheJilu}}" mode="aspectFit"/><text class="func-txt">考核记录</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToKaoheZhongxin"><image class="func-icon" src="{{imgUrls.iconKaoheZhongxin}}" mode="aspectFit"/><text class="func-txt">考核中心</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToKaoheWithdraw"><image class="func-icon" src="{{imgUrls.iconWithdraw}}" mode="aspectFit"/><text class="func-txt">考核提现</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -217,6 +242,7 @@
|
||||
<view class="func-grid">
|
||||
<view class="func-item flexmc" bindtap="goToKefu"><image class="func-icon" src="{{imgUrls.iconKefu}}" mode="aspectFit"/><text class="func-txt">在线客服</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToGuanzhuKs"><image class="func-icon" src="{{imgUrls.iconKuaishou}}" mode="aspectFit"/><text class="func-txt">关注快手</text></view>
|
||||
<view class="func-item flexmc" wx:if="{{showPindaoEntry}}" bindtap="openPindaoModal"><image class="func-icon" src="{{imgUrls.iconPindao}}" mode="aspectFit"/><text class="func-txt">频道</text></view>
|
||||
<view class="func-item flexmc" bindtap="contactInviter"><image class="func-icon" src="{{imgUrls.iconInvite}}" mode="aspectFit"/><text class="func-txt">联系邀请人</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToRules"><image class="func-icon" src="{{imgUrls.iconBook}}" mode="aspectFit"/><text class="func-txt">用户规则</text></view>
|
||||
<view class="func-item flexmc" bindtap="switchToNormal"><image class="func-icon" src="{{imgUrls.iconSwitch}}" mode="aspectFit"/><text class="func-txt">返回点单端</text></view>
|
||||
@@ -236,3 +262,10 @@
|
||||
|
||||
<global-notification id="global-notification"/>
|
||||
<popup-notice id="popupNotice"/>
|
||||
<pindao-modal
|
||||
visible="{{pindaoVisible}}"
|
||||
title="{{pindaoTitle}}"
|
||||
channelNo="{{pindaoChannelNo}}"
|
||||
images="{{pindaoImages}}"
|
||||
bind:close="closePindaoModal"
|
||||
/>
|
||||
@@ -40,6 +40,19 @@ page {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-bar-actions {
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-refresh-ico {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
@@ -134,6 +147,20 @@ page {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.user-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-action-ico {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 6rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.setting-ico {
|
||||
width: 53rpx;
|
||||
height: 53rpx;
|
||||
@@ -233,6 +260,23 @@ page {
|
||||
margin-right: 2%;
|
||||
}
|
||||
|
||||
.recharge-text-row {
|
||||
margin: 20rpx 24rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.recharge-text-btn {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 22rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #492f00;
|
||||
background: linear-gradient(180deg, #fff8e1, #ffe8b8);
|
||||
border: 1rpx solid rgba(201, 169, 98, 0.45);
|
||||
}
|
||||
|
||||
.freeze-row {
|
||||
margin: 16rpx 30rpx 0;
|
||||
padding: 20rpx 10rpx;
|
||||
@@ -356,11 +400,35 @@ page {
|
||||
|
||||
.order-nav {
|
||||
padding: 10rpx 0 20rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
width: 25%;
|
||||
width: 20%;
|
||||
box-sizing: border-box;
|
||||
padding: 10rpx 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.exam-tag {
|
||||
font-size: 20rpx;
|
||||
margin-top: 4rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.exam-tag-pass {
|
||||
color: #52c41a;
|
||||
background: rgba(82, 196, 26, 0.12);
|
||||
}
|
||||
|
||||
.exam-tag-pending {
|
||||
color: #fa8c16;
|
||||
background: rgba(250, 140, 16, 0.12);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
|
||||
+374
-385
@@ -1,385 +1,374 @@
|
||||
// pages/shangpin/shangpin.js
|
||||
const app = getApp();
|
||||
import { ensureLogin } from '../../utils/login';
|
||||
import { fetchShopGoods, bindShop } from '../../utils/shop';
|
||||
import { backfillUserProfileCache, getSessionToken } from '../../utils/role-tab-bar';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
|
||||
// 扫码场景参数名
|
||||
const SCENE_PARAM = 'scene';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
ossImageUrl: '',
|
||||
lunbozhanwei: '',
|
||||
|
||||
shangpingonggao: '',
|
||||
lunboList: [],
|
||||
shangpinleixing: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinliebiao: [],
|
||||
|
||||
selectedLeixingId: null,
|
||||
filteredData: [],
|
||||
showGonggaoModal: false,
|
||||
gonggaoAnim: true,
|
||||
isLoading: false,
|
||||
isRefreshing: false,
|
||||
hasInitialized: false,
|
||||
|
||||
currentTime: ''
|
||||
},
|
||||
|
||||
// 扫码解析的店铺ID
|
||||
shopId: null,
|
||||
|
||||
onLoad(options) {
|
||||
// 解析二维码参数
|
||||
if (options[SCENE_PARAM]) {
|
||||
try {
|
||||
const scene = decodeURIComponent(options[SCENE_PARAM]);
|
||||
this.shopId = scene;
|
||||
} catch (e) {
|
||||
console.error('scene解码失败', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 等待全局配置加载完成
|
||||
this.waitForConfigAndInit();
|
||||
// 已登录时才检查弹窗
|
||||
const token = getSessionToken(app);
|
||||
if (token) {
|
||||
PopupService.checkAndShow(this, 'index');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// 页面隐藏时清理弹窗视图
|
||||
onHide: function () {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
},
|
||||
|
||||
waitForConfigAndInit() {
|
||||
if (app.globalData.ossImageUrl) {
|
||||
this.setData({
|
||||
ossImageUrl: app.globalData.ossImageUrl,
|
||||
lunbozhanwei: app.globalData.lunbozhanwei,
|
||||
currentTime: this.getCurrentTime()
|
||||
});
|
||||
this.initPageData();
|
||||
} else {
|
||||
setTimeout(() => this.waitForConfigAndInit(), 100);
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
backfillUserProfileCache(app);
|
||||
if (this.data.ossImageUrl) {
|
||||
this.setData({
|
||||
gonggaoAnim: true,
|
||||
currentTime: this.getCurrentTime(),
|
||||
});
|
||||
}
|
||||
this.registerNotificationComponent();
|
||||
|
||||
if (!getSessionToken(app)) {
|
||||
this.setData({
|
||||
hasInitialized: false,
|
||||
filteredData: [],
|
||||
shangpinleixing: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinliebiao: [],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 已登录时加载/刷新商品;未登录不自动登录、不跳转
|
||||
if (!this.data.isLoading) {
|
||||
const needLoad = !this.data.hasInitialized
|
||||
|| !this.data.filteredData
|
||||
|| this.data.filteredData.length === 0;
|
||||
if (needLoad) {
|
||||
this.initPageData();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getCurrentTime() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hour = now.getHours().toString().padStart(2, '0');
|
||||
const minute = now.getMinutes().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`;
|
||||
},
|
||||
|
||||
sortByPaixu(list) {
|
||||
if (!list || !Array.isArray(list)) return [];
|
||||
return [...list].sort((a, b) => {
|
||||
const paixuA = a.paixu || 0;
|
||||
const paixuB = b.paixu || 0;
|
||||
if (paixuB !== paixuA) return paixuB - paixuA;
|
||||
return a.id - b.id;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化页面数据(重构后)
|
||||
* 流程:确保登录 → 如有扫码ID则绑定店铺 → 并行加载公告轮播图和商品数据
|
||||
*/
|
||||
async initPageData() {
|
||||
this.setData({ isLoading: true });
|
||||
|
||||
try {
|
||||
// 1. 确保登录(静默,不跳转)
|
||||
await ensureLogin({ page: this });
|
||||
|
||||
// 2. 扫码进入时绑定店铺
|
||||
if (this.shopId) {
|
||||
await bindShop(this.shopId);
|
||||
}
|
||||
|
||||
// 3. 并行加载公告轮播图和商品数据
|
||||
const [gonggaoResult, shopData] = await Promise.all([
|
||||
this.loadGonggaoAndLunbo(),
|
||||
fetchShopGoods()
|
||||
]);
|
||||
|
||||
// 4. 处理商品数据
|
||||
const sortedLeixing = this.sortByPaixu(shopData.shangpinleixing || []);
|
||||
const sortedZhuanqu = this.sortByPaixu(shopData.shangpinzhuanqu || []);
|
||||
const sortedShangpin = this.sortByPaixu(shopData.shangpinliebiao || []);
|
||||
|
||||
// 更新全局缓存
|
||||
app.globalData.shangpinleixing = sortedLeixing;
|
||||
app.globalData.shangpinzhuanqu = sortedZhuanqu;
|
||||
app.globalData.shangpinliebiao = sortedShangpin;
|
||||
|
||||
this.setData({
|
||||
shangpinleixing: sortedLeixing,
|
||||
shangpinzhuanqu: sortedZhuanqu,
|
||||
shangpinliebiao: sortedShangpin,
|
||||
selectedLeixingId: sortedLeixing[0]?.id || null,
|
||||
isLoading: false,
|
||||
hasInitialized: true,
|
||||
}, () => {
|
||||
this.filterShangpinData();
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化失败:', error);
|
||||
this.setData({ isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载公告和轮播图
|
||||
*/
|
||||
loadGonggaoAndLunbo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 如果全局已有数据,直接使用
|
||||
if (app.globalData.shangpingonggao && app.globalData.shangpinlunbo.length > 0) {
|
||||
this.setData({
|
||||
shangpingonggao: app.globalData.shangpingonggao,
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo)
|
||||
});
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
wx.request({
|
||||
url: app.globalData.apiBaseUrl + '/peizhi/shangpingonggao/',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data) {
|
||||
const data = res.data;
|
||||
app.globalData.shangpingonggao = data.shangpingonggao || '';
|
||||
app.globalData.shangpinlunbo = data.shangpinlunbo || [];
|
||||
this.setData({
|
||||
shangpingonggao: app.globalData.shangpingonggao,
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo)
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('请求失败'));
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
processImageUrls(urlList) {
|
||||
if (!urlList || !Array.isArray(urlList)) return [];
|
||||
return urlList.map(url => url.startsWith('http') ? url : this.data.ossImageUrl + url);
|
||||
},
|
||||
|
||||
/**
|
||||
* 筛选商品数据
|
||||
*/
|
||||
filterShangpinData() {
|
||||
const { selectedLeixingId, shangpinzhuanqu, shangpinliebiao } = this.data;
|
||||
|
||||
if (!selectedLeixingId || !shangpinzhuanqu || !shangpinliebiao) {
|
||||
this.setData({ filteredData: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
const zhuanquByLeixing = new Map();
|
||||
shangpinzhuanqu.forEach(zhuanqu => {
|
||||
if (zhuanqu.leixing_id === selectedLeixingId) {
|
||||
if (!zhuanquByLeixing.has(selectedLeixingId)) {
|
||||
zhuanquByLeixing.set(selectedLeixingId, []);
|
||||
}
|
||||
zhuanquByLeixing.get(selectedLeixingId).push(zhuanqu);
|
||||
}
|
||||
});
|
||||
|
||||
const shangpinByZhuanqu = new Map();
|
||||
shangpinliebiao.forEach(shangpin => {
|
||||
if (shangpin.leixing_id === selectedLeixingId && shangpin.zhuanqu_id) {
|
||||
const zhuanquId = shangpin.zhuanqu_id;
|
||||
if (!shangpinByZhuanqu.has(zhuanquId)) {
|
||||
shangpinByZhuanqu.set(zhuanquId, []);
|
||||
}
|
||||
|
||||
const jiage = parseFloat(shangpin.jiage) || 0;
|
||||
const priceInteger = Math.floor(jiage);
|
||||
let priceDecimal = '00';
|
||||
const decimalPart = (jiage - priceInteger).toFixed(2);
|
||||
if (decimalPart > 0) {
|
||||
priceDecimal = decimalPart.toString().split('.')[1];
|
||||
}
|
||||
|
||||
shangpinByZhuanqu.get(zhuanquId).push({
|
||||
id: shangpin.id,
|
||||
biaoqian: shangpin.biaoqian,
|
||||
jiage: jiage,
|
||||
priceInteger: priceInteger,
|
||||
priceDecimal: priceDecimal,
|
||||
tupian_url: shangpin.tupian_url,
|
||||
duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0,
|
||||
paixu: shangpin.paixu || 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const filteredData = [];
|
||||
const currentZhuanquList = zhuanquByLeixing.get(selectedLeixingId) || [];
|
||||
|
||||
currentZhuanquList.forEach(zhuanqu => {
|
||||
const shangpinList = shangpinByZhuanqu.get(zhuanqu.id) || [];
|
||||
if (shangpinList.length > 0) {
|
||||
filteredData.push({
|
||||
zhuanqu: {
|
||||
id: zhuanqu.id,
|
||||
mingzi: zhuanqu.mingzi,
|
||||
paixu: zhuanqu.paixu || 0
|
||||
},
|
||||
shangpinList: shangpinList
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sortedFilteredData = filteredData.sort((a, b) => {
|
||||
const paixuA = a.zhuanqu.paixu || 0;
|
||||
const paixuB = b.zhuanqu.paixu || 0;
|
||||
if (paixuB !== paixuA) return paixuB - paixuA;
|
||||
return a.zhuanqu.id - b.zhuanqu.id;
|
||||
});
|
||||
|
||||
this.setData({ filteredData: sortedFilteredData });
|
||||
},
|
||||
|
||||
selectLeixing(e) {
|
||||
const leixingId = e.currentTarget.dataset.id;
|
||||
if (this.data.selectedLeixingId === leixingId) return;
|
||||
this.setData({ selectedLeixingId: leixingId }, () => {
|
||||
this.filterShangpinData();
|
||||
});
|
||||
},
|
||||
|
||||
showGonggaoDetail() {
|
||||
this.setData({
|
||||
showGonggaoModal: true,
|
||||
_modalLeaving: false,
|
||||
gonggaoAnim: false,
|
||||
currentTime: this.getCurrentTime()
|
||||
});
|
||||
},
|
||||
|
||||
hideGonggaoDetail() {
|
||||
this.setData({
|
||||
showGonggaoModal: false,
|
||||
_modalLeaving: true,
|
||||
gonggaoAnim: true
|
||||
});
|
||||
setTimeout(() => this.setData({ _modalLeaving: false }), 300);
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新所有数据
|
||||
* 只刷新商品数据
|
||||
*/
|
||||
async refreshAllData() {
|
||||
if (this.data.isRefreshing) return;
|
||||
this.setData({ isRefreshing: true });
|
||||
wx.showLoading({ title: '刷新中...', mask: true });
|
||||
|
||||
try {
|
||||
// 重新获取商品数据
|
||||
const shopData = await fetchShopGoods();
|
||||
|
||||
const sortedLeixing = this.sortByPaixu(shopData.shangpinleixing || []);
|
||||
const sortedZhuanqu = this.sortByPaixu(shopData.shangpinzhuanqu || []);
|
||||
const sortedShangpin = this.sortByPaixu(shopData.shangpinliebiao || []);
|
||||
|
||||
app.globalData.shangpinleixing = sortedLeixing;
|
||||
app.globalData.shangpinzhuanqu = sortedZhuanqu;
|
||||
app.globalData.shangpinliebiao = sortedShangpin;
|
||||
|
||||
this.setData({
|
||||
shangpinleixing: sortedLeixing,
|
||||
shangpinzhuanqu: sortedZhuanqu,
|
||||
shangpinliebiao: sortedShangpin,
|
||||
selectedLeixingId: sortedLeixing[0]?.id || null,
|
||||
isRefreshing: false
|
||||
}, () => {
|
||||
this.filterShangpinData();
|
||||
});
|
||||
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 });
|
||||
} catch (error) {
|
||||
wx.hideLoading();
|
||||
this.setData({ isRefreshing: false });
|
||||
}
|
||||
},
|
||||
|
||||
goToDetail(e) {
|
||||
const shangpinId = e.currentTarget.dataset.id;
|
||||
if (!shangpinId) return;
|
||||
wx.navigateTo({
|
||||
url: `/pages/product-detail/product-detail?id=${shangpinId}`
|
||||
});
|
||||
},
|
||||
|
||||
onReachBottom() {},
|
||||
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '阿龙电竞',
|
||||
path: '/pages/shangpin/shangpin'
|
||||
};
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.setData({ gonggaoAnim: false });
|
||||
}
|
||||
}))
|
||||
// pages/shangpin/shangpin.js
|
||||
const app = getApp();
|
||||
import { ensureLogin } from '../../utils/login';
|
||||
import { fetchShopGoods, bindShop } from '../../utils/shop';
|
||||
import { backfillUserProfileCache, getSessionToken } from '../../utils/role-tab-bar';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
import { fetchGonggaoLunbo, isGonggaoCacheValid } from '../../utils/display-config';
|
||||
|
||||
// 扫码场景参数名
|
||||
const SCENE_PARAM = 'scene';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
ossImageUrl: '',
|
||||
lunbozhanwei: '',
|
||||
|
||||
shangpingonggao: '',
|
||||
lunboList: [],
|
||||
shangpinleixing: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinliebiao: [],
|
||||
|
||||
selectedLeixingId: null,
|
||||
filteredData: [],
|
||||
showGonggaoModal: false,
|
||||
gonggaoAnim: true,
|
||||
isLoading: false,
|
||||
isRefreshing: false,
|
||||
hasInitialized: false,
|
||||
|
||||
currentTime: ''
|
||||
},
|
||||
|
||||
// 扫码解析的店铺ID
|
||||
shopId: null,
|
||||
|
||||
onLoad(options) {
|
||||
// 解析二维码参数
|
||||
if (options[SCENE_PARAM]) {
|
||||
try {
|
||||
const scene = decodeURIComponent(options[SCENE_PARAM]);
|
||||
this.shopId = scene;
|
||||
} catch (e) {
|
||||
console.error('scene解码失败', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 等待全局配置加载完成
|
||||
this.waitForConfigAndInit();
|
||||
// 已登录时才检查弹窗
|
||||
const token = getSessionToken(app);
|
||||
if (token) {
|
||||
PopupService.checkAndShow(this, 'index');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// 页面隐藏时清理弹窗视图
|
||||
onHide: function () {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
},
|
||||
|
||||
waitForConfigAndInit() {
|
||||
if (app.globalData.ossImageUrl) {
|
||||
this.setData({
|
||||
ossImageUrl: app.globalData.ossImageUrl,
|
||||
lunbozhanwei: app.globalData.lunbozhanwei,
|
||||
currentTime: this.getCurrentTime()
|
||||
});
|
||||
this.initPageData();
|
||||
} else {
|
||||
setTimeout(() => this.waitForConfigAndInit(), 100);
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
backfillUserProfileCache(app);
|
||||
if (this.data.ossImageUrl) {
|
||||
this.setData({
|
||||
gonggaoAnim: true,
|
||||
currentTime: this.getCurrentTime(),
|
||||
});
|
||||
}
|
||||
this.registerNotificationComponent();
|
||||
|
||||
if (!getSessionToken(app)) {
|
||||
this.setData({
|
||||
hasInitialized: false,
|
||||
filteredData: [],
|
||||
shangpinleixing: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinliebiao: [],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 已登录时加载/刷新商品;未登录不自动登录、不跳转
|
||||
if (!this.data.isLoading) {
|
||||
const needLoad = !this.data.hasInitialized
|
||||
|| !this.data.filteredData
|
||||
|| this.data.filteredData.length === 0;
|
||||
if (needLoad) {
|
||||
this.initPageData();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getCurrentTime() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hour = now.getHours().toString().padStart(2, '0');
|
||||
const minute = now.getMinutes().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`;
|
||||
},
|
||||
|
||||
sortByPaixu(list) {
|
||||
if (!list || !Array.isArray(list)) return [];
|
||||
return [...list].sort((a, b) => {
|
||||
const paixuA = a.paixu || 0;
|
||||
const paixuB = b.paixu || 0;
|
||||
if (paixuB !== paixuA) return paixuB - paixuA;
|
||||
return a.id - b.id;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化页面数据(重构后)
|
||||
* 流程:确保登录 → 如有扫码ID则绑定店铺 → 并行加载公告轮播图和商品数据
|
||||
*/
|
||||
async initPageData() {
|
||||
this.setData({ isLoading: true });
|
||||
|
||||
try {
|
||||
// 1. 确保登录(静默,不跳转)
|
||||
await ensureLogin({ page: this });
|
||||
|
||||
// 2. 扫码进入时绑定店铺
|
||||
if (this.shopId) {
|
||||
await bindShop(this.shopId);
|
||||
}
|
||||
|
||||
// 3. 并行加载公告轮播图和商品数据
|
||||
const [gonggaoResult, shopData] = await Promise.all([
|
||||
this.loadGonggaoAndLunbo(),
|
||||
fetchShopGoods()
|
||||
]);
|
||||
|
||||
// 4. 处理商品数据
|
||||
const sortedLeixing = this.sortByPaixu(shopData.shangpinleixing || []);
|
||||
const sortedZhuanqu = this.sortByPaixu(shopData.shangpinzhuanqu || []);
|
||||
const sortedShangpin = this.sortByPaixu(shopData.shangpinliebiao || []);
|
||||
|
||||
// 更新全局缓存
|
||||
app.globalData.shangpinleixing = sortedLeixing;
|
||||
app.globalData.shangpinzhuanqu = sortedZhuanqu;
|
||||
app.globalData.shangpinliebiao = sortedShangpin;
|
||||
|
||||
this.setData({
|
||||
shangpinleixing: sortedLeixing,
|
||||
shangpinzhuanqu: sortedZhuanqu,
|
||||
shangpinliebiao: sortedShangpin,
|
||||
selectedLeixingId: sortedLeixing[0]?.id || null,
|
||||
isLoading: false,
|
||||
hasInitialized: true,
|
||||
}, () => {
|
||||
this.filterShangpinData();
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化失败:', error);
|
||||
this.setData({ isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载公告和轮播图
|
||||
*/
|
||||
loadGonggaoAndLunbo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (isGonggaoCacheValid(app)) {
|
||||
this.setData({
|
||||
shangpingonggao: app.globalData.shangpingonggao,
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo)
|
||||
});
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
fetchGonggaoLunbo(app, 'order_pool')
|
||||
.then((data) => {
|
||||
this.setData({
|
||||
shangpingonggao: data.shangpingonggao || '',
|
||||
lunboList: this.processImageUrls(data.shangpinlunbo || [])
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
},
|
||||
|
||||
processImageUrls(urlList) {
|
||||
if (!urlList || !Array.isArray(urlList)) return [];
|
||||
return urlList.map(url => url.startsWith('http') ? url : this.data.ossImageUrl + url);
|
||||
},
|
||||
|
||||
/**
|
||||
* 筛选商品数据
|
||||
*/
|
||||
filterShangpinData() {
|
||||
const { selectedLeixingId, shangpinzhuanqu, shangpinliebiao } = this.data;
|
||||
|
||||
if (!selectedLeixingId || !shangpinzhuanqu || !shangpinliebiao) {
|
||||
this.setData({ filteredData: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
const zhuanquByLeixing = new Map();
|
||||
shangpinzhuanqu.forEach(zhuanqu => {
|
||||
if (zhuanqu.leixing_id === selectedLeixingId) {
|
||||
if (!zhuanquByLeixing.has(selectedLeixingId)) {
|
||||
zhuanquByLeixing.set(selectedLeixingId, []);
|
||||
}
|
||||
zhuanquByLeixing.get(selectedLeixingId).push(zhuanqu);
|
||||
}
|
||||
});
|
||||
|
||||
const shangpinByZhuanqu = new Map();
|
||||
shangpinliebiao.forEach(shangpin => {
|
||||
if (shangpin.leixing_id === selectedLeixingId && shangpin.zhuanqu_id) {
|
||||
const zhuanquId = shangpin.zhuanqu_id;
|
||||
if (!shangpinByZhuanqu.has(zhuanquId)) {
|
||||
shangpinByZhuanqu.set(zhuanquId, []);
|
||||
}
|
||||
|
||||
const jiage = parseFloat(shangpin.jiage) || 0;
|
||||
const priceInteger = Math.floor(jiage);
|
||||
let priceDecimal = '00';
|
||||
const decimalPart = (jiage - priceInteger).toFixed(2);
|
||||
if (decimalPart > 0) {
|
||||
priceDecimal = decimalPart.toString().split('.')[1];
|
||||
}
|
||||
|
||||
shangpinByZhuanqu.get(zhuanquId).push({
|
||||
id: shangpin.id,
|
||||
biaoqian: shangpin.biaoqian,
|
||||
jiage: jiage,
|
||||
priceInteger: priceInteger,
|
||||
priceDecimal: priceDecimal,
|
||||
tupian_url: shangpin.tupian_url,
|
||||
duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0,
|
||||
paixu: shangpin.paixu || 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const filteredData = [];
|
||||
const currentZhuanquList = zhuanquByLeixing.get(selectedLeixingId) || [];
|
||||
|
||||
currentZhuanquList.forEach(zhuanqu => {
|
||||
const shangpinList = shangpinByZhuanqu.get(zhuanqu.id) || [];
|
||||
if (shangpinList.length > 0) {
|
||||
filteredData.push({
|
||||
zhuanqu: {
|
||||
id: zhuanqu.id,
|
||||
mingzi: zhuanqu.mingzi,
|
||||
paixu: zhuanqu.paixu || 0
|
||||
},
|
||||
shangpinList: shangpinList
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sortedFilteredData = filteredData.sort((a, b) => {
|
||||
const paixuA = a.zhuanqu.paixu || 0;
|
||||
const paixuB = b.zhuanqu.paixu || 0;
|
||||
if (paixuB !== paixuA) return paixuB - paixuA;
|
||||
return a.zhuanqu.id - b.zhuanqu.id;
|
||||
});
|
||||
|
||||
this.setData({ filteredData: sortedFilteredData });
|
||||
},
|
||||
|
||||
selectLeixing(e) {
|
||||
const leixingId = e.currentTarget.dataset.id;
|
||||
if (this.data.selectedLeixingId === leixingId) return;
|
||||
this.setData({ selectedLeixingId: leixingId }, () => {
|
||||
this.filterShangpinData();
|
||||
});
|
||||
},
|
||||
|
||||
showGonggaoDetail() {
|
||||
this.setData({
|
||||
showGonggaoModal: true,
|
||||
_modalLeaving: false,
|
||||
gonggaoAnim: false,
|
||||
currentTime: this.getCurrentTime()
|
||||
});
|
||||
},
|
||||
|
||||
hideGonggaoDetail() {
|
||||
this.setData({
|
||||
showGonggaoModal: false,
|
||||
_modalLeaving: true,
|
||||
gonggaoAnim: true
|
||||
});
|
||||
setTimeout(() => this.setData({ _modalLeaving: false }), 300);
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新所有数据
|
||||
* 只刷新商品数据
|
||||
*/
|
||||
async refreshAllData() {
|
||||
if (this.data.isRefreshing) return;
|
||||
this.setData({ isRefreshing: true });
|
||||
wx.showLoading({ title: '刷新中...', mask: true });
|
||||
|
||||
try {
|
||||
// 重新获取商品数据
|
||||
const shopData = await fetchShopGoods();
|
||||
|
||||
const sortedLeixing = this.sortByPaixu(shopData.shangpinleixing || []);
|
||||
const sortedZhuanqu = this.sortByPaixu(shopData.shangpinzhuanqu || []);
|
||||
const sortedShangpin = this.sortByPaixu(shopData.shangpinliebiao || []);
|
||||
|
||||
app.globalData.shangpinleixing = sortedLeixing;
|
||||
app.globalData.shangpinzhuanqu = sortedZhuanqu;
|
||||
app.globalData.shangpinliebiao = sortedShangpin;
|
||||
|
||||
this.setData({
|
||||
shangpinleixing: sortedLeixing,
|
||||
shangpinzhuanqu: sortedZhuanqu,
|
||||
shangpinliebiao: sortedShangpin,
|
||||
selectedLeixingId: sortedLeixing[0]?.id || null,
|
||||
isRefreshing: false
|
||||
}, () => {
|
||||
this.filterShangpinData();
|
||||
});
|
||||
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 });
|
||||
} catch (error) {
|
||||
wx.hideLoading();
|
||||
this.setData({ isRefreshing: false });
|
||||
}
|
||||
},
|
||||
|
||||
goToDetail(e) {
|
||||
const shangpinId = e.currentTarget.dataset.id;
|
||||
if (!shangpinId) return;
|
||||
wx.navigateTo({
|
||||
url: `/pages/product-detail/product-detail?id=${shangpinId}`
|
||||
});
|
||||
},
|
||||
|
||||
onReachBottom() {},
|
||||
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '阿龙电竞',
|
||||
path: '/pages/shangpin/shangpin'
|
||||
};
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.setData({ gonggaoAnim: false });
|
||||
}
|
||||
}))
|
||||
@@ -1,21 +1,32 @@
|
||||
// pages/zuzhanghaibao/zuzhanghaibao.js
|
||||
// pages/invite-manager/invite-manager.js — 组长推广海报
|
||||
import request from '../../utils/request.js';
|
||||
import {
|
||||
getFullImageUrl,
|
||||
loadPosterPageConfig,
|
||||
posterInviteCacheKey,
|
||||
posterQrCacheKey,
|
||||
} from '../../utils/poster-page.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
qrcodeUrl: '', // 二维码完整URL
|
||||
yaoqingma: '', // 组长邀请码字符串
|
||||
qrcodeUrl: '',
|
||||
yaoqingma: '',
|
||||
isLoading: false,
|
||||
showPosterCanvas: false,
|
||||
posterImageTemp: '', // 生成的海报临时路径
|
||||
avatarUrl: '', // 用户头像完整URL
|
||||
posterImageTemp: '',
|
||||
avatarUrl: '',
|
||||
uid: '',
|
||||
nickName: '',
|
||||
posterBgUrl: '',
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
async onLoad() {
|
||||
const { bgUrl, qrCacheKey } = await loadPosterPageConfig('zuzhang', app);
|
||||
this._qrCacheKey = qrCacheKey;
|
||||
this._inviteCacheKey = posterInviteCacheKey();
|
||||
this.setData({ posterBgUrl: bgUrl });
|
||||
this.loadFromCache();
|
||||
this.loadUserInfo();
|
||||
},
|
||||
@@ -29,126 +40,88 @@ Page({
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
hide: () => notificationComp.hideNotification(),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 从缓存加载二维码URL和邀请码
|
||||
* 缓存键名使用独特拼音,避免与管事页面冲突
|
||||
*/
|
||||
loadFromCache() {
|
||||
try {
|
||||
const relativeUrl = wx.getStorageSync('zuzhang_haibao_url');
|
||||
const key = this._qrCacheKey || posterQrCacheKey('zuzhang');
|
||||
const relativeUrl = wx.getStorageSync(key);
|
||||
if (relativeUrl) {
|
||||
const fullUrl = this.getFullImageUrl(relativeUrl);
|
||||
this.setData({ qrcodeUrl: fullUrl });
|
||||
}
|
||||
const yaoqingma = wx.getStorageSync('zuzhang_yaoqingma');
|
||||
if (yaoqingma) {
|
||||
this.setData({ yaoqingma });
|
||||
this.setData({ qrcodeUrl: getFullImageUrl(relativeUrl, app) });
|
||||
}
|
||||
const inviteKey = this._inviteCacheKey || posterInviteCacheKey();
|
||||
const yaoqingma = wx.getStorageSync(inviteKey);
|
||||
if (yaoqingma) this.setData({ yaoqingma });
|
||||
} catch (error) {
|
||||
console.error('读取缓存失败', error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载用户头像、昵称、UID
|
||||
*/
|
||||
loadUserInfo() {
|
||||
const touxiang = wx.getStorageSync('touxiang');
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg');
|
||||
let avatarUrl = defaultAvatar;
|
||||
if (touxiang && typeof touxiang === 'string' && touxiang.trim() !== '') {
|
||||
avatarUrl = this.getFullImageUrl(touxiang);
|
||||
avatarUrl = getFullImageUrl(touxiang, app);
|
||||
}
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
// 组长昵称可能从全局变量或缓存获取
|
||||
let nickName = app.globalData.nicheng || wx.getStorageSync('nicheng') || '组长';
|
||||
const nickName = app.globalData.nicheng || wx.getStorageSync('nicheng') || '组长';
|
||||
this.setData({ avatarUrl, uid, nickName });
|
||||
},
|
||||
|
||||
/**
|
||||
* 拼接完整图片URL
|
||||
*/
|
||||
getFullImageUrl(url) {
|
||||
if (!url) return '';
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
return url.startsWith('http') ? url : ossUrl + url;
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新/获取二维码和邀请码
|
||||
* 调用 /peizhi/zuzhanghb 接口
|
||||
*/
|
||||
refreshQRCode() {
|
||||
if (this.data.isLoading) return;
|
||||
this.setData({ isLoading: true });
|
||||
|
||||
request({
|
||||
url: '/peizhi/zuzhanghb',
|
||||
method: 'POST',
|
||||
})
|
||||
.then(res => {
|
||||
request({ url: '/peizhi/zuzhanghb', method: 'POST' })
|
||||
.then((res) => {
|
||||
if (res.data && res.data.code === 0) {
|
||||
const { url: relativeUrl, yaoqingma } = res.data.data; // 假设返回字段为 url 和 yaoqingma
|
||||
const { url: relativeUrl, yaoqingma } = res.data.data || {};
|
||||
if (relativeUrl) {
|
||||
wx.setStorageSync('zuzhang_haibao_url', relativeUrl);
|
||||
const fullUrl = this.getFullImageUrl(relativeUrl);
|
||||
this.setData({ qrcodeUrl: fullUrl });
|
||||
const key = this._qrCacheKey || posterQrCacheKey('zuzhang');
|
||||
wx.setStorageSync(key, relativeUrl);
|
||||
this.setData({ qrcodeUrl: getFullImageUrl(relativeUrl, app) });
|
||||
}
|
||||
if (yaoqingma) {
|
||||
wx.setStorageSync('zuzhang_yaoqingma', yaoqingma);
|
||||
const inviteKey = this._inviteCacheKey || posterInviteCacheKey();
|
||||
wx.setStorageSync(inviteKey, yaoqingma);
|
||||
this.setData({ yaoqingma });
|
||||
}
|
||||
wx.showToast({ title: '获取成功', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: res.data.msg || '获取失败', icon: 'none' });
|
||||
wx.showToast({ title: res.data.msg || res.data.message || '获取失败', icon: 'none' });
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
console.error('获取组长海报失败', err);
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
})
|
||||
.finally(() => {
|
||||
this.setData({ isLoading: false });
|
||||
});
|
||||
.finally(() => this.setData({ isLoading: false }));
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存二维码到相册(先下载网络图片)
|
||||
*/
|
||||
saveToAlbum() {
|
||||
if (!this.data.qrcodeUrl) {
|
||||
wx.showToast({ title: '暂无二维码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '保存中...', mask: true });
|
||||
wx.downloadFile({
|
||||
url: this.data.qrcodeUrl,
|
||||
success: (res) => {
|
||||
wx.hideLoading();
|
||||
if (res.statusCode === 200) {
|
||||
this.downloadAndSave(res.tempFilePath);
|
||||
} else {
|
||||
wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
}
|
||||
if (res.statusCode === 200) this.downloadAndSave(res.tempFilePath);
|
||||
else wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
},
|
||||
fail: (err) => {
|
||||
fail: () => {
|
||||
wx.hideLoading();
|
||||
console.error('下载二维码失败', err);
|
||||
wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 复制邀请码到剪贴板
|
||||
*/
|
||||
copyInviteCode() {
|
||||
if (!this.data.yaoqingma) {
|
||||
wx.showToast({ title: '暂无邀请码', icon: 'none' });
|
||||
@@ -156,145 +129,78 @@ Page({
|
||||
}
|
||||
wx.setClipboardData({
|
||||
data: this.data.yaoqingma,
|
||||
success: () => {
|
||||
wx.showToast({ title: '复制成功', icon: 'success' });
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({ title: '复制失败', icon: 'none' });
|
||||
}
|
||||
success: () => wx.showToast({ title: '复制成功', icon: 'success' }),
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成专属海报(包含头像、二维码、昵称、标语)
|
||||
*/
|
||||
generateMyPoster() {
|
||||
if (!this.data.qrcodeUrl) {
|
||||
wx.showToast({ title: '请先生成二维码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '生成海报中...', mask: true });
|
||||
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
// 🔥 组长海报背景图路径:beijing/zuzhangbeijing.jpg
|
||||
const bgUrl = ossUrl + 'beijing/zuzhangbeijing.jpg';
|
||||
const urls = [bgUrl, this.data.qrcodeUrl, this.data.avatarUrl];
|
||||
|
||||
// 并行加载所有图片
|
||||
const promises = urls.map(url => this.loadImage(url));
|
||||
Promise.all(promises)
|
||||
const bgUrl = this.data.posterBgUrl;
|
||||
Promise.all([bgUrl, this.data.qrcodeUrl, this.data.avatarUrl].map((u) => this.loadImage(u)))
|
||||
.then((images) => {
|
||||
wx.hideLoading();
|
||||
this.setData({ showPosterCanvas: true }, () => {
|
||||
// 确保 canvas 已渲染
|
||||
setTimeout(() => {
|
||||
this.drawPoster(images[0], images[1], images[2]);
|
||||
}, 200);
|
||||
setTimeout(() => this.drawPoster(images[0], images[1], images[2]), 200);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(() => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '图片加载失败', icon: 'none' });
|
||||
console.error('图片加载失败', err);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载图片为本地路径(用于 canvas 绘制)
|
||||
*/
|
||||
loadImage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getImageInfo({
|
||||
src: url,
|
||||
success: (res) => resolve(res.path),
|
||||
fail: reject
|
||||
});
|
||||
wx.getImageInfo({ src: url, success: (res) => resolve(res.path), fail: reject });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制海报
|
||||
* @param {string} bgPath 背景图本地路径
|
||||
* @param {string} qrPath 二维码本地路径
|
||||
* @param {string} avatarPath 头像本地路径
|
||||
*/
|
||||
drawPoster(bgPath, qrPath, avatarPath) {
|
||||
const ctx = wx.createCanvasContext('posterCanvas', this);
|
||||
const query = wx.createSelectorQuery().in(this);
|
||||
query.select('.poster-canvas').boundingClientRect(rect => {
|
||||
wx.createSelectorQuery().in(this).select('.poster-canvas').boundingClientRect((rect) => {
|
||||
if (!rect) {
|
||||
wx.showToast({ title: '画布获取失败', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const canvasWidth = rect.width;
|
||||
const canvasHeight = rect.height;
|
||||
|
||||
// 1. 绘制背景
|
||||
ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight);
|
||||
|
||||
// 🔥【位置调整】二维码向右上角移动一丢丢 (增加 margin 从 5 到 15)
|
||||
const qrSize = 100;
|
||||
const margin = 15; // 原来 5,现在 15,向右下各移一点,但看起来更协调
|
||||
const margin = 15;
|
||||
const qrX = canvasWidth - qrSize - margin;
|
||||
const qrY = canvasHeight - qrSize - margin;
|
||||
ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
|
||||
|
||||
// 🔥【位置调整】头像也向右上移动一丢丢 (原来 margin 5,现在 margin 15)
|
||||
const avatarSize = 60;
|
||||
const avatarMargin = 15;
|
||||
const avatarX = avatarMargin + 10; // 原来 margin 5+20,现在 15+20,相当于右移10
|
||||
const avatarY = canvasHeight - avatarSize - avatarMargin;
|
||||
|
||||
// 圆形头像裁剪
|
||||
const avatarX = margin + 10;
|
||||
const avatarY = canvasHeight - avatarSize - margin;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(avatarX + avatarSize/2, avatarY + avatarSize/2, avatarSize/2, 0, 2 * Math.PI);
|
||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI);
|
||||
ctx.clip();
|
||||
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
|
||||
ctx.restore();
|
||||
|
||||
// 绘制昵称(右侧)
|
||||
ctx.setFontSize(16);
|
||||
ctx.setFillStyle('#333333'); // 根据背景图颜色可调整
|
||||
ctx.setFillStyle('#333333');
|
||||
ctx.setTextAlign('left');
|
||||
|
||||
let nick = this.data.nickName || '组长';
|
||||
if (nick.length > 3) {
|
||||
nick = nick.substring(0, 3) + '...';
|
||||
}
|
||||
const nameText = nick;
|
||||
const textX = avatarX + avatarSize + 8;
|
||||
const textY = avatarY + avatarSize/2 + 6;
|
||||
ctx.fillText(nameText, textX, textY);
|
||||
|
||||
// 绘制邀请语
|
||||
if (nick.length > 3) nick = nick.substring(0, 3) + '...';
|
||||
ctx.fillText(nick, avatarX + avatarSize + 8, avatarY + avatarSize / 2 + 6);
|
||||
ctx.setFontSize(18);
|
||||
ctx.setFillStyle('#ffaa00'); // 金黄色,呼应组长主题
|
||||
ctx.setTextAlign('left');
|
||||
const slogan = '加入我的团队 ✦';
|
||||
const sloganX = avatarX;
|
||||
const sloganY = avatarY - 12;
|
||||
ctx.fillText(slogan, sloganX, sloganY);
|
||||
|
||||
// 异步绘制
|
||||
ctx.setFillStyle('#ffaa00');
|
||||
ctx.fillText('加入我的团队 ✦', avatarX, avatarY - 12);
|
||||
ctx.draw(false, () => {
|
||||
wx.canvasToTempFilePath({
|
||||
canvasId: 'posterCanvas',
|
||||
success: (res) => {
|
||||
this.setData({ posterImageTemp: res.tempFilePath });
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('生成海报临时文件失败', err);
|
||||
}
|
||||
success: (res) => this.setData({ posterImageTemp: res.tempFilePath }),
|
||||
}, this);
|
||||
});
|
||||
}).exec();
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存生成的海报
|
||||
*/
|
||||
savePoster() {
|
||||
if (!this.data.posterImageTemp) {
|
||||
wx.showToast({ title: '海报尚未生成', icon: 'none' });
|
||||
@@ -303,54 +209,37 @@ Page({
|
||||
this.downloadAndSave(this.data.posterImageTemp);
|
||||
},
|
||||
|
||||
/**
|
||||
* 隐藏海报画布,返回主界面
|
||||
*/
|
||||
hidePoster() {
|
||||
this.setData({ showPosterCanvas: false, posterImageTemp: '' });
|
||||
},
|
||||
|
||||
/**
|
||||
* 通用保存图片到相册(带权限检查)
|
||||
*/
|
||||
downloadAndSave(filePath) {
|
||||
wx.getSetting({
|
||||
success: (res) => {
|
||||
if (!res.authSetting['scope.writePhotosAlbum']) {
|
||||
wx.authorize({
|
||||
scope: 'scope.writePhotosAlbum',
|
||||
success: () => {
|
||||
this._saveImage(filePath);
|
||||
},
|
||||
success: () => this._saveImage(filePath),
|
||||
fail: () => {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '需要您授权保存到相册',
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
wx.openSetting();
|
||||
}
|
||||
}
|
||||
success: (m) => { if (m.confirm) wx.openSetting(); },
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this._saveImage(filePath);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_saveImage(filePath) {
|
||||
wx.saveImageToPhotosAlbum({
|
||||
filePath: filePath,
|
||||
success: () => {
|
||||
wx.showToast({ title: '保存成功', icon: 'success' });
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('保存失败', err);
|
||||
wx.showToast({ title: '保存失败', icon: 'none' });
|
||||
}
|
||||
filePath,
|
||||
success: () => wx.showToast({ title: '保存成功', icon: 'success' }),
|
||||
fail: () => wx.showToast({ title: '保存失败', icon: 'none' }),
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -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: [],
|
||||
@@ -20,6 +24,7 @@ Page(createPage({
|
||||
dingdanJieshao: '', // 订单介绍
|
||||
dingdanBeizhu: '', // 订单备注
|
||||
laobanName: '', // 老板游戏昵称/UID
|
||||
waibuDingdanId: '', // 外部平台订单号
|
||||
zhidingUid: '', // 指定打手UID
|
||||
jiage: '', // 订单价格
|
||||
|
||||
@@ -42,7 +47,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 +63,9 @@ Page(createPage({
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面时刷新余额(可能从其他页面充值后返回)
|
||||
this.loadShangjiaYue()
|
||||
// 重新拼接背景图(确保最新)
|
||||
this.loadBgImage()
|
||||
|
||||
// 注册通知组件(用于全局消息提示)
|
||||
this.registerNotificationComponent()
|
||||
|
||||
// 重置按钮缩进定时器(重新开始5秒倒计时)
|
||||
this.startTutorialBtnTimer()
|
||||
},
|
||||
|
||||
@@ -87,6 +94,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 +120,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 +141,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
|
||||
})
|
||||
}
|
||||
@@ -189,6 +215,9 @@ Page(createPage({
|
||||
onLaobanNameInput(e) {
|
||||
this.setData({ laobanName: e.detail.value })
|
||||
},
|
||||
onWaibuDingdanIdInput(e) {
|
||||
this.setData({ waibuDingdanId: e.detail.value })
|
||||
},
|
||||
onZhidingUidInput(e) {
|
||||
this.setData({ zhidingUid: e.detail.value })
|
||||
},
|
||||
@@ -282,21 +311,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
|
||||
}
|
||||
|
||||
@@ -309,6 +346,7 @@ Page(createPage({
|
||||
dingdanJieshao: '',
|
||||
dingdanBeizhu: '',
|
||||
laobanName: '',
|
||||
waibuDingdanId: '',
|
||||
zhidingUid: '',
|
||||
jiage: '',
|
||||
selectedLabelId: '',
|
||||
@@ -338,6 +376,7 @@ Page(createPage({
|
||||
dingdanJieshao: this.data.dingdanJieshao.trim(),
|
||||
dingdanBeizhu: this.data.dingdanBeizhu.trim() || '',
|
||||
laobanName: this.data.laobanName.trim(),
|
||||
waibuDingdanId: this.data.waibuDingdanId.trim() || '',
|
||||
zhidingUid: this.data.zhidingUid.trim() || '',
|
||||
jiage: this.data.jiage,
|
||||
// 新增字段
|
||||
@@ -349,29 +388,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>
|
||||
|
||||
<!-- 订单类型 -->
|
||||
@@ -84,6 +84,21 @@
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 拼多多订单号 -->
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
<text class="field-label">拼多多订单号</text>
|
||||
<text class="field-optional">选填</text>
|
||||
</view>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="外部平台订单号"
|
||||
maxlength="64"
|
||||
value="{{waibuDingdanId}}"
|
||||
bindinput="onWaibuDingdanIdInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 指定接单员UID -->
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
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';
|
||||
import { fetchGonggaoLunbo, isGonggaoCacheValid } from '../../utils/display-config';
|
||||
import { ICON_KEYS, resolveMiniappIcon } from '../../utils/miniapp-icons.js';
|
||||
import { fetchMerchantOrderStats, applyOrderStatsToHome } from '../../utils/merchant-order-stats.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
@@ -12,46 +20,106 @@ Page(createPage({
|
||||
statusBar: 20,
|
||||
navBar: 44,
|
||||
isShangjia: false,
|
||||
isStaffMode: false,
|
||||
staffRoleName: '',
|
||||
quotaAvailable: '0.00',
|
||||
canDispatch: true,
|
||||
canStaffManage: true,
|
||||
lunboList: [],
|
||||
lunbozhanwei: '/images/lunbozhanwei.jpg',
|
||||
gonggao: '',
|
||||
swiperCurrent: 0,
|
||||
imgUrls: {
|
||||
noticeIco: 'https://bintao.xmxym88.com/xcx/bintao/38.png',
|
||||
statCardBg: 'https://bintao.xmxym88.com/xcx/bintao/3.png',
|
||||
kefuKeyBtn: 'https://bintao.xmxym88.com/xcx/bintao/13.png',
|
||||
kefuListBtn: 'https://bintao.xmxym88.com/xcx/bintao/21.png',
|
||||
noticeIco: '',
|
||||
statCardBg: '',
|
||||
kefuKeyBtn: '',
|
||||
kefuListBtn: '',
|
||||
iconRegular: '',
|
||||
iconCustom: '',
|
||||
},
|
||||
stats: {
|
||||
pendingCount: 0,
|
||||
pendingAmount: '0.00',
|
||||
todayDispatch: 0,
|
||||
todayRefund: 0,
|
||||
completedCount: 0,
|
||||
completedAmount: '0.00',
|
||||
refundCount: 0,
|
||||
refundAmount: '0.00',
|
||||
dispatchCount: 0,
|
||||
dispatchAmount: '0.00',
|
||||
},
|
||||
recentOrders: [],
|
||||
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();
|
||||
this.waitForConfigAndLoadBanner();
|
||||
syncStaffUi(this);
|
||||
this.setupImageUrls();
|
||||
await this.waitForConfigAndLoadBanner();
|
||||
if (this.data.isShangjia) {
|
||||
await this.loadDashboard(false);
|
||||
}
|
||||
this._merchantHomeSessionReady = true;
|
||||
this._skipShowRefresh = true;
|
||||
this.checkColdStartPopup('shangjiaduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
if (!app.globalData._shangjiaPhoneChecked) {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' });
|
||||
if (!phoneOk) return;
|
||||
app.globalData._shangjiaPhoneChecked = true;
|
||||
}
|
||||
|
||||
this.registerNotificationComponent();
|
||||
// 先用本地缓存渲染,再向服务端同步子客服身份
|
||||
this.checkRole();
|
||||
syncStaffUi(this);
|
||||
if (app.globalData.ossImageUrl || app.globalData.miniappIcons) {
|
||||
this.setupImageUrls();
|
||||
}
|
||||
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();
|
||||
}
|
||||
this.loadBannerData();
|
||||
|
||||
if (this._skipShowRefresh) {
|
||||
this._skipShowRefresh = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const silent = this._merchantHomeSessionReady;
|
||||
if (this.data.isShangjia) {
|
||||
this.loadDashboard(silent);
|
||||
}
|
||||
if (silent) {
|
||||
this.loadBannerData(true, true);
|
||||
} else {
|
||||
this.loadBannerData(false);
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
@@ -65,10 +133,36 @@ Page(createPage({
|
||||
|
||||
waitForConfigAndLoadBanner() {
|
||||
if (app.globalData.ossImageUrl || app.globalData.apiBaseUrl) {
|
||||
this.loadBannerData(true);
|
||||
return;
|
||||
this.setupImageUrls();
|
||||
return this.loadBannerData(true);
|
||||
}
|
||||
setTimeout(() => this.waitForConfigAndLoadBanner(), 100);
|
||||
return new Promise((resolve) => {
|
||||
const tick = () => {
|
||||
if (app.globalData.ossImageUrl || app.globalData.apiBaseUrl) {
|
||||
this.setupImageUrls();
|
||||
this.loadBannerData(true).finally(resolve);
|
||||
} else {
|
||||
setTimeout(tick, 100);
|
||||
}
|
||||
};
|
||||
tick();
|
||||
});
|
||||
},
|
||||
|
||||
setupImageUrls() {
|
||||
const ossBase = app.globalData.ossImageUrl || '';
|
||||
const homeDir = `${ossBase}beijing/shangjiaduan/home/`;
|
||||
const icon = (key, fallback) => resolveMiniappIcon(app, key, fallback);
|
||||
this.setData({
|
||||
imgUrls: {
|
||||
noticeIco: icon(ICON_KEYS.MERCHANT_HOME_NOTICE, `${homeDir}notice.png`),
|
||||
statCardBg: icon(ICON_KEYS.MERCHANT_HOME_STAT_BG, `${ossBase}beijing/shangjiaduan/stat_card_bg.png`),
|
||||
kefuKeyBtn: icon(ICON_KEYS.MERCHANT_HOME_KEFU_KEY, `${homeDir}kefu_key.png`),
|
||||
kefuListBtn: icon(ICON_KEYS.MERCHANT_HOME_KEFU_LIST, `${homeDir}kefu_list.png`),
|
||||
iconRegular: icon(ICON_KEYS.MERCHANT_HOME_REGULAR, `${homeDir}regular_dispatch.png`),
|
||||
iconCustom: icon(ICON_KEYS.MERCHANT_HOME_CUSTOM, `${homeDir}custom_dispatch.png`),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
processImageUrls(urlList) {
|
||||
@@ -78,14 +172,13 @@ Page(createPage({
|
||||
},
|
||||
|
||||
checkRole() {
|
||||
const ok = Number(wx.getStorageSync('shangjiastatus')) === 1;
|
||||
this.setData({ isShangjia: ok });
|
||||
syncStaffUi(this);
|
||||
},
|
||||
|
||||
loadBannerData(forceFetch) {
|
||||
loadBannerData(forceFetch, silent) {
|
||||
const g = app.globalData || {};
|
||||
const cachedList = g.shangpinlunbo || [];
|
||||
if (!forceFetch && cachedList.length) {
|
||||
if (!forceFetch && isGonggaoCacheValid(app) && cachedList.length) {
|
||||
this.setData({
|
||||
lunboList: this.processImageUrls(cachedList),
|
||||
gonggao: g.shangpingonggao || '',
|
||||
@@ -93,45 +186,30 @@ Page(createPage({
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!forceFetch && g.shangpingonggao) {
|
||||
return fetchGonggaoLunbo(app, 'merchant_home').then((data) => {
|
||||
if (!data) return;
|
||||
this.setData({
|
||||
lunboList: this.processImageUrls(cachedList),
|
||||
gonggao: g.shangpingonggao,
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo),
|
||||
gonggao: app.globalData.shangpingonggao || '',
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
const apiBase = g.apiBaseUrl || '';
|
||||
if (!apiBase) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
wx.request({
|
||||
url: `${apiBase}/peizhi/shangpingonggao/`,
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data) {
|
||||
const data = res.data;
|
||||
app.globalData.shangpingonggao = data.shangpingonggao || '';
|
||||
app.globalData.shangpinlunbo = data.shangpinlunbo || [];
|
||||
this.setData({
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo),
|
||||
gonggao: app.globalData.shangpingonggao,
|
||||
});
|
||||
}
|
||||
},
|
||||
complete: resolve,
|
||||
});
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
async loadDashboard() {
|
||||
async loadDashboard(silent = false) {
|
||||
await Promise.all([
|
||||
this.loadShangjiaBrief(),
|
||||
this.loadPendingStats(),
|
||||
this.loadRecentOrders(),
|
||||
this.loadRecentOrders(silent),
|
||||
]);
|
||||
},
|
||||
|
||||
async loadShangjiaBrief() {
|
||||
if (isStaffMode()) {
|
||||
const ctx = await refreshStaffContext(request);
|
||||
syncStaffUi(this);
|
||||
applyStaffStatsToPage(this, ctx);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/shangjiaxinxi',
|
||||
@@ -155,39 +233,24 @@ Page(createPage({
|
||||
|
||||
async loadPendingStats() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: { zhuangtai_list: [8], page: 1, page_size: 20 },
|
||||
});
|
||||
if (res.data && (res.data.code === 0 || res.data.code === 200)) {
|
||||
const data = res.data.data || {};
|
||||
const list = data.list || [];
|
||||
let amount = 0;
|
||||
list.forEach((item) => {
|
||||
const v = parseFloat(item.jine || item.dingdan_jine || 0);
|
||||
if (!Number.isNaN(v)) amount += v;
|
||||
});
|
||||
this.setData({
|
||||
stats: {
|
||||
...this.data.stats,
|
||||
pendingCount: data.pending_count != null ? data.pending_count : list.length,
|
||||
pendingAmount: amount.toFixed(2),
|
||||
},
|
||||
});
|
||||
}
|
||||
const data = await fetchMerchantOrderStats(request, {});
|
||||
applyOrderStatsToHome(this, data);
|
||||
} catch (e) {
|
||||
console.error('待结算统计失败', e);
|
||||
console.warn('待结算统计失败', e);
|
||||
}
|
||||
},
|
||||
|
||||
/** 最近订单:同订单页「全部」筛选,取最新若干条 */
|
||||
async loadRecentOrders() {
|
||||
if (this.data.recentLoading) return;
|
||||
this.setData({ recentLoading: true });
|
||||
async loadRecentOrders(silent = false) {
|
||||
if (this.data.recentLoading && !silent) return;
|
||||
if (silent && this._recentOrdersRequesting) return;
|
||||
if (!silent) {
|
||||
this.setData({ recentLoading: true });
|
||||
}
|
||||
this._recentOrdersRequesting = 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 +269,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,
|
||||
@@ -217,7 +280,10 @@ Page(createPage({
|
||||
} catch (e) {
|
||||
console.error('最近订单加载失败', e);
|
||||
} finally {
|
||||
this.setData({ recentLoading: false });
|
||||
this._recentOrdersRequesting = false;
|
||||
if (!silent) {
|
||||
this.setData({ recentLoading: false });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -225,12 +291,24 @@ Page(createPage({
|
||||
this.setData({ swiperCurrent: e.detail.current });
|
||||
},
|
||||
|
||||
goDispatch() {
|
||||
goRegularDispatch() {
|
||||
wx.navigateTo({ url: '/pages/merchant-regular-dispatch/merchant-regular-dispatch' });
|
||||
},
|
||||
|
||||
goLinkDispatch() {
|
||||
wx.navigateTo({ url: '/pages/express-order/express-order' });
|
||||
},
|
||||
|
||||
goCustomDispatch() {
|
||||
wx.navigateTo({ url: '/pages/merchant-dispatch/merchant-dispatch' });
|
||||
},
|
||||
|
||||
goDispatch() {
|
||||
this.goCustomDispatch();
|
||||
},
|
||||
|
||||
goExpress() {
|
||||
wx.navigateTo({ url: '/pages/express-order/express-order' });
|
||||
this.goLinkDispatch();
|
||||
},
|
||||
|
||||
goOrdersTab() {
|
||||
|
||||
@@ -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>
|
||||
@@ -18,6 +18,11 @@
|
||||
|
||||
<block wx:else>
|
||||
<view class="sj-main-body">
|
||||
<view class="sj-notice-bar" wx:if="{{gonggao}}">
|
||||
<image class="notice-ico" src="{{imgUrls.noticeIco}}" mode="aspectFit"/>
|
||||
<text class="sj-notice-txt">{{gonggao}}</text>
|
||||
</view>
|
||||
|
||||
<view class="swiper-wrap" wx:if="{{lunboList.length}}">
|
||||
<view class="swiper-con">
|
||||
<swiper class="banner-swiper" autoplay circular interval="4000" duration="500" bindchange="onSwiperChange">
|
||||
@@ -34,14 +39,21 @@
|
||||
<image class="slide-image banner-placeholder" src="{{lunbozhanwei}}" mode="aspectFill"/>
|
||||
</view>
|
||||
|
||||
<view class="sj-notice-bar" wx:if="{{gonggao}}">
|
||||
<image class="notice-ico" src="{{imgUrls.noticeIco}}" mode="aspectFit"/>
|
||||
<text class="sj-notice-txt">{{gonggao}}</text>
|
||||
<view class="staff-banner sj-mx20" wx:if="{{isStaffMode}}">
|
||||
<text class="staff-banner-txt">商家客服 · {{staffRoleName}} · 可用额度 ¥{{quotaAvailable}}</text>
|
||||
</view>
|
||||
|
||||
<view class="sj-fadan-row">
|
||||
<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 class="sj-fadan-block" wx:if="{{canDispatch}}">
|
||||
<view class="sj-fadan-row">
|
||||
<view class="sj-fadan-img-btn" style="background-image:url({{imgUrls.iconRegular}})" bindtap="goRegularDispatch"/>
|
||||
<view class="sj-fadan-img-btn" style="background-image:url({{imgUrls.iconCustom}})" bindtap="goCustomDispatch"/>
|
||||
</view>
|
||||
<view class="sj-fadan-row sj-fadan-row--link">
|
||||
<view class="sj-fadan-big-btn sj-fadan-big-btn--link" bindtap="goLinkDispatch">
|
||||
<text class="sj-fadan-big-title">链接派单</text>
|
||||
<text class="sj-fadan-big-sub">模板管理 · 生成链接</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="merchant-stats shadow">
|
||||
@@ -62,6 +74,28 @@
|
||||
<text class="stat-num">{{stats.pendingAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-row flexa stats-row--gap">
|
||||
<view class="stat-col stat-col--l">
|
||||
<text class="stat-lbl">成交总量</text>
|
||||
<text class="stat-num">{{stats.completedCount}}</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-col stat-col--r">
|
||||
<text class="stat-lbl">成交总额(元)</text>
|
||||
<text class="stat-num">{{stats.completedAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-row flexa stats-row--gap">
|
||||
<view class="stat-col stat-col--l">
|
||||
<text class="stat-lbl">退款总量</text>
|
||||
<text class="stat-num">{{stats.refundCount}}</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-col stat-col--r">
|
||||
<text class="stat-lbl">退款总额(元)</text>
|
||||
<text class="stat-num">{{stats.refundAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-row flexa stats-row--gap">
|
||||
<view class="stat-col stat-col--l">
|
||||
<text class="stat-lbl">今日派单(数)</text>
|
||||
@@ -73,7 +107,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"/>
|
||||
|
||||
@@ -92,7 +92,7 @@ page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fdf9db;
|
||||
margin: 0 24rpx;
|
||||
margin: 12rpx 24rpx 0;
|
||||
border-radius: 10rpx;
|
||||
padding: 10rpx 16rpx;
|
||||
}
|
||||
@@ -111,11 +111,22 @@ page {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sj-fadan-block {
|
||||
margin: 16rpx 24rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.sj-fadan-row {
|
||||
display: flex;
|
||||
margin: 24rpx 20rpx 0;
|
||||
padding: 10rpx;
|
||||
gap: 8rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.sj-fadan-row--link {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.sj-fadan-img-btn {
|
||||
@@ -125,18 +136,35 @@ page {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.sj-fadan-img-btn--left {
|
||||
background-image: url('https://bintao.xmxym88.com/xcx/bintao/74.png');
|
||||
margin-right: 8rpx;
|
||||
.sj-fadan-big-btn {
|
||||
flex: 1;
|
||||
height: 128rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.sj-fadan-img-btn--right {
|
||||
background-image: url('https://bintao.xmxym88.com/xcx/bintao/76.png');
|
||||
margin-left: 8rpx;
|
||||
.sj-fadan-big-btn--link {
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
}
|
||||
|
||||
.sj-fadan-big-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
.sj-fadan-big-sub {
|
||||
font-size: 22rpx;
|
||||
color: #886633;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.merchant-stats {
|
||||
margin: 32rpx 25rpx 0;
|
||||
margin: 12rpx 25rpx 0;
|
||||
border-radius: 26rpx;
|
||||
padding-bottom: 24rpx;
|
||||
position: relative;
|
||||
@@ -307,3 +335,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,433 @@
|
||||
/** 客服列表 - 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(); },
|
||||
|
||||
|
||||
|
||||
goBack() { wx.navigateBack(); },
|
||||
|
||||
goStaffOrders(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const label = e.currentTarget.dataset.label || '';
|
||||
wx.redirectTo({
|
||||
url: `/pages/merchant-orders/merchant-orders?staff_member_id=${id}&staff_label=${encodeURIComponent(label)}`,
|
||||
});
|
||||
},
|
||||
|
||||
onEditRemark(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const oldRemark = e.currentTarget.dataset.remark || '';
|
||||
wx.showModal({
|
||||
title: '客服备注',
|
||||
editable: true,
|
||||
placeholderText: '如:晚班小李、财务小王',
|
||||
content: oldRemark,
|
||||
success: async (r) => {
|
||||
if (!r.confirm) return;
|
||||
const remark = (r.content || '').trim();
|
||||
const res = await request({
|
||||
url: STAFF_API.memberUpdateRemark,
|
||||
method: 'POST',
|
||||
data: { member_id: id, merchant_remark: remark },
|
||||
});
|
||||
wx.showToast({
|
||||
title: (res.data && res.data.code === 0) ? '已保存' : (res.data.msg || '失败'),
|
||||
icon: 'none',
|
||||
});
|
||||
this.loadList();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
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,165 @@
|
||||
<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 wx:if="{{item.merchant_remark}}" class="remark-row">备注:{{item.merchant_remark}}</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-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 class="kefu-actions flexa">
|
||||
<text class="act" bindtap="onAction">查看订单</text>
|
||||
<text class="act" bindtap="onAction">权限管理</text>
|
||||
<text class="act act-del" bindtap="onAction">删除</text>
|
||||
<text class="act" bindtap="onEditRemark" data-id="{{item.id}}" data-remark="{{item.merchant_remark}}">备注</text>
|
||||
<text class="act" bindtap="goStaffOrders" data-id="{{item.id}}" data-label="{{item.nickname}}">查看订单</text>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!item.isSelf}}" class="kefu-actions flexa kefu-actions--sub">
|
||||
|
||||
<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,369 @@
|
||||
<!--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>
|
||||
<!-- 拼多多订单号 -->
|
||||
<view class="info-row" wx:if="{{jibenShuju.waibu_dingdan_id}}"><text class="label">拼多多订单号</text><view class="value-with-copy"><text class="value">{{jibenShuju.waibu_dingdan_id}}</text><text class="copy-btn" bindtap="fuzhiWenben" data-text="{{jibenShuju.waibu_dingdan_id}}">📋</text></view></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="sj-action-btn sj-action-btn--gold" bindtap="showCancelTuikuan">撤销退款申请</view>
|
||||
<view class="sj-action-btn sj-action-btn--outline" 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}}">
|
||||
<block wx:if="{{fadanData.zhuangtai == 1 || fadanData.zhuangtai == 3}}">
|
||||
<view class="sj-action-btn sj-action-btn--gold" bindtap="showModifyFakuan">修改罚款</view>
|
||||
<view class="sj-action-btn sj-action-btn--outline" bindtap="showCancelFakuan">撤销罚款</view>
|
||||
</block>
|
||||
<block wx:if="{{fadanData.zhuangtai == 4}}">
|
||||
<view class="sj-action-btn sj-action-btn--gold sj-action-btn--full" 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 sj-action-btn sj-action-btn--gold sj-action-btn--full" bindtap="showJiesuanConfirm">立即结算</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定操作栏 -->
|
||||
<view class="bottom-bar" wx:if="{{!isLoading}}">
|
||||
<view class="bottom-btn-group">
|
||||
<view class="sj-action-btn sj-action-btn--outline" bindtap="goToKefu">联系客服</view>
|
||||
<view class="sj-action-btn sj-action-btn--outline" bindtap="goToChatWithDashou" wx:if="{{dashouInfo.yonghuid}}">联系接单员</view>
|
||||
</view>
|
||||
<view class="bottom-btn-group">
|
||||
<view class="sj-action-btn sj-action-btn--warn" bindtap="showChexiaoBtn" wx:if="{{canCancel && (jibenShuju.zhuangtai == 1 || jibenShuju.zhuangtai == 7)}}">撤销订单</view>
|
||||
<view class="sj-action-btn sj-action-btn--gold" bindtap="showGhDashouConfirm" wx:if="{{canChangePlayer && (jibenShuju.zhuangtai == 2 || jibenShuju.zhuangtai == 8) && dashouInfo.yonghuid}}">更换接单员</view>
|
||||
<view class="sj-action-btn sj-action-btn--outline" bindtap="showTuikuanBtn" wx:if="{{canRefund && (jibenShuju.zhuangtai == 2 || jibenShuju.zhuangtai == 8)}}">申请退款</view>
|
||||
<view class="sj-action-btn sj-action-btn--gold" bindtap="showFakuanModal" wx:if="{{canShowPenaltyApply}}">申请罚款</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>
|
||||
@@ -1,185 +1,214 @@
|
||||
/* pages/merchant-order-detail/merchant-order-detail.wxss - 商家订单详情页样式(正方形九宫格,圆形头像) */
|
||||
/* 页面全局背景 */
|
||||
page { background: #f5f7fa; }
|
||||
/* 页面底部留出固定栏的空间 */
|
||||
.sjddxq-page { padding-bottom: 160rpx; }
|
||||
|
||||
/* 加载状态居中布局 */
|
||||
.loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 60vh; }
|
||||
/* 加载旋转动画图标 */
|
||||
.loading-spinner { width: 64rpx; height: 64rpx; border: 6rpx solid #e0e0e0; border-top-color: #4caf50; border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 20rpx; }
|
||||
|
||||
/* 加载文字 */
|
||||
.loading-text { color: #666; font-size: 28rpx; }
|
||||
|
||||
/* 内容区域的内边距 */
|
||||
.content-container { padding: 24rpx; }
|
||||
|
||||
/* 通用卡片样式 */
|
||||
.card { background: #fff; border-radius: 24rpx; padding: 28rpx; margin-bottom: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.04); }
|
||||
/* 卡片标题样式 */
|
||||
.card-title { font-size: 32rpx; font-weight: 600; color: #1a1a1a; margin-bottom: 20rpx; padding-bottom: 12rpx; border-bottom: 1rpx solid #f0f0f0; }
|
||||
|
||||
/* 商品卡片:左右布局 */
|
||||
.shangpin-card { display: flex; align-items: center; }
|
||||
/* 商品图片:固定宽高为正方形 */
|
||||
.shangpin-img { width: 180rpx; height: 180rpx; border-radius: 16rpx; background: #f5f5f5; margin-right: 24rpx; flex-shrink: 0; }
|
||||
.shangpin-text { flex: 1; }
|
||||
.jieshao-text { font-size: 28rpx; color: #333; line-height: 1.6; }
|
||||
|
||||
/* 信息行:左右分布,底部虚线 */
|
||||
.info-row { display: flex; justify-content: space-between; align-items: center; padding: 14rpx 0; border-bottom: 1rpx dashed #f5f5f5; }
|
||||
.info-row:last-child { border-bottom: none; }
|
||||
.label { font-size: 28rpx; color: #888; min-width: 140rpx; }
|
||||
.value { font-size: 28rpx; color: #333; text-align: right; word-break: break-all; }
|
||||
.value-with-copy { display: flex; align-items: center; }
|
||||
/* 复制按钮 */
|
||||
.copy-btn { margin-left: 12rpx; color: #4caf50; font-size: 26rpx; padding: 4rpx 12rpx; background: #e8f5e9; border-radius: 16rpx; }
|
||||
/* 红色价格 */
|
||||
.price-red { color: #ff4444; font-weight: 600; }
|
||||
.zhuangtai-text { font-weight: 600; }
|
||||
|
||||
/* 打手卡片头部 */
|
||||
.dashou-header { display: flex; align-items: center; }
|
||||
/* 打手头像:固定宽高,圆形 */
|
||||
.dashou-avatar { width: 88rpx; height: 88rpx; border-radius: 50%; margin-right: 20rpx; }
|
||||
.dashou-detail { flex: 1; }
|
||||
.dashou-nicheng { font-size: 30rpx; font-weight: 500; margin-bottom: 6rpx; }
|
||||
.dashou-id-row { display: flex; align-items: center; font-size: 26rpx; }
|
||||
|
||||
/* 服务详情子标题 */
|
||||
.sub-title { font-size: 28rpx; color: #555; margin: 16rpx 0 8rpx; }
|
||||
/* 内容框(留言、理由等) */
|
||||
.content-box { background: #f9f9f9; border-radius: 12rpx; padding: 16rpx; font-size: 26rpx; color: #444; line-height: 1.5; margin-bottom: 16rpx; }
|
||||
|
||||
/* ========= 核心:交付图片九宫格,强制正方形 ========= */
|
||||
.image-grid { display: flex; flex-wrap: wrap; margin-bottom: 16rpx; }
|
||||
/* 每个图片的外层容器 */
|
||||
.grid-img-wrapper {
|
||||
width: calc(33.33% - 8rpx); /* 每行三个,减去间距 */
|
||||
margin: 4rpx;
|
||||
position: relative; /* 为绝对定位的图片提供锚点 */
|
||||
overflow: hidden;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
/* 使用伪元素撑出 1:1 的正方形高度 */
|
||||
.grid-img-wrapper::after {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-bottom: 100%; /* 100% 相对于自身宽度,实现正方形 */
|
||||
}
|
||||
/* 图片绝对定位填满整个容器 */
|
||||
.grid-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* 保持比例裁剪,不变形 */
|
||||
}
|
||||
|
||||
/* 结算区域:星星评分 */
|
||||
.star-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20rpx; }
|
||||
.stars { display: flex; }
|
||||
.star { font-size: 48rpx; color: #ccc; margin-left: 8rpx; transition: all 0.2s; }
|
||||
.star-active { color: #ffc107; transform: scale(1.1); }
|
||||
/* 留言输入框 */
|
||||
.liuyan-input { width: 100%; height: 120rpx; background: #f9f9f9; border-radius: 12rpx; padding: 16rpx; font-size: 28rpx; margin-bottom: 20rpx; }
|
||||
/* 结算按钮 */
|
||||
.jiesuan-btn { background: #4caf50; color: #fff; text-align: center; padding: 20rpx; border-radius: 48rpx; font-size: 30rpx; font-weight: 600; margin-top: 10rpx; }
|
||||
|
||||
/* 底部固定操作栏 */
|
||||
.bottom-bar { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; padding: 16rpx 24rpx; box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.06); display: flex; flex-direction: column; gap: 12rpx; z-index: 100; }
|
||||
.bottom-btn-group { display: flex; gap: 16rpx; }
|
||||
.btn-outline { flex: 1; text-align: center; padding: 18rpx 0; border-radius: 44rpx; font-size: 28rpx; font-weight: 500; border: 2rpx solid #1976D2; color: #1976D2; background: #fff; }
|
||||
.btn-primary { background: #2196F3; color: #fff; flex: 1; text-align: center; padding: 18rpx 0; border-radius: 44rpx; font-weight: 600; font-size: 28rpx; }
|
||||
.btn-warn { background: #ff9800; color: #fff; flex: 1; text-align: center; padding: 18rpx 0; border-radius: 44rpx; font-weight: 600; font-size: 28rpx; }
|
||||
.btn-danger { background: #f44336; color: #fff; flex: 1; text-align: center; padding: 18rpx 0; border-radius: 44rpx; font-weight: 600; font-size: 28rpx; }
|
||||
|
||||
/* 弹窗通用样式 */
|
||||
.modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; }
|
||||
.modal-mask { position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.45); }
|
||||
.modal-content { position: relative; width: 620rpx; background: #fff; border-radius: 32rpx; overflow: hidden; }
|
||||
.modal-hd { padding: 32rpx; font-size: 34rpx; font-weight: 600; display: flex; justify-content: space-between; border-bottom: 1rpx solid #f0f0f0; }
|
||||
.modal-close { color: #999; font-size: 40rpx; }
|
||||
.modal-body { padding: 32rpx; }
|
||||
.modal-tip { font-size: 28rpx; color: #555; margin-bottom: 12rpx; }
|
||||
.modal-input { width: 100%; height: 140rpx; background: #f5f5f5; border-radius: 12rpx; padding: 16rpx; font-size: 28rpx; margin-bottom: 20rpx; }
|
||||
.modal-input-num { width: 100%; height: 80rpx; background: #f5f5f5; border-radius: 12rpx; padding: 0 20rpx; font-size: 32rpx; margin-bottom: 20rpx; }
|
||||
.toggle-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
|
||||
.toggle { width: 100rpx; height: 50rpx; border-radius: 50rpx; background: #e0e0e0; display: flex; align-items: center; padding: 0 4rpx; transition: background 0.2s; }
|
||||
.toggle-on { background: #4caf50; }
|
||||
.toggle-knob { width: 42rpx; height: 42rpx; border-radius: 50%; background: #fff; transition: transform 0.2s; }
|
||||
.toggle-on .toggle-knob { transform: translateX(50rpx); }
|
||||
.fenhong-tip { text-align: center; font-size: 28rpx; color: #ff9800; margin: 16rpx 0 0; }
|
||||
.modal-btns { display: flex; border-top: 1rpx solid #f0f0f0; }
|
||||
.modal-btn { flex: 1; text-align: center; padding: 28rpx; font-size: 30rpx; font-weight: 500; }
|
||||
.modal-btn.cancel { color: #888; background: #f5f5f5; }
|
||||
.modal-btn.confirm { color: #4caf50; background: #e8f5e9; }
|
||||
|
||||
|
||||
|
||||
|
||||
/* ========== 新增:罚单/订单操作按钮样式 ========== */
|
||||
.fakuan-actions, .refund-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 24rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 16rpx 0;
|
||||
border-radius: 44rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.action-modify {
|
||||
background: #2196F3;
|
||||
color: #fff;
|
||||
}
|
||||
.action-cancel {
|
||||
background: #ff9800;
|
||||
color: #fff;
|
||||
}
|
||||
.action-resubmit {
|
||||
background: #4caf50;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ========== 新增:教程按钮样式 ========== */
|
||||
.tutorial-btn {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 20%;
|
||||
transform: translateY(-50%);
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
backdrop-filter: blur(15rpx);
|
||||
border-radius: 60rpx 0 0 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10001;
|
||||
box-shadow: -8rpx 0 20rpx rgba(0,0,0,0.3);
|
||||
transition: right 0.3s ease-out;
|
||||
border: 2rpx solid rgba(255,215,0,0.5);
|
||||
border-right: none;
|
||||
}
|
||||
.tutorial-btn-text {
|
||||
color: #FFD700;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
writing-mode: vertical-rl;
|
||||
letter-spacing: 6rpx;
|
||||
}
|
||||
.tutorial-btn-hidden {
|
||||
right: -80rpx;
|
||||
}
|
||||
.tutorial-btn-hover {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
transform: translateY(-50%) scale(1.05);
|
||||
/* pages/merchant-order-detail/merchant-order-detail.wxss - 商家订单详情(逍遥梦金色主题) */
|
||||
@import '../../styles/shangjia-xym-common.wxss';
|
||||
|
||||
page { background: #fff8e1; }
|
||||
.sjddxq-page { padding-bottom: calc(200rpx + env(safe-area-inset-bottom)); }
|
||||
|
||||
/* 加载状态居中布局 */
|
||||
.loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 60vh; }
|
||||
/* 加载旋转动画图标 */
|
||||
.loading-spinner { width: 64rpx; height: 64rpx; border: 6rpx solid #e0e0e0; border-top-color: #4caf50; border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 20rpx; }
|
||||
|
||||
/* 加载文字 */
|
||||
.loading-text { color: #666; font-size: 28rpx; }
|
||||
|
||||
/* 内容区域的内边距 */
|
||||
.content-container { padding: 24rpx; }
|
||||
|
||||
/* 通用卡片样式 */
|
||||
.card { background: #fdfcfa; border-radius: 24rpx; padding: 28rpx; margin-bottom: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.06); border: 1rpx solid rgba(245, 213, 99, 0.25); }
|
||||
/* 卡片标题样式 */
|
||||
.card-title { font-size: 32rpx; font-weight: 600; color: #1a1a1a; margin-bottom: 20rpx; padding-bottom: 12rpx; border-bottom: 1rpx solid #f0f0f0; }
|
||||
|
||||
/* 商品卡片:左右布局 */
|
||||
.shangpin-card { display: flex; align-items: center; }
|
||||
/* 商品图片:固定宽高为正方形 */
|
||||
.shangpin-img { width: 180rpx; height: 180rpx; border-radius: 16rpx; background: #f5f5f5; margin-right: 24rpx; flex-shrink: 0; }
|
||||
.shangpin-text { flex: 1; }
|
||||
.jieshao-text { font-size: 28rpx; color: #333; line-height: 1.6; }
|
||||
|
||||
/* 信息行:左右分布,底部虚线 */
|
||||
.info-row { display: flex; justify-content: space-between; align-items: center; padding: 14rpx 0; border-bottom: 1rpx dashed #f5f5f5; }
|
||||
.info-row:last-child { border-bottom: none; }
|
||||
.label { font-size: 28rpx; color: #888; min-width: 140rpx; }
|
||||
.value { font-size: 28rpx; color: #333; text-align: right; word-break: break-all; }
|
||||
.value-with-copy { display: flex; align-items: center; }
|
||||
/* 复制按钮 */
|
||||
.copy-btn { margin-left: 12rpx; color: #4caf50; font-size: 26rpx; padding: 4rpx 12rpx; background: #e8f5e9; border-radius: 16rpx; }
|
||||
/* 红色价格 */
|
||||
.price-red { color: #ff4444; font-weight: 600; }
|
||||
.zhuangtai-text { font-weight: 600; }
|
||||
|
||||
/* 打手卡片头部 */
|
||||
.dashou-header { display: flex; align-items: center; }
|
||||
/* 打手头像:固定宽高,圆形 */
|
||||
.dashou-avatar { width: 88rpx; height: 88rpx; border-radius: 50%; margin-right: 20rpx; }
|
||||
.dashou-detail { flex: 1; }
|
||||
.dashou-nicheng { font-size: 30rpx; font-weight: 500; margin-bottom: 6rpx; }
|
||||
.dashou-id-row { display: flex; align-items: center; font-size: 26rpx; }
|
||||
|
||||
/* 服务详情子标题 */
|
||||
.sub-title { font-size: 28rpx; color: #555; margin: 16rpx 0 8rpx; }
|
||||
/* 内容框(留言、理由等) */
|
||||
.content-box { background: #f9f9f9; border-radius: 12rpx; padding: 16rpx; font-size: 26rpx; color: #444; line-height: 1.5; margin-bottom: 16rpx; }
|
||||
|
||||
/* ========= 核心:交付图片九宫格,强制正方形 ========= */
|
||||
.image-grid { display: flex; flex-wrap: wrap; margin-bottom: 16rpx; }
|
||||
/* 每个图片的外层容器 */
|
||||
.grid-img-wrapper {
|
||||
width: calc(33.33% - 8rpx); /* 每行三个,减去间距 */
|
||||
margin: 4rpx;
|
||||
position: relative; /* 为绝对定位的图片提供锚点 */
|
||||
overflow: hidden;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
/* 使用伪元素撑出 1:1 的正方形高度 */
|
||||
.grid-img-wrapper::after {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-bottom: 100%; /* 100% 相对于自身宽度,实现正方形 */
|
||||
}
|
||||
/* 图片绝对定位填满整个容器 */
|
||||
.grid-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* 保持比例裁剪,不变形 */
|
||||
}
|
||||
|
||||
/* 结算区域:星星评分 */
|
||||
.star-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20rpx; }
|
||||
.stars { display: flex; }
|
||||
.star { font-size: 48rpx; color: #ccc; margin-left: 8rpx; transition: all 0.2s; }
|
||||
.star-active { color: #ffc107; transform: scale(1.1); }
|
||||
/* 留言输入框 */
|
||||
.liuyan-input { width: 100%; height: 120rpx; background: #f9f9f9; border-radius: 12rpx; padding: 16rpx; font-size: 28rpx; margin-bottom: 20rpx; }
|
||||
/* 结算按钮(卡片内) */
|
||||
.jiesuan-btn { margin-top: 10rpx; }
|
||||
|
||||
/* 底部固定操作栏 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(180deg, rgba(255, 248, 225, 0.96), #fff8e1 30%);
|
||||
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -8rpx 28rpx rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
z-index: 100;
|
||||
border-top: 1rpx solid rgba(245, 213, 99, 0.45);
|
||||
}
|
||||
|
||||
.bottom-btn-group {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sj-action-btn {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
padding: 22rpx 12rpx;
|
||||
border-radius: 60rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sj-action-btn--gold {
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
border: 2rpx solid #fff;
|
||||
box-shadow: 0 6rpx 18rpx rgba(245, 213, 99, 0.35);
|
||||
}
|
||||
|
||||
.sj-action-btn--outline {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: #492f00;
|
||||
border: 2rpx solid #f5d563;
|
||||
}
|
||||
|
||||
.sj-action-btn--warn {
|
||||
background: linear-gradient(180deg, #ffb74d, #ff8a65);
|
||||
color: #492f00;
|
||||
border: 2rpx solid #fff;
|
||||
box-shadow: 0 6rpx 16rpx rgba(255, 138, 101, 0.28);
|
||||
}
|
||||
|
||||
.sj-action-btn--full {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
/* 弹窗通用样式 */
|
||||
.modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; }
|
||||
.modal-mask { position: absolute; width: 100%; height: 100%; background: rgba(0,0,0,0.45); }
|
||||
.modal-content { position: relative; width: 620rpx; background: #fdfcfa; border-radius: 32rpx; overflow: hidden; border: 2rpx solid rgba(245, 213, 99, 0.4); }
|
||||
.modal-hd { padding: 32rpx; font-size: 34rpx; font-weight: 700; color: #492f00; display: flex; justify-content: space-between; border-bottom: 1rpx solid rgba(245, 213, 99, 0.35); }
|
||||
.modal-close { color: #999; font-size: 40rpx; }
|
||||
.modal-body { padding: 32rpx; }
|
||||
.modal-tip { font-size: 28rpx; color: #555; margin-bottom: 12rpx; }
|
||||
.modal-input { width: 100%; height: 140rpx; background: #f5f5f5; border-radius: 12rpx; padding: 16rpx; font-size: 28rpx; margin-bottom: 20rpx; }
|
||||
.modal-input-num { width: 100%; height: 80rpx; background: #f5f5f5; border-radius: 12rpx; padding: 0 20rpx; font-size: 32rpx; margin-bottom: 20rpx; }
|
||||
.toggle-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
|
||||
.toggle { width: 100rpx; height: 50rpx; border-radius: 50rpx; background: #e0e0e0; display: flex; align-items: center; padding: 0 4rpx; transition: background 0.2s; }
|
||||
.toggle-on { background: #4caf50; }
|
||||
.toggle-knob { width: 42rpx; height: 42rpx; border-radius: 50%; background: #fff; transition: transform 0.2s; }
|
||||
.toggle-on .toggle-knob { transform: translateX(50rpx); }
|
||||
.fenhong-tip { text-align: center; font-size: 28rpx; color: #ff9800; margin: 16rpx 0 0; }
|
||||
.modal-btns { display: flex; border-top: 1rpx solid #f0f0f0; }
|
||||
.modal-btn { flex: 1; text-align: center; padding: 28rpx; font-size: 30rpx; font-weight: 500; }
|
||||
.modal-btn.cancel { color: #886633; background: #fff8e1; }
|
||||
.modal-btn.confirm { color: #492f00; background: linear-gradient(180deg, #fae04d, #ffc0a3); font-weight: 700; }
|
||||
|
||||
|
||||
|
||||
|
||||
/* 罚单/退款操作按钮 */
|
||||
.fakuan-actions, .refund-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 24rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx dashed rgba(245, 213, 99, 0.5);
|
||||
}
|
||||
|
||||
/* ========== 新增:教程按钮样式 ========== */
|
||||
.tutorial-btn {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 20%;
|
||||
transform: translateY(-50%);
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
backdrop-filter: blur(15rpx);
|
||||
border-radius: 60rpx 0 0 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10001;
|
||||
box-shadow: -8rpx 0 20rpx rgba(0,0,0,0.3);
|
||||
transition: right 0.3s ease-out;
|
||||
border: 2rpx solid rgba(255,215,0,0.5);
|
||||
border-right: none;
|
||||
}
|
||||
.tutorial-btn-text {
|
||||
color: #FFD700;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
writing-mode: vertical-rl;
|
||||
letter-spacing: 6rpx;
|
||||
}
|
||||
.tutorial-btn-hidden {
|
||||
right: -80rpx;
|
||||
}
|
||||
.tutorial-btn-hover {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
transform: translateY(-50%) scale(1.05);
|
||||
}
|
||||
@@ -3,17 +3,36 @@ 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';
|
||||
import { normalizeOrderTags } from '../../utils/order-tags.js';
|
||||
import { ICON_KEYS, resolveMiniappIcon, MERCHANT_GOLD_BANNER_FALLBACK } from '../../utils/miniapp-icons.js';
|
||||
import { fetchMerchantOrderStats } from '../../utils/merchant-order-stats.js';
|
||||
|
||||
const EMPTY_TAB = { list: [], page: 1, hasMore: true, isLoading: false };
|
||||
|
||||
function buildEmptyStatusData() {
|
||||
return {
|
||||
all: { ...EMPTY_TAB },
|
||||
jinxingzhong: { ...EMPTY_TAB },
|
||||
tuikuanzhong: { ...EMPTY_TAB },
|
||||
yituikuan: { ...EMPTY_TAB },
|
||||
tuikuanshibai: { ...EMPTY_TAB },
|
||||
yiwancheng: { ...EMPTY_TAB },
|
||||
jiesuanzhong: { ...EMPTY_TAB },
|
||||
};
|
||||
}
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 商品类型(和打手端用同一个接口)
|
||||
shangpinleixing: [],
|
||||
xuanzhongLeixingId: null,
|
||||
|
||||
// 搜索关键字(模糊搜索)
|
||||
searchKeyword: '',
|
||||
|
||||
// 状态列表(含全部)
|
||||
timeStart: '',
|
||||
timeEnd: '',
|
||||
|
||||
statusList: [
|
||||
{ name: '全部', key: 'all', zhuangtaiList: [1, 2, 3, 4, 5, 6, 7, 8], color: '#333333' },
|
||||
{ name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' },
|
||||
@@ -21,75 +40,210 @@ Page(createPage({
|
||||
{ name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' },
|
||||
{ name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' },
|
||||
{ name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' },
|
||||
{ name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' }
|
||||
{ name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' },
|
||||
],
|
||||
currentStatusKey: 'all',
|
||||
|
||||
// 每个状态独立的数据集
|
||||
sjDingdanShuju: {
|
||||
all: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
tuikuanzhong: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
yituikuan: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
tuikuanshibai: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }
|
||||
},
|
||||
sjDingdanShuju: buildEmptyStatusData(),
|
||||
|
||||
currentList: [],
|
||||
hasMore: true,
|
||||
isLoading: false,
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false,
|
||||
listScrollTop: 0,
|
||||
defaultImg: '/images/default-order.png',
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
|
||||
// 待结算数量
|
||||
pendingCount: 0,
|
||||
goldBannerUrl: '',
|
||||
staffList: [],
|
||||
selectedStaffMemberId: '',
|
||||
selectedStaffLabel: '全部客服',
|
||||
canViewFinance: true,
|
||||
orderStats: {
|
||||
pending_count: 0,
|
||||
pending_amount: '0.00',
|
||||
completed_count: 0,
|
||||
completed_amount: '0.00',
|
||||
refund_count: 0,
|
||||
refund_amount: '0.00',
|
||||
dispatch_count: 0,
|
||||
dispatch_amount: '0.00',
|
||||
},
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
wx.setNavigationBarTitle({ title: '我的派单' });
|
||||
onLoad(options) {
|
||||
this._listInitialized = false;
|
||||
this._savedScrollTop = 0;
|
||||
this._skipShowRefresh = false;
|
||||
this.setData({
|
||||
goldBannerUrl: resolveMiniappIcon(app, ICON_KEYS.MERCHANT_GOLD_BANNER, MERCHANT_GOLD_BANNER_FALLBACK),
|
||||
});
|
||||
wx.setNavigationBarTitle({ title: isStaffMode() ? '客服派单' : '我的派单' });
|
||||
syncStaffUi(this);
|
||||
if (isStaffMode()) {
|
||||
refreshStaffContext(request).then(() => syncStaffUi(this));
|
||||
}
|
||||
this.loadShangpinLeixing();
|
||||
this.registerNotificationComponent();
|
||||
if (options && options.staff_member_id) {
|
||||
this.setData({
|
||||
selectedStaffMemberId: String(options.staff_member_id),
|
||||
selectedStaffLabel: options.staff_label || '指定客服',
|
||||
});
|
||||
}
|
||||
if (!isStaffMode()) {
|
||||
this.loadStaffList();
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' });
|
||||
if (!phoneOk) return;
|
||||
|
||||
this.registerNotificationComponent();
|
||||
this.loadPendingCount();
|
||||
this.loadOrderStats();
|
||||
if (wx.getStorageSync('uid')) {
|
||||
reconnectForRole('shangjia');
|
||||
if (app.startImWhenReady) app.startImWhenReady();
|
||||
}
|
||||
if (this.data.currentStatusKey && this.data.xuanzhongLeixingId) {
|
||||
this.loadCurrentStatusOrders(true);
|
||||
|
||||
if (this._skipShowRefresh) {
|
||||
this._skipShowRefresh = false;
|
||||
this.restoreListScroll();
|
||||
return;
|
||||
}
|
||||
|
||||
// 从详情返回:保持列表与滚动位置,不整页重载
|
||||
if (this._listInitialized) {
|
||||
this.restoreListScroll();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
restoreListScroll() {
|
||||
const top = this._savedScrollTop || 0;
|
||||
if (top <= 0) return;
|
||||
this.setData({ listScrollTop: 0 });
|
||||
wx.nextTick(() => {
|
||||
this.setData({ listScrollTop: top });
|
||||
});
|
||||
},
|
||||
|
||||
onListScroll(e) {
|
||||
this._savedScrollTop = e.detail.scrollTop || 0;
|
||||
},
|
||||
|
||||
buildListParams(key, page) {
|
||||
const statusItem = this.data.statusList.find((s) => s.key === key);
|
||||
const params = {
|
||||
zhuangtai_list: statusItem.zhuangtaiList,
|
||||
page,
|
||||
page_size: 5,
|
||||
leixing_id: this.data.xuanzhongLeixingId,
|
||||
keyword: this.data.searchKeyword || undefined,
|
||||
};
|
||||
const { timeStart, timeEnd } = this.data;
|
||||
if (timeStart) params.start_time = `${timeStart} 00:00:00`;
|
||||
if (timeEnd) params.end_time = `${timeEnd} 23:59:59`;
|
||||
if (this.data.selectedStaffMemberId) {
|
||||
params.staff_member_id = this.data.selectedStaffMemberId;
|
||||
}
|
||||
return params;
|
||||
},
|
||||
|
||||
async loadStaffList() {
|
||||
try {
|
||||
const res = await request({ url: STAFF_API.memberList, method: 'POST' });
|
||||
if (res.data && res.data.code === 0) {
|
||||
const list = (res.data.data && res.data.data.list) || [];
|
||||
this.setData({ staffList: list });
|
||||
}
|
||||
} catch (e) {
|
||||
/* 非老板无权限时静默 */
|
||||
}
|
||||
},
|
||||
|
||||
onStaffFilterChange(e) {
|
||||
const idx = Number(e.detail.value);
|
||||
const list = this.data.staffList || [];
|
||||
const row = list[idx];
|
||||
if (!row) return;
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({
|
||||
selectedStaffMemberId: String(row.id),
|
||||
selectedStaffLabel: row.nickname || row.uid,
|
||||
listScrollTop: 0,
|
||||
});
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
this.loadOrderStats();
|
||||
},
|
||||
|
||||
clearStaffFilter() {
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({
|
||||
selectedStaffMemberId: '',
|
||||
selectedStaffLabel: '全部客服',
|
||||
listScrollTop: 0,
|
||||
});
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
this.loadOrderStats();
|
||||
},
|
||||
|
||||
async loadOrderStats() {
|
||||
try {
|
||||
const params = {};
|
||||
const { timeStart, timeEnd, selectedStaffMemberId, xuanzhongLeixingId } = this.data;
|
||||
if (timeStart) params.start_time = `${timeStart} 00:00:00`;
|
||||
if (timeEnd) params.end_time = `${timeEnd} 23:59:59`;
|
||||
if (selectedStaffMemberId) params.staff_member_id = selectedStaffMemberId;
|
||||
if (xuanzhongLeixingId) params.leixing_id = xuanzhongLeixingId;
|
||||
|
||||
const data = await fetchMerchantOrderStats(request, params);
|
||||
const order = data.order || {};
|
||||
this.setData({
|
||||
orderStats: {
|
||||
pending_count: order.pending_count || 0,
|
||||
pending_amount: order.pending_amount || '0.00',
|
||||
completed_count: order.completed_count || 0,
|
||||
completed_amount: order.completed_amount || '0.00',
|
||||
refund_count: order.refund_count || 0,
|
||||
refund_amount: order.refund_amount || '0.00',
|
||||
dispatch_count: order.dispatch_count || 0,
|
||||
dispatch_amount: order.dispatch_amount || '0.00',
|
||||
},
|
||||
pendingCount: order.pending_count || 0,
|
||||
canViewFinance: data.can_view_finance !== false,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('订单统计失败', e);
|
||||
}
|
||||
},
|
||||
|
||||
// 🆕 加载商品类型(和打手端用同一个接口 /dingdan/dsqdhqddlx)
|
||||
async loadShangpinLeixing() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/dsqdhqddlx',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' }
|
||||
header: { 'content-type': 'application/json' },
|
||||
});
|
||||
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
|
||||
let list = res.data.data.list || res.data.data || [];
|
||||
if (!Array.isArray(list)) list = [];
|
||||
const oss = this.data.ossImageUrl;
|
||||
// 拼接图片完整URL
|
||||
const processed = list.map(item => ({
|
||||
const processed = list.map((item) => ({
|
||||
...item,
|
||||
full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http')
|
||||
? oss + item.tupian_url
|
||||
: (item.tupian_url || '/images/default-type.png')
|
||||
: (item.tupian_url || '/images/default-type.png'),
|
||||
}));
|
||||
this.setData({
|
||||
shangpinleixing: processed,
|
||||
xuanzhongLeixingId: processed[0]?.id || null
|
||||
xuanzhongLeixingId: processed[0]?.id || null,
|
||||
});
|
||||
// 初始加载全部状态订单
|
||||
this.loadCurrentStatusOrders(true);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -97,16 +251,16 @@ Page(createPage({
|
||||
}
|
||||
},
|
||||
|
||||
// 选择商品类型
|
||||
selectLeixing(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
if (id === this.data.xuanzhongLeixingId) return;
|
||||
this.setData({ xuanzhongLeixingId: id });
|
||||
this.resetCurrentStatusData();
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ xuanzhongLeixingId: id, listScrollTop: 0 });
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
this.loadOrderStats();
|
||||
},
|
||||
|
||||
// 搜索输入(防抖)
|
||||
onSearchInput(e) {
|
||||
this.setData({ searchKeyword: e.detail.value });
|
||||
if (this._searchTimer) clearTimeout(this._searchTimer);
|
||||
@@ -115,26 +269,76 @@ Page(createPage({
|
||||
}, 500);
|
||||
},
|
||||
|
||||
// 搜索确认
|
||||
onSearchConfirm() {
|
||||
this.resetCurrentStatusData();
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ listScrollTop: 0 });
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
this.loadOrderStats();
|
||||
},
|
||||
|
||||
// 清除搜索
|
||||
clearSearch() {
|
||||
this.setData({ searchKeyword: '' });
|
||||
this.resetCurrentStatusData();
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ searchKeyword: '', listScrollTop: 0 });
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
},
|
||||
|
||||
// 切换状态Tab
|
||||
onTimeStartChange(e) {
|
||||
this.setData({ timeStart: e.detail.value }, () => this.triggerTimeFilterReload());
|
||||
},
|
||||
|
||||
onTimeEndChange(e) {
|
||||
this.setData({ timeEnd: e.detail.value }, () => this.triggerTimeFilterReload());
|
||||
},
|
||||
|
||||
triggerTimeFilterReload() {
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ listScrollTop: 0 });
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
},
|
||||
|
||||
clearTimeFilter() {
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ timeStart: '', timeEnd: '', listScrollTop: 0 });
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
this.loadOrderStats();
|
||||
},
|
||||
|
||||
pickQuickRange(e) {
|
||||
const days = Number(e.currentTarget.dataset.days);
|
||||
if (!days) {
|
||||
this.clearTimeFilter();
|
||||
return;
|
||||
}
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setDate(end.getDate() - (days - 1));
|
||||
const fmt = (d) => {
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
return `${y}-${m}-${day}`;
|
||||
};
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({
|
||||
timeStart: fmt(start),
|
||||
timeEnd: fmt(end),
|
||||
listScrollTop: 0,
|
||||
});
|
||||
this.resetAllStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
this.loadOrderStats();
|
||||
},
|
||||
|
||||
switchStatus(e) {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
if (key === this.data.currentStatusKey) return;
|
||||
this.setData({ currentStatusKey: key });
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ currentStatusKey: key, listScrollTop: 0 });
|
||||
const tabData = this.data.sjDingdanShuju[key];
|
||||
// 如果该状态没有数据,则加载;否则只刷新视图
|
||||
if (tabData.list.length === 0 && tabData.hasMore && !tabData.isLoading) {
|
||||
this.loadCurrentStatusOrders(true);
|
||||
} else {
|
||||
@@ -142,67 +346,62 @@ Page(createPage({
|
||||
}
|
||||
},
|
||||
|
||||
// 🆕 加载订单(核心方法,接口固定用商家订单接口 /dingdan/sjdingdanhq)
|
||||
async loadCurrentStatusOrders(isRefresh = false) {
|
||||
async loadCurrentStatusOrders(isRefresh = false, pageOverride = null) {
|
||||
const key = this.data.currentStatusKey;
|
||||
const tabData = this.data.sjDingdanShuju[key];
|
||||
if (tabData.isLoading) return;
|
||||
const page = isRefresh ? 1 : tabData.page;
|
||||
|
||||
const page = isRefresh ? 1 : (pageOverride || tabData.page + 1);
|
||||
if (!isRefresh && !tabData.hasMore) return;
|
||||
|
||||
this.setData({
|
||||
[`sjDingdanShuju.${key}.isLoading`]: true,
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh
|
||||
isLoading: isRefresh && tabData.list.length === 0,
|
||||
isLoadingMore: !isRefresh,
|
||||
});
|
||||
|
||||
try {
|
||||
const statusItem = this.data.statusList.find(s => s.key === key);
|
||||
const zhuangtaiList = statusItem.zhuangtaiList;
|
||||
|
||||
// 构建请求参数
|
||||
const params = {
|
||||
zhuangtai_list: zhuangtaiList,
|
||||
page: page,
|
||||
page_size: 5,
|
||||
leixing_id: this.data.xuanzhongLeixingId, // 🆕 商品类型筛选
|
||||
keyword: this.data.searchKeyword || undefined // 🆕 模糊搜索
|
||||
};
|
||||
const statusItem = this.data.statusList.find((s) => s.key === key);
|
||||
const params = this.buildListParams(key, page);
|
||||
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq', // 商家订单接口(和原来一样)
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: params,
|
||||
header: { 'content-type': 'application/json' }
|
||||
header: { 'content-type': 'application/json' },
|
||||
});
|
||||
|
||||
const code = res.data.code;
|
||||
if (code === 200 || code === 0) {
|
||||
const newList = res.data.data.list || [];
|
||||
const hasMore = res.data.data.has_more || false;
|
||||
const total = Number(res.data.data.total);
|
||||
const hasMore = typeof res.data.data.has_more === 'boolean'
|
||||
? res.data.data.has_more
|
||||
: (Number.isFinite(total) ? page * params.page_size < total : newList.length >= params.page_size);
|
||||
|
||||
// 处理数据:拼接图片、转换状态中文
|
||||
const processed = newList.map(item => ({
|
||||
...item,
|
||||
zhuangtaiZh: getOrderStatusText(item.zhuangtai),
|
||||
zhuangtaiColor: statusItem.color,
|
||||
// 如果有商品图片则用商品图片,否则用默认图
|
||||
tupian: item.tupian
|
||||
? (item.tupian.startsWith('http') ? item.tupian : this.data.ossImageUrl + item.tupian)
|
||||
: this.data.defaultImg
|
||||
}));
|
||||
const processed = newList.map((item) => this.processOrderItem(item, statusItem.color));
|
||||
const prevList = isRefresh ? [] : tabData.list;
|
||||
const updatedList = [...prevList, ...processed];
|
||||
|
||||
const currentList = this.data.sjDingdanShuju[key].list;
|
||||
const updatedList = isRefresh ? processed : [...currentList, ...processed];
|
||||
// 按订单 ID 去重,避免分页边界重复
|
||||
const seen = new Set();
|
||||
const deduped = updatedList.filter((row) => {
|
||||
const id = row.dingdan_id;
|
||||
if (!id || seen.has(id)) return false;
|
||||
seen.add(id);
|
||||
return true;
|
||||
});
|
||||
|
||||
this.setData({
|
||||
[`sjDingdanShuju.${key}.list`]: updatedList,
|
||||
[`sjDingdanShuju.${key}.list`]: deduped,
|
||||
[`sjDingdanShuju.${key}.page`]: page,
|
||||
[`sjDingdanShuju.${key}.hasMore`]: hasMore,
|
||||
[`sjDingdanShuju.${key}.isLoading`]: false,
|
||||
isLoading: false,
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
scrollViewRefreshing: false,
|
||||
});
|
||||
this._listInitialized = true;
|
||||
this.refreshCurrentListView();
|
||||
} else {
|
||||
throw new Error(res.data.msg || '加载失败');
|
||||
@@ -214,94 +413,109 @@ Page(createPage({
|
||||
[`sjDingdanShuju.${key}.isLoading`]: false,
|
||||
isLoading: false,
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
scrollViewRefreshing: false,
|
||||
});
|
||||
this.refreshCurrentListView();
|
||||
}
|
||||
},
|
||||
|
||||
// 刷新当前视图
|
||||
refreshCurrentListView() {
|
||||
const key = this.data.currentStatusKey;
|
||||
const tabData = this.data.sjDingdanShuju[key];
|
||||
this.setData({
|
||||
currentList: tabData.list,
|
||||
hasMore: tabData.hasMore,
|
||||
isLoading: tabData.isLoading
|
||||
isLoading: tabData.isLoading,
|
||||
});
|
||||
},
|
||||
|
||||
// 重置当前状态数据
|
||||
resetAllStatusData() {
|
||||
const patch = {};
|
||||
Object.keys(this.data.sjDingdanShuju).forEach((k) => {
|
||||
patch[`sjDingdanShuju.${k}`] = { ...EMPTY_TAB };
|
||||
});
|
||||
this.setData(patch);
|
||||
this.refreshCurrentListView();
|
||||
},
|
||||
|
||||
resetCurrentStatusData() {
|
||||
const key = this.data.currentStatusKey;
|
||||
this.setData({
|
||||
[`sjDingdanShuju.${key}.list`]: [],
|
||||
[`sjDingdanShuju.${key}.page`]: 1,
|
||||
[`sjDingdanShuju.${key}.hasMore`]: true,
|
||||
[`sjDingdanShuju.${key}.isLoading`]: false
|
||||
[`sjDingdanShuju.${key}`]: { ...EMPTY_TAB },
|
||||
});
|
||||
this.refreshCurrentListView();
|
||||
},
|
||||
|
||||
// 🆕 加载待结算数量
|
||||
async loadPendingCount() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
zhuangtai_list: [8],
|
||||
page: 1,
|
||||
page_size: 1
|
||||
}
|
||||
});
|
||||
if (res && res.data.code === 0) {
|
||||
this.setData({ pendingCount: res.data.data.pending_count || 0 });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载待结算数量失败:', error);
|
||||
}
|
||||
return this.loadOrderStats();
|
||||
},
|
||||
|
||||
// 下拉刷新(由 scroll-view 触发)
|
||||
onPullDownRefresh() {
|
||||
if (this.data.isLoading) {
|
||||
if (this.data.isLoading || this.data.isLoadingMore) {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
return;
|
||||
}
|
||||
this.setData({ scrollViewRefreshing: true });
|
||||
this._savedScrollTop = 0;
|
||||
this.setData({ scrollViewRefreshing: true, listScrollTop: 0 });
|
||||
this.resetCurrentStatusData();
|
||||
this.loadCurrentStatusOrders(true);
|
||||
},
|
||||
|
||||
// 上拉加载更多(由 scroll-view 触发)
|
||||
onReachBottom() {
|
||||
const key = this.data.currentStatusKey;
|
||||
const tabData = this.data.sjDingdanShuju[key];
|
||||
if (tabData.isLoading || !tabData.hasMore) return;
|
||||
this.setData({ [`sjDingdanShuju.${key}.page`]: tabData.page + 1 });
|
||||
this.loadCurrentStatusOrders(false);
|
||||
},
|
||||
|
||||
// 🆕 手动点击"加载更多"按钮
|
||||
onLoadMoreTap() {
|
||||
const key = this.data.currentStatusKey;
|
||||
const tabData = this.data.sjDingdanShuju[key];
|
||||
if (tabData.isLoading || !tabData.hasMore) return;
|
||||
this.setData({ [`sjDingdanShuju.${key}.page`]: tabData.page + 1 });
|
||||
this.loadCurrentStatusOrders(false);
|
||||
},
|
||||
|
||||
// 跳转订单详情
|
||||
processOrderItem(item, zhuangtaiColor) {
|
||||
const oss = this.data.ossImageUrl || '';
|
||||
const leixing = this.data.shangpinleixing.find((l) => l.id === item.leixing_id);
|
||||
const leixingIconUrl = leixing ? leixing.full_tupian_url : '/images/default-type.png';
|
||||
|
||||
let sjAvatarFull = '/images/default-avatar.png';
|
||||
if (item.sj_avatar) {
|
||||
sjAvatarFull = item.sj_avatar.startsWith('http') ? item.sj_avatar : oss + item.sj_avatar;
|
||||
}
|
||||
|
||||
let zhidingAvatarFull = '';
|
||||
if (item.zhiding_avatar) {
|
||||
zhidingAvatarFull = item.zhiding_avatar.startsWith('http') ? item.zhiding_avatar : oss + item.zhiding_avatar;
|
||||
}
|
||||
|
||||
const tags = normalizeOrderTags(item);
|
||||
|
||||
return {
|
||||
...item,
|
||||
zhuangtaiZh: getOrderStatusText(item.zhuangtai),
|
||||
zhuangtaiColor: zhuangtaiColor || '#333333',
|
||||
leixing_icon_url: leixingIconUrl,
|
||||
sj_avatar_full: sjAvatarFull,
|
||||
zhiding_avatar_full: zhidingAvatarFull,
|
||||
isZhiding: item.zhuangtai === 7,
|
||||
creat_time: item.creat_time || item.create_time || '',
|
||||
...tags,
|
||||
shangjia_youzhi: !!item.shangjia_youzhi,
|
||||
};
|
||||
},
|
||||
|
||||
goToSjDingdanXiangqing(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item) return;
|
||||
this._skipShowRefresh = true;
|
||||
const dataStr = encodeURIComponent(JSON.stringify(item));
|
||||
wx.navigateTo({
|
||||
url: `/pages/merchant-order-detail/merchant-order-detail?dingdanData=${dataStr}`
|
||||
url: `/pages/merchant-order-detail/merchant-order-detail?dingdanData=${dataStr}`,
|
||||
});
|
||||
},
|
||||
|
||||
// 图片加载失败
|
||||
onImageError() {}
|
||||
}))
|
||||
onImageError() {},
|
||||
}));
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
"navigationBarTitleText": "我的派单",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "dark",
|
||||
"backgroundColor": "#f8f9fa",
|
||||
"backgroundColor": "#fff8e1",
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag"
|
||||
}
|
||||
}
|
||||
@@ -1,135 +1,265 @@
|
||||
<!--pages/merchant-orders/merchant-orders.wxml-->
|
||||
<view class="sj-page">
|
||||
<!-- 顶部商品类型滑动 -->
|
||||
<view class="leixing-area">
|
||||
<scroll-view class="leixing-scroll" scroll-x enhanced show-scrollbar="{{false}}">
|
||||
<view class="leixing-container">
|
||||
<block wx:for="{{shangpinleixing}}" wx:key="id">
|
||||
<view
|
||||
class="leixing-item {{xuanzhongLeixingId == item.id ? 'leixing-active' : ''}}"
|
||||
data-id="{{item.id}}"
|
||||
bindtap="selectLeixing"
|
||||
>
|
||||
<image class="leixing-img" src="{{item.full_tupian_url}}" mode="aspectFill"/>
|
||||
<text class="leixing-name">{{item.jieshao || '类型'}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索行 -->
|
||||
<view class="filter-row">
|
||||
<view class="search-box">
|
||||
<image class="search-icon" src="/images/icon_search.svg" mode="aspectFit"/>
|
||||
<input class="search-input" placeholder="搜索订单ID/介绍/昵称/接单员ID" value="{{searchKeyword}}" bindinput="onSearchInput" bindconfirm="onSearchConfirm" confirm-type="search"/>
|
||||
<view class="search-clear" wx:if="{{searchKeyword}}" bindtap="clearSearch"><text>✕</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 待结算数量提示条(保留原功能) -->
|
||||
<view class="sj-pending-tip" wx:if="{{pendingCount > 0}}">
|
||||
<text class="sj-pending-icon"></text>
|
||||
<text class="sj-pending-text">待结算订单</text>
|
||||
<view class="sj-pending-badge">{{pendingCount}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体:左右布局 -->
|
||||
<view class="main-container">
|
||||
<!-- 左侧状态栏(固定) -->
|
||||
<view class="left-status">
|
||||
<block wx:for="{{statusList}}" wx:key="key">
|
||||
<view class="status-item {{currentStatusKey == item.key ? 'status-active' : ''}}" data-key="{{item.key}}" bindtap="switchStatus">
|
||||
<text class="status-name">{{item.name}}</text>
|
||||
<!-- 结算中状态旁边显示红色数字(保留原功能) -->
|
||||
<view class="status-dot" wx:if="{{currentStatusKey == item.key}}"></view>
|
||||
<view wx:if="{{item.key === 'jiesuanzhong' && pendingCount > 0}}" class="sj-status-badge">{{pendingCount}}</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 右侧包裹:撑出右边距,里面是 scroll-view -->
|
||||
<view class="right-wrapper">
|
||||
<scroll-view
|
||||
class="right-list"
|
||||
scroll-y enhanced show-scrollbar="{{false}}"
|
||||
refresher-enabled="{{true}}"
|
||||
refresher-threshold="80"
|
||||
refresher-default-style="black"
|
||||
refresher-background="#f5f6fa"
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
bindscrolltolower="onReachBottom"
|
||||
lower-threshold="50"
|
||||
>
|
||||
<view class="refresher-slot" slot="refresher">
|
||||
<text wx:if="{{scrollViewRefreshing}}" class="refreshing-text">⚡ 正在刷新...</text>
|
||||
<text wx:else class="pull-text">▼ 下拉刷新</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容容器 -->
|
||||
<view class="list-inner">
|
||||
<!-- 加载中 -->
|
||||
<view wx:if="{{isLoading && currentList.length === 0}}" class="loading-state">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-tip">加载订单中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view wx:elif="{{!isLoading && currentList.length === 0}}" class="empty-state">
|
||||
<image class="empty-img" src="/images/empty-order.png" mode="aspectFit"/>
|
||||
<text class="empty-text">暂无订单</text>
|
||||
<text class="empty-subtext">快去派发订单吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单卡片列表 -->
|
||||
<block wx:for="{{currentList}}" wx:key="dingdan_id">
|
||||
<view class="order-card {{item.zhuangtai === 8 ? 'sj-card-jiesuan' : ''}}" data-item="{{item}}" bindtap="goToSjDingdanXiangqing">
|
||||
<!-- 创建时间 -->
|
||||
<view class="card-time" wx:if="{{item.create_time}}">{{item.create_time}}</view>
|
||||
<!-- 内容行 -->
|
||||
<view class="card-content-row">
|
||||
<image class="card-img" src="{{item.tupian}}" mode="aspectFill" binderror="onImageError"/>
|
||||
<view class="card-info">
|
||||
<view class="card-title">{{item.jieshao || '暂无描述'}}</view>
|
||||
<view class="card-id" wx:if="{{item.dingdan_id}}">ID: {{item.dingdan_id}}</view>
|
||||
<view class="card-nicheng" wx:if="{{item.nicheng}}">昵称: {{item.nicheng}}</view>
|
||||
</view>
|
||||
<view class="card-right">
|
||||
<view class="card-status {{item.zhuangtai === 8 ? 'sj-status-jiesuan' : ''}}" style="color: {{item.zhuangtaiColor}};">{{item.zhuangtaiZh}}</view>
|
||||
<view class="card-price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-number">{{item.jine || '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 上拉加载更多提示 -->
|
||||
<view class="load-more" wx:if="{{hasMore && currentList.length > 0 && !isLoadingMore}}">
|
||||
<text class="load-more-text">上拉加载更多</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<view class="loading-more-state" wx:if="{{isLoadingMore}}">
|
||||
<view class="mini-spinner"></view>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 🆕 手动加载更多按钮 -->
|
||||
<view class="manual-load-more" wx:if="{{hasMore && currentList.length > 0 && !isLoadingMore}}">
|
||||
<button class="load-more-btn" bindtap="onLoadMoreTap" loading="{{false}}">点击加载更多</button>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多 -->
|
||||
<view class="no-more" wx:if="{{!hasMore && currentList.length > 0}}">
|
||||
<text>—— 没有更多了 ——</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
<!--pages/merchant-orders/merchant-orders.wxml-->
|
||||
<view class="sj-page">
|
||||
<!-- 顶部商品类型滑动 -->
|
||||
<view class="leixing-area">
|
||||
<scroll-view class="leixing-scroll" scroll-x enhanced show-scrollbar="{{false}}">
|
||||
<view class="leixing-container">
|
||||
<block wx:for="{{shangpinleixing}}" wx:key="id">
|
||||
<view
|
||||
class="leixing-item {{xuanzhongLeixingId == item.id ? 'leixing-active' : ''}}"
|
||||
data-id="{{item.id}}"
|
||||
bindtap="selectLeixing"
|
||||
>
|
||||
<image class="leixing-img" src="{{item.full_tupian_url}}" mode="aspectFill"/>
|
||||
<text class="leixing-name">{{item.jieshao || '类型'}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索行 -->
|
||||
<view class="filter-row">
|
||||
<view class="search-box">
|
||||
<image class="search-icon" src="/images/icon_search.svg" mode="aspectFit"/>
|
||||
<input class="search-input" placeholder="搜索订单ID/介绍/昵称/接单员ID/拼多多订单号" value="{{searchKeyword}}" bindinput="onSearchInput" bindconfirm="onSearchConfirm" confirm-type="search"/>
|
||||
<view class="search-clear" wx:if="{{searchKeyword}}" bindtap="clearSearch"><text>✕</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 时间段筛选 -->
|
||||
<view class="time-filter-row">
|
||||
<picker mode="date" value="{{timeStart}}" bindchange="onTimeStartChange">
|
||||
<view class="time-pill {{timeStart ? 'time-pill--active' : ''}}">{{timeStart || '开始日期'}}</view>
|
||||
</picker>
|
||||
<text class="time-sep">至</text>
|
||||
<picker mode="date" value="{{timeEnd}}" bindchange="onTimeEndChange">
|
||||
<view class="time-pill {{timeEnd ? 'time-pill--active' : ''}}">{{timeEnd || '结束日期'}}</view>
|
||||
</picker>
|
||||
<view class="time-clear" wx:if="{{timeStart || timeEnd}}" bindtap="clearTimeFilter">清除</view>
|
||||
</view>
|
||||
<view class="time-quick-row">
|
||||
<view class="time-quick-chip" data-days="0" bindtap="pickQuickRange">全部</view>
|
||||
<view class="time-quick-chip" data-days="1" bindtap="pickQuickRange">今天</view>
|
||||
<view class="time-quick-chip" data-days="7" bindtap="pickQuickRange">近7天</view>
|
||||
<view class="time-quick-chip" data-days="30" bindtap="pickQuickRange">近30天</view>
|
||||
</view>
|
||||
|
||||
<view class="staff-filter-row" wx:if="{{staffList.length > 0}}">
|
||||
<picker mode="selector" range="{{staffList}}" range-key="nickname" bindchange="onStaffFilterChange">
|
||||
<view class="staff-filter-pill">{{selectedStaffLabel}}</view>
|
||||
</picker>
|
||||
<view wx:if="{{selectedStaffMemberId}}" class="staff-filter-clear" bindtap="clearStaffFilter">清除</view>
|
||||
</view>
|
||||
|
||||
<!-- 待结算数量提示条(保留原功能) -->
|
||||
<view class="sj-pending-tip" wx:if="{{pendingCount > 0}}">
|
||||
<text class="sj-pending-icon"></text>
|
||||
<text class="sj-pending-text">待结算订单</text>
|
||||
<view class="sj-pending-badge">{{pendingCount}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体:左右布局 -->
|
||||
<view class="main-container">
|
||||
<!-- 左侧状态栏(固定) -->
|
||||
<view class="left-status">
|
||||
<view class="sidebar-stats" wx:if="{{canViewFinance}}">
|
||||
<text class="sb-line">派单 {{orderStats.dispatch_count}} / ¥{{orderStats.dispatch_amount}}</text>
|
||||
<text class="sb-line">成交 {{orderStats.completed_count}} / ¥{{orderStats.completed_amount}}</text>
|
||||
<text class="sb-line">退款 {{orderStats.refund_count}} / ¥{{orderStats.refund_amount}}</text>
|
||||
<text class="sb-line accent">待结 {{orderStats.pending_count}} / ¥{{orderStats.pending_amount}}</text>
|
||||
</view>
|
||||
<block wx:for="{{statusList}}" wx:key="key">
|
||||
<view class="status-item {{currentStatusKey == item.key ? 'status-active' : ''}}" data-key="{{item.key}}" bindtap="switchStatus">
|
||||
<text class="status-name">{{item.name}}</text>
|
||||
<!-- 结算中状态旁边显示红色数字(保留原功能) -->
|
||||
<view class="status-dot" wx:if="{{currentStatusKey == item.key}}"></view>
|
||||
<view wx:if="{{item.key === 'jiesuanzhong' && pendingCount > 0}}" class="sj-status-badge">{{pendingCount}}</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 右侧包裹:撑出右边距,里面是 scroll-view -->
|
||||
<view class="right-wrapper">
|
||||
<scroll-view
|
||||
class="right-list"
|
||||
scroll-y enhanced show-scrollbar="{{false}}"
|
||||
scroll-top="{{listScrollTop}}"
|
||||
bindscroll="onListScroll"
|
||||
refresher-enabled="{{true}}"
|
||||
refresher-threshold="80"
|
||||
refresher-default-style="black"
|
||||
refresher-background="#fff8e1"
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
bindscrolltolower="onReachBottom"
|
||||
lower-threshold="50"
|
||||
>
|
||||
<view class="refresher-slot" slot="refresher">
|
||||
<text wx:if="{{scrollViewRefreshing}}" class="refreshing-text">⚡ 正在刷新...</text>
|
||||
<text wx:else class="pull-text">▼ 下拉刷新</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容容器 -->
|
||||
<view class="list-inner">
|
||||
<!-- 加载中 -->
|
||||
<view wx:if="{{isLoading && currentList.length === 0}}" class="loading-state">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-tip">加载订单中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view wx:elif="{{!isLoading && currentList.length === 0}}" class="empty-state">
|
||||
<image class="empty-img" src="/images/empty-order.png" mode="aspectFit"/>
|
||||
<text class="empty-text">暂无订单</text>
|
||||
<text class="empty-subtext">快去派发订单吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单卡片:逍遥梦 ui-preview 布局 -->
|
||||
<view class="sj-order-list">
|
||||
<block wx:for="{{currentList}}" wx:key="dingdan_id">
|
||||
<!-- 优质商家金牌卡 -->
|
||||
<view wx:if="{{item.shangjia_youzhi}}" class="xym-order-item gold-card" data-item="{{item}}" bindtap="goToSjDingdanXiangqing">
|
||||
<view class="gold-head flexb">
|
||||
<view class="kehuduan-banner-wrap">
|
||||
<image class="gold-banner" src="{{goldBannerUrl}}" mode="widthFix"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gold-body-wrap">
|
||||
<view class="order-con gold-inner">
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-strip">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-row myflex">
|
||||
<image class="o-logo" src="{{item.leixing_icon_url}}" mode="aspectFill"/>
|
||||
<view class="o-col">
|
||||
<view class="o-goods line2">{{item.jieshao || '暂无介绍'}}</view>
|
||||
<view class="tag-row myflex">
|
||||
<text class="tag-pill tag-youzhi">优质商家</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="reward-badge normal-reward">
|
||||
<text class="reward-yen sm">¥</text>
|
||||
<text class="reward-num dark">{{item.jine || '0.00'}}</text>
|
||||
</view>
|
||||
<view class="remark-block dark">
|
||||
<text class="remark-label dark-t">商家备注:</text>{{item.beizhu || '暂无'}}
|
||||
</view>
|
||||
<view wx:if="{{item.shangjia_biaoqian.length || item.shangjia_identity_biaoqian.length || item.xuqiu_biaoqian.length}}" class="merchant-tags-container">
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length}}" class="biaoqian-inline-scroll" scroll-x>
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.shangjia_identity_biaoqian.length}}" class="biaoqian-inline-scroll" scroll-x style="margin-top:8rpx">
|
||||
<chenghao-tag wx:for="{{item.shangjia_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length}}" class="biaoqian-inline-scroll" scroll-x style="margin-top:8rpx">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="merchant-strip myflex">
|
||||
<image class="m-avatar" src="{{item.sj_avatar_full}}" mode="aspectFill"/>
|
||||
<view class="m-info">
|
||||
<view class="m-name light-t">{{item.sjnicheng || '未知商家'}}</view>
|
||||
<view class="m-sn light-sub">ID:{{item.shangjia_id}} 发布于 {{item.creat_time}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="o-foot flexb">
|
||||
<text class="o-amt-grey">订单金额 ¥{{item.jine || '0.00'}}</text>
|
||||
<view class="sj-status-pill" style="color:{{item.zhuangtaiColor}}">{{item.zhuangtaiZh}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 普通商家订单 -->
|
||||
<view wx:else class="xym-order-item normal-card" data-item="{{item}}" bindtap="goToSjDingdanXiangqing">
|
||||
<view class="normal-body-wrap">
|
||||
<view class="order-con">
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-strip">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-row myflex">
|
||||
<image class="o-logo" src="{{item.leixing_icon_url}}" mode="aspectFill"/>
|
||||
<view class="o-col">
|
||||
<view class="o-goods line2">{{item.jieshao || '暂无介绍'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="reward-badge normal-reward">
|
||||
<text class="reward-yen sm">¥</text>
|
||||
<text class="reward-num dark">{{item.jine || '0.00'}}</text>
|
||||
</view>
|
||||
<view class="remark-block dark">
|
||||
<text class="remark-label dark-t">商家备注:</text>{{item.beizhu || '暂无'}}
|
||||
</view>
|
||||
<view wx:if="{{item.shangjia_biaoqian.length || item.shangjia_identity_biaoqian.length || item.xuqiu_biaoqian.length}}" class="merchant-tags-container">
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length}}" class="biaoqian-inline-scroll" scroll-x>
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.shangjia_identity_biaoqian.length}}" class="biaoqian-inline-scroll" scroll-x style="margin-top:8rpx">
|
||||
<chenghao-tag wx:for="{{item.shangjia_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length}}" class="biaoqian-inline-scroll" scroll-x style="margin-top:8rpx">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="merchant-strip myflex">
|
||||
<image class="m-avatar" src="{{item.sj_avatar_full}}" mode="aspectFill"/>
|
||||
<view class="m-info">
|
||||
<view class="m-name light-t">{{item.sjnicheng || '未知商家'}}</view>
|
||||
<view class="m-sn light-sub">ID:{{item.shangjia_id}} 发布于 {{item.creat_time}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="o-foot flexb">
|
||||
<text class="o-amt-grey">订单金额 ¥{{item.jine || '0.00'}}</text>
|
||||
<view class="sj-status-pill" style="color:{{item.zhuangtaiColor}}">{{item.zhuangtaiZh}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 上拉加载更多提示 -->
|
||||
<view class="load-more" wx:if="{{hasMore && currentList.length > 0 && !isLoadingMore}}">
|
||||
<text class="load-more-text">上拉加载更多</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<view class="loading-more-state" wx:if="{{isLoadingMore}}">
|
||||
<view class="mini-spinner"></view>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 🆕 手动加载更多按钮 -->
|
||||
<view class="manual-load-more" wx:if="{{hasMore && currentList.length > 0 && !isLoadingMore}}">
|
||||
<button class="load-more-btn" bindtap="onLoadMoreTap" loading="{{false}}">点击加载更多</button>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多 -->
|
||||
<view class="no-more" wx:if="{{!hasMore && currentList.length > 0}}">
|
||||
<text>—— 没有更多了 ——</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
<tab-bar />
|
||||
@@ -1,428 +1,529 @@
|
||||
/* pages/merchant-orders/merchant-orders.wxss */
|
||||
|
||||
/* 页面容器:禁止整体滚动 */
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sj-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
|
||||
/* ========== 商品类型区 ========== */
|
||||
.leixing-area {
|
||||
background: #fff;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.leixing-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.leixing-container {
|
||||
display: inline-flex;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
.leixing-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-right: 32rpx;
|
||||
padding: 12rpx 20rpx;
|
||||
border-radius: 28rpx;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.leixing-item.leixing-active {
|
||||
background: linear-gradient(135deg, #e8f0fe, #d4e4ff);
|
||||
box-shadow: 0 4rpx 12rpx rgba(33,150,243,0.15);
|
||||
}
|
||||
.leixing-img {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: #f0f0f0;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.leixing-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
max-width: 100rpx;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ========== 搜索行 ========== */
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 24rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.search-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f0f2f5;
|
||||
border-radius: 36rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 68rpx;
|
||||
}
|
||||
.search-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
.search-clear {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ccc;
|
||||
border-radius: 50%;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
.search-clear text {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ========== 待结算提示条(保留原样式) ========== */
|
||||
.sj-pending-tip {
|
||||
margin: 20rpx 24rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
background: linear-gradient(145deg, #ffe8cc, #ffdbb5);
|
||||
border-left: 8rpx solid #ffaa00;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(255,170,0,0.3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sj-pending-icon {
|
||||
font-size: 36rpx;
|
||||
color: #ffaa00;
|
||||
}
|
||||
.sj-pending-text {
|
||||
font-size: 28rpx;
|
||||
color: #b25700;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sj-pending-badge {
|
||||
min-width: 44rpx;
|
||||
height: 44rpx;
|
||||
padding: 0 12rpx;
|
||||
background: linear-gradient(145deg, #ff3b3b, #d10000);
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
line-height: 44rpx;
|
||||
text-align: center;
|
||||
border-radius: 22rpx;
|
||||
margin-left: auto;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255,0,0,0.5);
|
||||
}
|
||||
|
||||
/* ========== 主体布局 ========== */
|
||||
.main-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 左侧状态栏 */
|
||||
.left-status {
|
||||
width: 180rpx;
|
||||
background: #fff;
|
||||
border-right: 1rpx solid #f0f0f0;
|
||||
padding: 28rpx 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.status-item {
|
||||
padding: 28rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
.status-item.status-active {
|
||||
background: linear-gradient(to right, #e3f2fd, #fff);
|
||||
}
|
||||
.status-name {
|
||||
font-size: 28rpx;
|
||||
color: #555;
|
||||
}
|
||||
.status-active .status-name {
|
||||
color: #1976D2;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
border-radius: 50%;
|
||||
background: #1976D2;
|
||||
}
|
||||
/* 结算中状态的小红点数字 */
|
||||
.sj-status-badge {
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
padding: 0 8rpx;
|
||||
background: linear-gradient(145deg, #ff4d4f, #d10000);
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
line-height: 36rpx;
|
||||
text-align: center;
|
||||
border-radius: 18rpx;
|
||||
margin-left: 8rpx;
|
||||
box-shadow: 0 4rpx 8rpx rgba(255,0,0,0.4);
|
||||
}
|
||||
|
||||
/* 右侧包裹 */
|
||||
.right-wrapper {
|
||||
flex: 1;
|
||||
padding-right: 30rpx;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.right-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
.refresher-slot {
|
||||
text-align: center;
|
||||
padding: 14rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 内容容器 */
|
||||
.list-inner {
|
||||
padding: 24rpx 0 200rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ========== 订单卡片 ========== */
|
||||
.order-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0,0,0,0.04);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* 结算中卡片特殊边框 */
|
||||
.sj-card-jiesuan {
|
||||
border: 2rpx solid #ff5722;
|
||||
box-shadow: 0 6rpx 20rpx rgba(255,87,34,0.15);
|
||||
}
|
||||
.order-card:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* 创建时间 */
|
||||
.card-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 12rpx;
|
||||
padding-bottom: 8rpx;
|
||||
border-bottom: 1rpx dashed rgba(0,120,255,0.15);
|
||||
}
|
||||
|
||||
/* 内容行 */
|
||||
.card-content-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 商品图片 */
|
||||
.card-img {
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
border-radius: 18rpx;
|
||||
background: #f0f0f0;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 中间信息区 */
|
||||
.card-info {
|
||||
flex: 1;
|
||||
min-height: 130rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
}
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-id {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
.card-nicheng {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
/* 右侧状态和价格 */
|
||||
.card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
min-height: 130rpx;
|
||||
margin-left: 20rpx;
|
||||
flex-shrink: 0;
|
||||
max-width: 160rpx;
|
||||
}
|
||||
.card-status {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
padding: 4rpx 12rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 8rpx;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* 结算中状态红色高亮(保留原样式) */
|
||||
.sj-status-jiesuan {
|
||||
background: linear-gradient(145deg, #ff4d4f, #d10000) !important;
|
||||
color: #fff !important;
|
||||
border-color: #ffaa00;
|
||||
box-shadow: 0 0 15rpx #ff4d4f;
|
||||
}
|
||||
.card-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.price-symbol {
|
||||
font-size: 28rpx;
|
||||
color: #ffaa00;
|
||||
font-weight: 700;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
.price-number {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* ========== 加载与空状态 ========== */
|
||||
.loading-state, .empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
.loading-spinner {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border: 4rpx solid #e0e0e0;
|
||||
border-top-color: #1976D2;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
opacity: 0.4;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.empty-text, .loading-tip {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
.empty-subtext {
|
||||
font-size: 26rpx;
|
||||
color: #b0b0b0;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
text-align: center;
|
||||
padding: 36rpx 0 20rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
.loading-more-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
.mini-spinner {
|
||||
display: inline-block;
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
border: 3rpx solid #ccc;
|
||||
border-top-color: #1976D2;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.6s linear infinite;
|
||||
vertical-align: middle;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.manual-load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 30rpx 0 10rpx 0;
|
||||
}
|
||||
.load-more-btn {
|
||||
background: #ffffff;
|
||||
color: #1976D2;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border: 2rpx solid #1976D2;
|
||||
border-radius: 40rpx;
|
||||
padding: 16rpx 60rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(33,150,243,0.1);
|
||||
}
|
||||
.load-more-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
/* pages/merchant-orders/merchant-orders.wxss */
|
||||
@import '../../styles/dashou-xym-order-card.wxss';
|
||||
@import '../../styles/shangjia-merchant-order-card.wxss';
|
||||
|
||||
/* 页面容器:禁止整体滚动 */
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #fff8e1;
|
||||
}
|
||||
.sj-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff8e1;
|
||||
}
|
||||
|
||||
/* ========== 商品类型区 ========== */
|
||||
.leixing-area {
|
||||
background: #fff;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.leixing-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.leixing-container {
|
||||
display: inline-flex;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
.leixing-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-right: 32rpx;
|
||||
padding: 12rpx 20rpx;
|
||||
border-radius: 28rpx;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.leixing-item.leixing-active {
|
||||
background: linear-gradient(135deg, #e8f0fe, #d4e4ff);
|
||||
box-shadow: 0 4rpx 12rpx rgba(33,150,243,0.15);
|
||||
}
|
||||
.leixing-img {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: #f0f0f0;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.leixing-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
max-width: 100rpx;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ========== 搜索行 ========== */
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 24rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.search-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f0f2f5;
|
||||
border-radius: 36rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 68rpx;
|
||||
}
|
||||
.search-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
.search-clear {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ccc;
|
||||
border-radius: 50%;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
.search-clear text {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ========== 时间段筛选 ========== */
|
||||
.time-filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12rpx 24rpx 8rpx;
|
||||
background: #fff;
|
||||
gap: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.time-pill {
|
||||
min-width: 180rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
background: #f0f2f5;
|
||||
border-radius: 28rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.time-pill--active {
|
||||
color: #492f00;
|
||||
background: linear-gradient(180deg, #fff8e1, #ffe9b8);
|
||||
border: 1rpx solid #e8c547;
|
||||
}
|
||||
.time-sep {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.time-clear {
|
||||
font-size: 24rpx;
|
||||
color: #c9a227;
|
||||
padding: 8rpx 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.time-quick-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 0 24rpx 16rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.time-quick-chip {
|
||||
padding: 8rpx 20rpx;
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.staff-filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.staff-filter-pill {
|
||||
padding: 10rpx 24rpx;
|
||||
background: #f0f7ff;
|
||||
color: #1565C0;
|
||||
border-radius: 24rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.staff-filter-clear {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.sidebar-stats {
|
||||
padding: 12rpx 8rpx 16rpx;
|
||||
margin-bottom: 8rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
.sb-line {
|
||||
display: block;
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
}
|
||||
.sb-line.accent { color: #E65100; font-weight: 600; }
|
||||
|
||||
/* ========== 待结算提示条(保留原样式) ========== */
|
||||
.sj-pending-tip {
|
||||
margin: 20rpx 24rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
background: linear-gradient(145deg, #ffe8cc, #ffdbb5);
|
||||
border-left: 8rpx solid #ffaa00;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(255,170,0,0.3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sj-pending-icon {
|
||||
font-size: 36rpx;
|
||||
color: #ffaa00;
|
||||
}
|
||||
.sj-pending-text {
|
||||
font-size: 28rpx;
|
||||
color: #b25700;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sj-pending-badge {
|
||||
min-width: 44rpx;
|
||||
height: 44rpx;
|
||||
padding: 0 12rpx;
|
||||
background: linear-gradient(145deg, #ff3b3b, #d10000);
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
line-height: 44rpx;
|
||||
text-align: center;
|
||||
border-radius: 22rpx;
|
||||
margin-left: auto;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255,0,0,0.5);
|
||||
}
|
||||
|
||||
/* ========== 主体布局 ========== */
|
||||
.main-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 左侧状态栏 */
|
||||
.left-status {
|
||||
width: 180rpx;
|
||||
background: #fff;
|
||||
border-right: 1rpx solid #f0f0f0;
|
||||
padding: 28rpx 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.status-item {
|
||||
padding: 28rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
.status-item.status-active {
|
||||
background: linear-gradient(to right, #e3f2fd, #fff);
|
||||
}
|
||||
.status-name {
|
||||
font-size: 28rpx;
|
||||
color: #555;
|
||||
}
|
||||
.status-active .status-name {
|
||||
color: #1976D2;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
border-radius: 50%;
|
||||
background: #1976D2;
|
||||
}
|
||||
/* 结算中状态的小红点数字 */
|
||||
.sj-status-badge {
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
padding: 0 8rpx;
|
||||
background: linear-gradient(145deg, #ff4d4f, #d10000);
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
line-height: 36rpx;
|
||||
text-align: center;
|
||||
border-radius: 18rpx;
|
||||
margin-left: 8rpx;
|
||||
box-shadow: 0 4rpx 8rpx rgba(255,0,0,0.4);
|
||||
}
|
||||
|
||||
/* 右侧包裹 */
|
||||
.right-wrapper {
|
||||
flex: 1;
|
||||
padding-right: 30rpx;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
.right-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
.refresher-slot {
|
||||
text-align: center;
|
||||
padding: 14rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 内容容器 */
|
||||
.list-inner {
|
||||
padding: 24rpx 0 200rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ========== 订单卡片 ========== */
|
||||
.order-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0,0,0,0.04);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* 结算中卡片特殊边框 */
|
||||
.sj-card-jiesuan {
|
||||
border: 2rpx solid #ff5722;
|
||||
box-shadow: 0 6rpx 20rpx rgba(255,87,34,0.15);
|
||||
}
|
||||
.order-card:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* 创建时间 */
|
||||
.card-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 12rpx;
|
||||
padding-bottom: 8rpx;
|
||||
border-bottom: 1rpx dashed rgba(0,120,255,0.15);
|
||||
}
|
||||
|
||||
/* 内容行 */
|
||||
.card-content-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 商品图片 */
|
||||
.card-img {
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
border-radius: 18rpx;
|
||||
background: #f0f0f0;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 中间信息区 */
|
||||
.card-info {
|
||||
flex: 1;
|
||||
min-height: 130rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
}
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-id {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
.card-nicheng {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
/* 右侧状态和价格 */
|
||||
.card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
min-height: 130rpx;
|
||||
margin-left: 20rpx;
|
||||
flex-shrink: 0;
|
||||
max-width: 160rpx;
|
||||
}
|
||||
.card-status {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
padding: 4rpx 12rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 8rpx;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* 结算中状态红色高亮(保留原样式) */
|
||||
.sj-status-jiesuan {
|
||||
background: linear-gradient(145deg, #ff4d4f, #d10000) !important;
|
||||
color: #fff !important;
|
||||
border-color: #ffaa00;
|
||||
box-shadow: 0 0 15rpx #ff4d4f;
|
||||
}
|
||||
.card-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.price-symbol {
|
||||
font-size: 28rpx;
|
||||
color: #ffaa00;
|
||||
font-weight: 700;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
.price-number {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* ========== 加载与空状态 ========== */
|
||||
.loading-state, .empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
.loading-spinner {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border: 4rpx solid #e0e0e0;
|
||||
border-top-color: #1976D2;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
opacity: 0.4;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.empty-text, .loading-tip {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
.empty-subtext {
|
||||
font-size: 26rpx;
|
||||
color: #b0b0b0;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
text-align: center;
|
||||
padding: 36rpx 0 20rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
.loading-more-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
.mini-spinner {
|
||||
display: inline-block;
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
border: 3rpx solid #ccc;
|
||||
border-top-color: #1976D2;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.6s linear infinite;
|
||||
vertical-align: middle;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.manual-load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 30rpx 0 10rpx 0;
|
||||
}
|
||||
.load-more-btn {
|
||||
background: #ffffff;
|
||||
color: #1976D2;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border: 2rpx solid #1976D2;
|
||||
border-radius: 40rpx;
|
||||
padding: 16rpx 60rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(33,150,243,0.1);
|
||||
}
|
||||
.load-more-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sj-order-list {
|
||||
padding: 0 4rpx 8rpx;
|
||||
}
|
||||
|
||||
.list-inner {
|
||||
padding: 12rpx 8rpx 24rpx;
|
||||
}
|
||||
|
||||
.load-more-btn {
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
border: 2rpx solid #fff;
|
||||
box-shadow: 0 6rpx 16rpx rgba(245, 213, 99, 0.3);
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
import request from '../../../../utils/request.js';
|
||||
|
||||
const app = getApp();
|
||||
const MEIYE = 5;
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
status: { type: Number, value: 1, observer: 'reload' },
|
||||
searchDingdan: { type: String, value: '', observer: 'reload' },
|
||||
trigger: { type: Number, value: 0, observer: 'reload' }
|
||||
},
|
||||
|
||||
data: {
|
||||
list: [],
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
|
||||
showDetail: false,
|
||||
detailItem: null,
|
||||
|
||||
showModify: false,
|
||||
modifyLiyou: '',
|
||||
modifyJine: '',
|
||||
|
||||
showCancel: false,
|
||||
cancelLiyou: '',
|
||||
|
||||
showResubmit: false,
|
||||
resubmitLiyou: '',
|
||||
resubmitJine: '',
|
||||
|
||||
uploading: false,
|
||||
uploadProgressWidth: '0%'
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() { this.reload(); }
|
||||
},
|
||||
|
||||
methods: {
|
||||
reload() {
|
||||
this.setData({ page: 1, list: [], hasMore: true });
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
mapItem(item) {
|
||||
const z = Number(item.zhuangtai);
|
||||
let display_status = '未知', status_class = 'zhuangtai-weizhi';
|
||||
if (z === 1) { display_status = '待缴纳'; status_class = 'zhuangtai-daijiaona'; }
|
||||
else if (z === 2) { display_status = '已缴纳'; status_class = 'zhuangtai-yijiaona'; }
|
||||
else if (z === 3) { display_status = '申诉中'; status_class = 'zhuangtai-shensuzhong'; }
|
||||
else if (z === 4) { display_status = '已驳回'; status_class = 'zhuangtai-yibohui'; }
|
||||
|
||||
const zhengju = (item.zhengju_tupian || []).map(img => ({
|
||||
id: img.id,
|
||||
url: img.url,
|
||||
fullUrl: this.getFullUrl(img.url)
|
||||
}));
|
||||
const shensu = (item.shensu_tupian || []).map(img => ({
|
||||
id: img.id,
|
||||
url: img.url,
|
||||
fullUrl: this.getFullUrl(img.url)
|
||||
}));
|
||||
|
||||
return {
|
||||
...item,
|
||||
display_status,
|
||||
status_class,
|
||||
create_time: item.CreateTime || '',
|
||||
zhengju_list: zhengju,
|
||||
shensu_list: shensu,
|
||||
staff_label: item.staff_display_name ? `客服:${item.staff_display_name}` : ''
|
||||
};
|
||||
},
|
||||
|
||||
async loadData(isLoadMore = false) {
|
||||
if (this.data.loading || this.data.loadingMore) return;
|
||||
if (isLoadMore && !this.data.hasMore) return;
|
||||
|
||||
if (isLoadMore) this.setData({ loadingMore: true });
|
||||
else this.setData({ loading: true });
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/sjfklbhq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
page: this.data.page,
|
||||
page_size: MEIYE,
|
||||
zhuangtai: this.data.status,
|
||||
sousuo_dingdan_id: this.data.searchDingdan
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
const rows = (data.list || []).map(item => this.mapItem(item));
|
||||
const newList = isLoadMore ? this.data.list.concat(rows) : rows;
|
||||
this.setData({
|
||||
list: newList,
|
||||
hasMore: data.has_more || false,
|
||||
page: isLoadMore ? this.data.page + 1 : 2,
|
||||
loading: false,
|
||||
loadingMore: false
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '加载失败', icon: 'none' });
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
if (this.data.list.length === 0 || !this.data.hasMore) return;
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
openDetail(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
this.setData({ showDetail: true, detailItem: item });
|
||||
},
|
||||
|
||||
closeDetail() {
|
||||
this.setData({ showDetail: false, detailItem: null });
|
||||
},
|
||||
|
||||
goOrder(e) {
|
||||
const id = e.currentTarget.dataset.id || this.data.detailItem?.guanliandingdan_id;
|
||||
if (!id) return;
|
||||
wx.navigateTo({ url: `/pages/merchant-order-detail/merchant-order-detail?dingdan_id=${id}` });
|
||||
},
|
||||
|
||||
openModify() {
|
||||
const item = this.data.detailItem;
|
||||
this.setData({
|
||||
showModify: true,
|
||||
modifyLiyou: item.chufaliyou || '',
|
||||
modifyJine: item.fakuanjine || ''
|
||||
});
|
||||
},
|
||||
|
||||
closeModify() { this.setData({ showModify: false }); },
|
||||
|
||||
onModifyLiyou(e) { this.setData({ modifyLiyou: e.detail.value.slice(0, 500) }); },
|
||||
onModifyJine(e) { this.setData({ modifyJine: e.detail.value }); },
|
||||
|
||||
async submitModify() {
|
||||
const item = this.data.detailItem;
|
||||
const liyou = this.data.modifyLiyou.trim();
|
||||
const jine = parseFloat(this.data.modifyJine);
|
||||
if (!liyou) { wx.showToast({ title: '请输入罚款原因', icon: 'none' }); return; }
|
||||
if (!jine || jine <= 0) { wx.showToast({ title: '金额无效', icon: 'none' }); return; }
|
||||
|
||||
wx.showLoading({ title: '提交中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjfkxgycx',
|
||||
method: 'POST',
|
||||
data: {
|
||||
operation: 'modify_penalty',
|
||||
dingdan_id: item.guanliandingdan_id,
|
||||
fadan_id: item.id,
|
||||
liyou: liyou,
|
||||
jine: jine.toFixed(2)
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
wx.showToast({ title: '修改成功', icon: 'success' });
|
||||
this.setData({ showModify: false });
|
||||
this.closeDetail();
|
||||
this.reload();
|
||||
this.triggerEvent('changed');
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '修改失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
openCancel() { this.setData({ showCancel: true, cancelLiyou: '' }); },
|
||||
closeCancel() { this.setData({ showCancel: false }); },
|
||||
onCancelLiyou(e) { this.setData({ cancelLiyou: e.detail.value.slice(0, 500) }); },
|
||||
|
||||
async submitCancel() {
|
||||
const item = this.data.detailItem;
|
||||
const liyou = this.data.cancelLiyou.trim();
|
||||
if (!liyou) { wx.showToast({ title: '请输入驳回理由', icon: 'none' }); return; }
|
||||
wx.showLoading({ title: '提交中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjfkxgycx',
|
||||
method: 'POST',
|
||||
data: {
|
||||
operation: 'cancel_penalty',
|
||||
dingdan_id: item.guanliandingdan_id,
|
||||
fadan_id: item.id,
|
||||
liyou: liyou
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
wx.showToast({ title: '已撤销', icon: 'success' });
|
||||
this.setData({ showCancel: false });
|
||||
this.closeDetail();
|
||||
this.reload();
|
||||
this.triggerEvent('changed');
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '操作失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
openResubmit() {
|
||||
const item = this.data.detailItem;
|
||||
this.setData({
|
||||
showResubmit: true,
|
||||
resubmitLiyou: item.chufaliyou || '',
|
||||
resubmitJine: item.fakuanjine || ''
|
||||
});
|
||||
},
|
||||
|
||||
closeResubmit() { this.setData({ showResubmit: false }); },
|
||||
onResubmitLiyou(e) { this.setData({ resubmitLiyou: e.detail.value.slice(0, 500) }); },
|
||||
onResubmitJine(e) { this.setData({ resubmitJine: e.detail.value }); },
|
||||
|
||||
async submitResubmit() {
|
||||
const item = this.data.detailItem;
|
||||
const liyou = this.data.resubmitLiyou.trim();
|
||||
const jine = parseFloat(this.data.resubmitJine);
|
||||
if (!liyou || !jine || jine <= 0) {
|
||||
wx.showToast({ title: '请填写完整信息', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '提交中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjfkxgycx',
|
||||
method: 'POST',
|
||||
data: {
|
||||
operation: 'resubmit_penalty',
|
||||
dingdan_id: item.guanliandingdan_id,
|
||||
fadan_id: item.id,
|
||||
liyou: liyou,
|
||||
jine: jine.toFixed(2)
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
wx.showToast({ title: '已重新申请', icon: 'success' });
|
||||
this.setData({ showResubmit: false });
|
||||
this.closeDetail();
|
||||
this.reload();
|
||||
this.triggerEvent('changed');
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '操作失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
chooseEvidence() {
|
||||
const item = this.data.detailItem;
|
||||
const remain = 9 - (item.zhengju_list || []).length;
|
||||
if (remain <= 0) return;
|
||||
wx.chooseMedia({
|
||||
count: remain,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: async (res) => {
|
||||
const paths = res.tempFiles.map(f => f.tempFilePath);
|
||||
await this.uploadAndAddEvidence(paths);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async uploadAndAddEvidence(localPaths) {
|
||||
const item = this.data.detailItem;
|
||||
if (!localPaths.length) return;
|
||||
this.setData({ uploading: true, uploadProgressWidth: '0%' });
|
||||
|
||||
try {
|
||||
const keys = await this.uploadToCos(localPaths, item);
|
||||
if (!keys) return;
|
||||
const res = await request({
|
||||
url: '/dingdan/sjfkxgycx',
|
||||
method: 'POST',
|
||||
data: {
|
||||
operation: 'manage_penalty_evidence',
|
||||
dingdan_id: item.guanliandingdan_id,
|
||||
fadan_id: item.id,
|
||||
add_urls: keys,
|
||||
remove_ids: []
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
wx.showToast({ title: '图片已更新', icon: 'success' });
|
||||
this.reload();
|
||||
this.triggerEvent('changed');
|
||||
this.setData({ showDetail: false, detailItem: null });
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '保存失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this.setData({ uploading: false });
|
||||
}
|
||||
},
|
||||
|
||||
async deleteEvidence(e) {
|
||||
const imgId = e.currentTarget.dataset.id;
|
||||
const item = this.data.detailItem;
|
||||
wx.showLoading({ title: '删除中' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjfkxgycx',
|
||||
method: 'POST',
|
||||
data: {
|
||||
operation: 'manage_penalty_evidence',
|
||||
dingdan_id: item.guanliandingdan_id,
|
||||
fadan_id: item.id,
|
||||
add_urls: [],
|
||||
remove_ids: [imgId]
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const zhengju = item.zhengju_list.filter(img => img.id !== imgId);
|
||||
this.setData({ detailItem: { ...item, zhengju_list: zhengju } });
|
||||
this.reload();
|
||||
this.triggerEvent('changed');
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '删除失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
async uploadToCos(localPaths, item) {
|
||||
const credRes = await request({
|
||||
url: '/dingdan/dsscpz',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdan_id: item.guanliandingdan_id,
|
||||
fadan_id: item.id,
|
||||
yongtu: 'fakuan_evidence'
|
||||
}
|
||||
});
|
||||
if (credRes.data.code !== 0) {
|
||||
wx.showToast({ title: credRes.data.msg || '凭证失败', icon: 'none' });
|
||||
return null;
|
||||
}
|
||||
const tokenData = credRes.data.data;
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.min.js');
|
||||
const cos = new COS({
|
||||
SimpleUploadMethod: 'putObject',
|
||||
getAuthorization: (_, callback) => {
|
||||
callback({
|
||||
TmpSecretId: credentials.tmpSecretId,
|
||||
TmpSecretKey: credentials.tmpSecretKey,
|
||||
SecurityToken: credentials.sessionToken || '',
|
||||
StartTime: tokenData.startTime,
|
||||
ExpiredTime: tokenData.expiredTime
|
||||
});
|
||||
}
|
||||
});
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063';
|
||||
const region = tokenData.region || 'ap-shanghai';
|
||||
const uid = app.globalData.userInfo?.yonghuid || wx.getStorageSync('uid') || '0000000';
|
||||
const keys = [];
|
||||
const total = localPaths.length;
|
||||
|
||||
for (let i = 0; i < total; i++) {
|
||||
const key = `fakuan/shangjiafakuan/zhengju/${uid}_${item.id}_${Date.now() + i}.jpg`;
|
||||
await new Promise((resolve, reject) => {
|
||||
cos.uploadFile({
|
||||
Bucket: bucket,
|
||||
Region: region,
|
||||
Key: key,
|
||||
FilePath: localPaths[i]
|
||||
}, (err) => err ? reject(err) : resolve());
|
||||
});
|
||||
keys.push(key);
|
||||
this.setData({ uploadProgressWidth: `${((i + 1) / total * 100).toFixed(0)}%` });
|
||||
}
|
||||
return keys;
|
||||
},
|
||||
|
||||
previewImage(e) {
|
||||
const url = e.currentTarget.dataset.url;
|
||||
wx.previewImage({ current: url, urls: [url] });
|
||||
},
|
||||
|
||||
getFullUrl(url) {
|
||||
if (!url) return '';
|
||||
if (url.startsWith('http')) return url;
|
||||
return this.data.ossImageUrl + url;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<view class="container">
|
||||
<scroll-view scroll-y class="scroll" bindscrolltolower="loadMore">
|
||||
<view wx:if="{{ loading && list.length === 0 }}" class="tip">加载中...</view>
|
||||
<block wx:for="{{ list }}" wx:key="id">
|
||||
<view class="card" bindtap="openDetail" data-item="{{ item }}">
|
||||
<view class="card-head">
|
||||
<text class="time">{{ item.create_time }}</text>
|
||||
<text class="tag {{ item.status_class }}">{{ item.display_status }}</text>
|
||||
</view>
|
||||
<text class="info">金额:¥{{ item.fakuanjine }}</text>
|
||||
<text class="info">理由:{{ item.chufaliyou }}</text>
|
||||
<text class="info staff" wx:if="{{ item.staff_label }}">{{ item.staff_label }}</text>
|
||||
<text class="info link" wx:if="{{ item.guanliandingdan_id }}">订单:{{ item.guanliandingdan_id }}</text>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ list.length > 0 && !loadingMore && hasMore }}" class="load-more-btn" bindtap="loadMore">点击获取更多</view>
|
||||
<view wx:if="{{ loadingMore }}" class="tip">加载中...</view>
|
||||
<view wx:if="{{ !hasMore && list.length > 0 }}" class="tip">—— 没有更多了 ——</view>
|
||||
<view wx:if="{{ !loading && list.length === 0 }}" class="empty">暂无罚单记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<view wx:if="{{ showDetail }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">罚单详情</text>
|
||||
<view class="modal-close" bindtap="closeDetail">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="row"><text class="lbl">罚款金额</text><text class="val strong">¥{{ detailItem.fakuanjine }}</text></view>
|
||||
<view class="row"><text class="lbl">状态</text><text class="tag {{ detailItem.status_class }}">{{ detailItem.display_status }}</text></view>
|
||||
<view class="row"><text class="lbl">分红到账</text><text class="val">¥{{ detailItem.applicant_bonus_amount || '0.00' }}</text></view>
|
||||
<view class="row" wx:if="{{ detailItem.staff_label }}"><text class="lbl">申请客服</text><text class="val">{{ detailItem.staff_label }}</text></view>
|
||||
<view class="row"><text class="lbl">处罚理由</text><text class="val">{{ detailItem.chufaliyou }}</text></view>
|
||||
<view class="row" wx:if="{{ detailItem.guanliandingdan_id }}">
|
||||
<text class="lbl">关联订单</text>
|
||||
<text class="val link" bindtap="goOrder" data-id="{{ detailItem.guanliandingdan_id }}">{{ detailItem.guanliandingdan_id }} ›</text>
|
||||
</view>
|
||||
<view class="row" wx:if="{{ detailItem.bohuiliyou }}"><text class="lbl">驳回理由</text><text class="val">{{ detailItem.bohuiliyou }}</text></view>
|
||||
<view class="row" wx:if="{{ detailItem.shensuliyou }}"><text class="lbl">申诉理由</text><text class="val">{{ detailItem.shensuliyou }}</text></view>
|
||||
|
||||
<view class="section">
|
||||
<text class="sec-title">处罚证据图(可管理)</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.zhengju_list }}" wx:key="id">
|
||||
<view class="img-box">
|
||||
<image class="img" src="{{ item.fullUrl }}" mode="aspectFill" bindtap="previewImage" data-url="{{ item.fullUrl }}"/>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3 }}" class="img-del" catchtap="deleteEvidence" data-id="{{ item.id }}">✕</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ (detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3) && detailItem.zhengju_list.length < 9 }}" class="img-add" bindtap="chooseEvidence">+</view>
|
||||
</view>
|
||||
<view wx:if="{{ uploading }}" class="progress-text">上传中 {{ uploadProgressWidth }}</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{ detailItem.shensu_list.length }}" class="section">
|
||||
<text class="sec-title">打手申诉图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.shensu_list }}" wx:key="id">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ item.fullUrl }}">
|
||||
<image class="img" src="{{ item.fullUrl }}" mode="aspectFill"/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3 }}" class="pbtn primary" bindtap="openModify">修改</view>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3 }}" class="pbtn warn" bindtap="openCancel">撤销</view>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 4 }}" class="pbtn primary" bindtap="openResubmit">再次申请</view>
|
||||
<view class="pbtn default" bindtap="closeDetail">关闭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{ showModify }}" class="modal-mask sub-modal">
|
||||
<view class="modal-panel small">
|
||||
<view class="modal-head"><text class="modal-title">修改罚单</text><view class="modal-close" bindtap="closeModify">✕</view></view>
|
||||
<view class="modal-body">
|
||||
<textarea class="textarea" placeholder="罚款原因" value="{{ modifyLiyou }}" bindinput="onModifyLiyou"/>
|
||||
<input class="input-num" type="digit" placeholder="金额" value="{{ modifyJine }}" bindinput="onModifyJine"/>
|
||||
</view>
|
||||
<view class="modal-foot">
|
||||
<view class="pbtn primary" bindtap="submitModify">确认</view>
|
||||
<view class="pbtn default" bindtap="closeModify">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{ showCancel }}" class="modal-mask sub-modal">
|
||||
<view class="modal-panel small">
|
||||
<view class="modal-head"><text class="modal-title">撤销罚单</text><view class="modal-close" bindtap="closeCancel">✕</view></view>
|
||||
<view class="modal-body">
|
||||
<textarea class="textarea" placeholder="驳回理由" value="{{ cancelLiyou }}" bindinput="onCancelLiyou"/>
|
||||
</view>
|
||||
<view class="modal-foot">
|
||||
<view class="pbtn warn" bindtap="submitCancel">确认撤销</view>
|
||||
<view class="pbtn default" bindtap="closeCancel">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{ showResubmit }}" class="modal-mask sub-modal">
|
||||
<view class="modal-panel small">
|
||||
<view class="modal-head"><text class="modal-title">再次申请</text><view class="modal-close" bindtap="closeResubmit">✕</view></view>
|
||||
<view class="modal-body">
|
||||
<textarea class="textarea" placeholder="罚款原因" value="{{ resubmitLiyou }}" bindinput="onResubmitLiyou"/>
|
||||
<input class="input-num" type="digit" placeholder="金额" value="{{ resubmitJine }}" bindinput="onResubmitJine"/>
|
||||
</view>
|
||||
<view class="modal-foot">
|
||||
<view class="pbtn primary" bindtap="submitResubmit">提交</view>
|
||||
<view class="pbtn default" bindtap="closeResubmit">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,17 @@
|
||||
@import '../../../../pages/penalty/components/fakuan-list/fakuan-list.wxss';
|
||||
|
||||
.staff { color: #1565C0; }
|
||||
.link { color: #1565C0; }
|
||||
.warn { background: #E53935; color: #fff; }
|
||||
.sub-modal { z-index: 1000; }
|
||||
.modal-panel.small { max-height: 60vh; }
|
||||
.input-num {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx;
|
||||
background: #f9f9f9;
|
||||
border-radius: 10rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.progress-text { font-size: 22rpx; color: #1565C0; margin-top: 8rpx; }
|
||||
@@ -0,0 +1,112 @@
|
||||
import request from '../../../../utils/request.js';
|
||||
|
||||
const app = getApp();
|
||||
const MEIYE = 10;
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
status: { type: Number, value: 0, observer: 'reload' },
|
||||
trigger: { type: Number, value: 0, observer: 'reload' }
|
||||
},
|
||||
|
||||
data: {
|
||||
list: [],
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
showDetail: false,
|
||||
detailItem: {}
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() { this.reload(); }
|
||||
},
|
||||
|
||||
methods: {
|
||||
reload() {
|
||||
this.setData({ page: 1, list: [], hasMore: true });
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
mapItem(item) {
|
||||
const n = Number(item.sqzhuangtai);
|
||||
let display_status = '未知', status_class = 'zhuangtai-weizhi';
|
||||
if (n === 0) { display_status = '待处理'; status_class = 'zhuangtai-daijiaona'; }
|
||||
else if (n === 1) { display_status = '已处罚'; status_class = 'zhuangtai-yijiaona'; }
|
||||
else if (n === 2) { display_status = '已撤销'; status_class = 'zhuangtai-yibohui'; }
|
||||
else if (n === 3) { display_status = '申诉中'; status_class = 'zhuangtai-shensuzhong'; }
|
||||
|
||||
return {
|
||||
...item,
|
||||
display_status,
|
||||
status_class,
|
||||
display_time: item.CreateTime || item.create_time || '--',
|
||||
full_zhengju: (item.zhengju_tupian || []).map(u => this.fullUrl(u)),
|
||||
full_shensu: (item.shensu_tupian || []).map(u => this.fullUrl(u))
|
||||
};
|
||||
},
|
||||
|
||||
fullUrl(url) {
|
||||
if (!url) return '';
|
||||
if (url.startsWith('http')) return url;
|
||||
return this.data.ossImageUrl + url;
|
||||
},
|
||||
|
||||
async loadData(isLoadMore = false) {
|
||||
if (this.data.loading || this.data.loadingMore) return;
|
||||
if (isLoadMore && !this.data.hasMore) return;
|
||||
if (isLoadMore) this.setData({ loadingMore: true });
|
||||
else this.setData({ loading: true });
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/sjcfjlhq',
|
||||
method: 'POST',
|
||||
data: { page: this.data.page, page_size: MEIYE, zhuangtai: this.data.status }
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
const rows = (data.list || []).map(item => this.mapItem(item));
|
||||
const newList = isLoadMore ? this.data.list.concat(rows) : rows;
|
||||
this.setData({
|
||||
list: newList,
|
||||
hasMore: data.has_more === true,
|
||||
page: this.data.page + 1,
|
||||
loading: false,
|
||||
loadingMore: false
|
||||
});
|
||||
} else {
|
||||
this.setData({ loading: false, loadingMore: false, hasMore: false });
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
if (!this.data.hasMore || this.data.loadingMore) return;
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
openDetail(e) {
|
||||
this.setData({ showDetail: true, detailItem: e.currentTarget.dataset.item });
|
||||
},
|
||||
|
||||
closeDetail() {
|
||||
this.setData({ showDetail: false, detailItem: {} });
|
||||
},
|
||||
|
||||
goOrder(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
if (!id) return;
|
||||
wx.navigateTo({ url: `/pages/merchant-order-detail/merchant-order-detail?dingdan_id=${id}` });
|
||||
},
|
||||
|
||||
previewImage(e) {
|
||||
const url = e.currentTarget.dataset.url;
|
||||
wx.previewImage({ current: url, urls: [url] });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<view class="container">
|
||||
<scroll-view scroll-y class="scroll" bindscrolltolower="loadMore">
|
||||
<view wx:if="{{ loading && list.length === 0 }}" class="tip">加载中...</view>
|
||||
<block wx:for="{{ list }}" wx:key="id">
|
||||
<view class="card" bindtap="openDetail" data-item="{{ item }}">
|
||||
<view class="card-head">
|
||||
<text class="time">{{ item.display_time }}</text>
|
||||
<text class="tag {{ item.status_class }}">{{ item.display_status }}</text>
|
||||
</view>
|
||||
<text class="info">扣分:{{ item.jifen }} 积分</text>
|
||||
<text class="info">理由:{{ item.cfliyou }}</text>
|
||||
<text class="info link" wx:if="{{ item.dingdan_id }}">订单:{{ item.dingdan_id }}</text>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ !hasMore && list.length > 0 }}" class="tip">—— 没有更多了 ——</view>
|
||||
<view wx:if="{{ !loading && list.length === 0 }}" class="empty">暂无积分处罚记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<view wx:if="{{ showDetail }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">积分处罚详情</text>
|
||||
<view class="modal-close" bindtap="closeDetail">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="row"><text class="lbl">扣分</text><text class="val strong">{{ detailItem.jifen }} 积分</text></view>
|
||||
<view class="row"><text class="lbl">状态</text><text class="tag {{ detailItem.status_class }}">{{ detailItem.display_status }}</text></view>
|
||||
<view class="row"><text class="lbl">处罚理由</text><text class="val">{{ detailItem.cfliyou }}</text></view>
|
||||
<view class="row" wx:if="{{ detailItem.dingdan_id }}">
|
||||
<text class="lbl">关联订单</text>
|
||||
<text class="val link" bindtap="goOrder" data-id="{{ detailItem.dingdan_id }}">{{ detailItem.dingdan_id }} ›</text>
|
||||
</view>
|
||||
<view class="row" wx:if="{{ detailItem.ssliyou }}"><text class="lbl">申诉理由</text><text class="val">{{ detailItem.ssliyou }}</text></view>
|
||||
<view wx:if="{{ detailItem.full_zhengju.length }}" class="section">
|
||||
<text class="sec-title">商家证据图</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.full_zhengju }}" wx:key="*this">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ item }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill"/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{ detailItem.full_shensu.length }}" class="section">
|
||||
<text class="sec-title">打手申诉图</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.full_shensu }}" wx:key="*this">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ item }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill"/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="pbtn default" bindtap="closeDetail">关闭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,3 @@
|
||||
@import '../../../../pages/penalty/components/fakuan-list/fakuan-list.wxss';
|
||||
|
||||
.link { color: #1565C0; }
|
||||
@@ -0,0 +1,88 @@
|
||||
import request from '../../utils/request.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
stats: {
|
||||
fakuan: {
|
||||
total: 0, total_jine: '0.00',
|
||||
daijiaona: 0, daijiaona_jine: '0.00',
|
||||
yijiaona: 0, yijiaona_jine: '0.00',
|
||||
shensuzhong: 0, shensuzhong_jine: '0.00',
|
||||
yibohui: 0, yibohui_jine: '0.00',
|
||||
shijidaozhang_jine: '0.00'
|
||||
},
|
||||
jifen: {
|
||||
total: 0, total_jifen: 0,
|
||||
daichuli: 0, daichuli_jifen: 0,
|
||||
yichuli: 0, yichuli_jifen: 0
|
||||
}
|
||||
},
|
||||
activeTab: 'fakuan',
|
||||
activeFakuanStatus: 1,
|
||||
activeJifenStatus: 0,
|
||||
searchDingdan: '',
|
||||
trigger: { fakuan: 0, jifen: 0 }
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.fetchTongji();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.fetchTongji();
|
||||
},
|
||||
|
||||
async fetchTongji() {
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/sjcftjhq', method: 'POST' });
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
this.setData({ stats: res.data.data });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取商家处罚统计失败', e);
|
||||
}
|
||||
},
|
||||
|
||||
switchMainTab(e) {
|
||||
const tab = e.currentTarget.dataset.tab;
|
||||
if (tab === this.data.activeTab) return;
|
||||
this.setData({ activeTab: tab });
|
||||
},
|
||||
|
||||
switchFakuanStatus(e) {
|
||||
const status = Number(e.currentTarget.dataset.status);
|
||||
if (status === this.data.activeFakuanStatus) return;
|
||||
this.setData({ activeFakuanStatus: status });
|
||||
this.bumpTrigger('fakuan');
|
||||
},
|
||||
|
||||
switchJifenStatus(e) {
|
||||
const status = Number(e.currentTarget.dataset.status);
|
||||
if (status === this.data.activeJifenStatus) return;
|
||||
this.setData({ activeJifenStatus: status });
|
||||
this.bumpTrigger('jifen');
|
||||
},
|
||||
|
||||
onSearchInput(e) {
|
||||
this.setData({ searchDingdan: e.detail.value });
|
||||
},
|
||||
|
||||
onSearchConfirm() {
|
||||
this.bumpTrigger(this.data.activeTab);
|
||||
},
|
||||
|
||||
clearSearch() {
|
||||
this.setData({ searchDingdan: '' });
|
||||
this.bumpTrigger(this.data.activeTab);
|
||||
},
|
||||
|
||||
bumpTrigger(tab) {
|
||||
const key = `trigger.${tab}`;
|
||||
this.setData({ [key]: this.data.trigger[tab] + 1 });
|
||||
},
|
||||
|
||||
onPenaltyChanged() {
|
||||
this.fetchTongji();
|
||||
this.bumpTrigger('fakuan');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"navigationBarTitleText": "处罚管理",
|
||||
"usingComponents": {
|
||||
"merchant-fakuan-list": "./components/merchant-fakuan-list/merchant-fakuan-list",
|
||||
"merchant-jifen-list": "./components/merchant-jifen-list/merchant-jifen-list"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<view class="page-container">
|
||||
<view class="stats-card">
|
||||
<view class="stats-title">金额罚单汇总</view>
|
||||
<view class="money-row">
|
||||
<view class="money-item">
|
||||
<text class="money-num">¥{{ stats.fakuan.total_jine }}</text>
|
||||
<text class="money-label">总金额</text>
|
||||
</view>
|
||||
<view class="money-item">
|
||||
<text class="money-num orange">¥{{ stats.fakuan.daijiaona_jine }}</text>
|
||||
<text class="money-label">待缴纳</text>
|
||||
</view>
|
||||
<view class="money-item">
|
||||
<text class="money-num green">¥{{ stats.fakuan.yijiaona_jine }}</text>
|
||||
<text class="money-label">已缴纳</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="money-row sub">
|
||||
<text>申诉中 ¥{{ stats.fakuan.shensuzhong_jine }}</text>
|
||||
<text>已驳回 ¥{{ stats.fakuan.yibohui_jine }}</text>
|
||||
<text>实际到账 ¥{{ stats.fakuan.shijidaozhang_jine }}</text>
|
||||
</view>
|
||||
<view class="stats-title jifen-title">积分处罚汇总</view>
|
||||
<view class="money-row">
|
||||
<view class="money-item">
|
||||
<text class="money-num">{{ stats.jifen.total_jifen }}</text>
|
||||
<text class="money-label">总积分</text>
|
||||
</view>
|
||||
<view class="money-item">
|
||||
<text class="money-num orange">{{ stats.jifen.daichuli_jifen }}</text>
|
||||
<text class="money-label">待处理</text>
|
||||
</view>
|
||||
<view class="money-item">
|
||||
<text class="money-num green">{{ stats.jifen.yichuli_jifen }}</text>
|
||||
<text class="money-label">已处理</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tab-row">
|
||||
<view class="tab-item {{ activeTab === 'fakuan' ? 'active' : '' }}" bindtap="switchMainTab" data-tab="fakuan">金额罚单</view>
|
||||
<view class="tab-item {{ activeTab === 'jifen' ? 'active' : '' }}" bindtap="switchMainTab" data-tab="jifen">积分处罚</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{ activeTab === 'fakuan' }}">
|
||||
<scroll-view class="sub-tabs" scroll-x enable-flex>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 1 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="1">待缴纳 {{ stats.fakuan.daijiaona }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 3 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="3">申诉中 {{ stats.fakuan.shensuzhong }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 2 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="2">已缴纳 {{ stats.fakuan.yijiaona }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 4 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="4">已驳回 {{ stats.fakuan.yibohui }}</view>
|
||||
</scroll-view>
|
||||
<view class="search-bar">
|
||||
<input class="search-input" placeholder="输入订单号搜索" value="{{ searchDingdan }}" bindinput="onSearchInput" bindconfirm="onSearchConfirm" confirm-type="search"/>
|
||||
<view class="search-btn" bindtap="onSearchConfirm">搜索</view>
|
||||
<view wx:if="{{ searchDingdan }}" class="clear-btn" bindtap="clearSearch">✕</view>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<scroll-view class="sub-tabs" scroll-x enable-flex>
|
||||
<view class="sub-tag {{ activeJifenStatus === 0 ? 'sub-active' : '' }}" bindtap="switchJifenStatus" data-status="0">待处理 {{ stats.jifen.daichuli }}</view>
|
||||
<view class="sub-tag {{ activeJifenStatus === 1 ? 'sub-active' : '' }}" bindtap="switchJifenStatus" data-status="1">已处理 {{ stats.jifen.yichuli }}</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
|
||||
<view wx:if="{{ activeTab === 'fakuan' }}" class="list-area">
|
||||
<merchant-fakuan-list
|
||||
status="{{ activeFakuanStatus }}"
|
||||
searchDingdan="{{ searchDingdan }}"
|
||||
trigger="{{ trigger.fakuan }}"
|
||||
bind:changed="onPenaltyChanged"
|
||||
/>
|
||||
</view>
|
||||
<view wx:else class="list-area">
|
||||
<merchant-jifen-list status="{{ activeJifenStatus }}" trigger="{{ trigger.jifen }}" />
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,76 @@
|
||||
page { background: #f7f8fa; }
|
||||
.page-container { padding-bottom: 30rpx; }
|
||||
|
||||
.stats-card {
|
||||
margin: 24rpx 28rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
.stats-title { font-size: 28rpx; font-weight: 600; color: #333; margin-bottom: 16rpx; }
|
||||
.jifen-title { margin-top: 20rpx; padding-top: 16rpx; border-top: 1rpx solid #eee; }
|
||||
.money-row { display: flex; justify-content: space-around; margin-bottom: 12rpx; }
|
||||
.money-row.sub { justify-content: space-between; font-size: 22rpx; color: #888; padding: 8rpx 0; }
|
||||
.money-item { display: flex; flex-direction: column; align-items: center; }
|
||||
.money-num { font-size: 32rpx; font-weight: 700; color: #333; }
|
||||
.money-num.orange { color: #E67E22; }
|
||||
.money-num.green { color: #2E7D32; }
|
||||
.money-label { font-size: 22rpx; color: #999; margin-top: 6rpx; }
|
||||
|
||||
.tab-row {
|
||||
display: flex;
|
||||
margin: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 24rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #1565C0;
|
||||
font-weight: 600;
|
||||
background: #E3F2FD;
|
||||
}
|
||||
|
||||
.sub-tabs { white-space: nowrap; margin: 8rpx 28rpx 16rpx; }
|
||||
.sub-tag {
|
||||
display: inline-block;
|
||||
padding: 10rpx 26rpx;
|
||||
margin-right: 14rpx;
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
.sub-tag.sub-active {
|
||||
background: #E3F2FD;
|
||||
color: #1565C0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 12rpx 22rpx;
|
||||
}
|
||||
.search-input { flex: 1; font-size: 26rpx; }
|
||||
.search-btn {
|
||||
margin-left: 18rpx;
|
||||
padding: 12rpx 30rpx;
|
||||
background: #1565C0;
|
||||
color: #fff;
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.clear-btn { margin-left: 16rpx; color: #aaa; }
|
||||
|
||||
.list-area { margin-top: 10rpx; }
|
||||
@@ -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();
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
// pages/merchant-regular-dispatch/merchant-regular-dispatch.js
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import {
|
||||
STAFF_API, isStaffMode, refreshStaffContext, getStaffContext,
|
||||
restoreStaffContextAfterAuth, isMerchantPortalUser, staffOrOwner,
|
||||
} from '../../utils/staff-api.js';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
function apiOk(res) {
|
||||
const code = res && res.data && res.data.code;
|
||||
return code === 200 || code === 0;
|
||||
}
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
sjyue: '0.00',
|
||||
isStaffMode: false,
|
||||
balanceLabel: '可用余额',
|
||||
balanceTip: '派单将从此余额扣除',
|
||||
|
||||
shangpinList: [],
|
||||
selectedTypeId: null,
|
||||
selectedHuiyuanId: null,
|
||||
selectedYaoqiuleixing: null,
|
||||
currentTypeChenghaoList: [],
|
||||
|
||||
dingdanJieshao: '',
|
||||
dingdanBeizhu: '',
|
||||
laobanName: '',
|
||||
waibuDingdanId: '',
|
||||
zhidingUid: '',
|
||||
jiage: '',
|
||||
selectedLabelId: '',
|
||||
selectedLabelName: '',
|
||||
commissionEnabled: false,
|
||||
commissionValue: '',
|
||||
|
||||
showTemplateSheet: false,
|
||||
templateKeyword: '',
|
||||
templateList: [],
|
||||
templatePage: 1,
|
||||
templateHasMore: true,
|
||||
templateLoading: false,
|
||||
|
||||
isLoading: false,
|
||||
isSubmitting: false,
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
if (wx.getStorageSync('token')) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
}
|
||||
if (!isMerchantPortalUser()) {
|
||||
wx.showToast({ title: '请先成为商家或绑定客服', icon: 'none' });
|
||||
setTimeout(() => wx.navigateBack(), 1500);
|
||||
return;
|
||||
}
|
||||
this.loadShangjiaYue();
|
||||
this.loadShangpinTypes();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadShangjiaYue();
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
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({ sjyue: shangjiaData.sjyue || '0.00' });
|
||||
},
|
||||
|
||||
async loadShangpinTypes() {
|
||||
this.setData({ isLoading: true });
|
||||
const staff = isStaffMode();
|
||||
try {
|
||||
const res = await request({
|
||||
url: staff ? STAFF_API.productTypes : '/dingdan/sjspleixing',
|
||||
method: 'POST',
|
||||
});
|
||||
if (apiOk(res)) {
|
||||
let list = res.data.data || [];
|
||||
if (!Array.isArray(list) && list.list) list = list.list;
|
||||
list.reverse();
|
||||
this.setData({ shangpinList: list, isLoading: false });
|
||||
if (list.length > 0) {
|
||||
this.setSelectedType(list[0]);
|
||||
}
|
||||
} else {
|
||||
throw new Error((res.data && res.data.msg) || '加载失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载商品类型失败:', error);
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: error.message || '加载类型失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
setSelectedType(item) {
|
||||
this.setData({
|
||||
selectedTypeId: item.id,
|
||||
selectedHuiyuanId: item.huiyuan_id,
|
||||
selectedYaoqiuleixing: item.yaoqiuleixing,
|
||||
currentTypeChenghaoList: item.chenghaoList || [],
|
||||
dingdanJieshao: '',
|
||||
jiage: '',
|
||||
selectedLabelId: '',
|
||||
selectedLabelName: '',
|
||||
commissionEnabled: false,
|
||||
commissionValue: '',
|
||||
});
|
||||
},
|
||||
|
||||
onSelectType(e) {
|
||||
const { item } = e.currentTarget.dataset;
|
||||
this.setSelectedType(item);
|
||||
},
|
||||
|
||||
openTemplateSheet() {
|
||||
if (!this.data.selectedTypeId) {
|
||||
wx.showToast({ title: '请先选择订单类型', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({
|
||||
showTemplateSheet: true,
|
||||
templateKeyword: '',
|
||||
templateList: [],
|
||||
templatePage: 1,
|
||||
templateHasMore: true,
|
||||
}, () => this.loadTemplates(true));
|
||||
},
|
||||
|
||||
closeTemplateSheet() {
|
||||
this.setData({ showTemplateSheet: false });
|
||||
},
|
||||
|
||||
onTemplateKeywordInput(e) {
|
||||
this.setData({ templateKeyword: e.detail.value });
|
||||
},
|
||||
|
||||
onTemplateSearch() {
|
||||
this.setData({ templateList: [], templatePage: 1, templateHasMore: true }, () => {
|
||||
this.loadTemplates(true);
|
||||
});
|
||||
},
|
||||
|
||||
onTemplateScrollLower() {
|
||||
const { templateHasMore, templateLoading } = this.data;
|
||||
if (!templateHasMore || templateLoading) return;
|
||||
this.setData({ templatePage: this.data.templatePage + 1 }, () => this.loadTemplates(false));
|
||||
},
|
||||
|
||||
async loadTemplates(reset) {
|
||||
const { selectedTypeId, templatePage, templateKeyword } = this.data;
|
||||
if (!selectedTypeId) return;
|
||||
this.setData({ templateLoading: true });
|
||||
try {
|
||||
const postData = {
|
||||
shangpinTypeId: selectedTypeId,
|
||||
page: templatePage,
|
||||
pageSize: PAGE_SIZE,
|
||||
getType: 1,
|
||||
};
|
||||
if (templateKeyword.trim()) {
|
||||
postData.keyword = templateKeyword.trim();
|
||||
}
|
||||
const res = await request({
|
||||
url: staffOrOwner(STAFF_API.templateList, '/peizhi/sjddmblb'),
|
||||
method: 'POST',
|
||||
data: postData,
|
||||
});
|
||||
if (apiOk(res)) {
|
||||
const data = res.data.data || {};
|
||||
const newTemplates = (data.list || []).map((item) => ({
|
||||
mobanId: item.mobanId || item.id,
|
||||
jieshao: item.jieshao || item.moban_jieshao || '',
|
||||
jiage: item.jiage || item.price || '',
|
||||
labelId: item.labelId || '',
|
||||
labelName: item.labelName || '',
|
||||
commissionEnabled: !!item.commissionEnabled,
|
||||
commissionValue: item.commissionValue || '',
|
||||
}));
|
||||
const merged = reset || templatePage === 1
|
||||
? newTemplates
|
||||
: [...this.data.templateList, ...newTemplates];
|
||||
this.setData({
|
||||
templateList: merged,
|
||||
templateHasMore: data.hasMore !== false && newTemplates.length === PAGE_SIZE,
|
||||
templateLoading: false,
|
||||
});
|
||||
} else {
|
||||
this.setData({ templateLoading: false, templateHasMore: false });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setData({ templateLoading: false });
|
||||
wx.showToast({ title: '加载模板失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
onSelectTemplate(e) {
|
||||
const { item } = e.currentTarget.dataset;
|
||||
if (!item) return;
|
||||
this.setData({
|
||||
dingdanJieshao: item.jieshao || '',
|
||||
jiage: String(item.jiage || ''),
|
||||
selectedLabelId: item.labelId || '',
|
||||
selectedLabelName: item.labelName || this.getLabelNameById(item.labelId),
|
||||
commissionEnabled: !!item.commissionEnabled,
|
||||
commissionValue: item.commissionValue || '',
|
||||
showTemplateSheet: false,
|
||||
});
|
||||
},
|
||||
|
||||
getLabelNameById(labelId) {
|
||||
if (!labelId) return '';
|
||||
const found = this.data.currentTypeChenghaoList.find((x) => x.id == labelId);
|
||||
return found ? found.mingcheng : '';
|
||||
},
|
||||
|
||||
onDingdanBeizhuInput(e) { this.setData({ dingdanBeizhu: e.detail.value }); },
|
||||
onLaobanNameInput(e) { this.setData({ laobanName: e.detail.value }); },
|
||||
onWaibuDingdanIdInput(e) { this.setData({ waibuDingdanId: e.detail.value }); },
|
||||
onZhidingUidInput(e) { this.setData({ zhidingUid: e.detail.value }); },
|
||||
|
||||
validateForm() {
|
||||
const { selectedTypeId, dingdanJieshao, laobanName, jiage, sjyue } = this.data;
|
||||
if (!selectedTypeId) {
|
||||
wx.showToast({ title: '请选择订单类型', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!dingdanJieshao.trim()) {
|
||||
wx.showToast({ title: '请选择订单模板', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!laobanName.trim()) {
|
||||
wx.showToast({ title: '请输入老板游戏昵称', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!jiage) {
|
||||
wx.showToast({ title: '模板价格无效', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
const price = parseFloat(jiage);
|
||||
if (isNaN(price) || price < 0.1 || price > 10000) {
|
||||
wx.showToast({ title: '价格需在0.1-10000元之间', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
const balance = parseFloat(sjyue);
|
||||
if (price > balance) {
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.setData({
|
||||
dingdanJieshao: '',
|
||||
dingdanBeizhu: '',
|
||||
laobanName: '',
|
||||
waibuDingdanId: '',
|
||||
zhidingUid: '',
|
||||
jiage: '',
|
||||
selectedLabelId: '',
|
||||
selectedLabelName: '',
|
||||
commissionEnabled: false,
|
||||
commissionValue: '',
|
||||
});
|
||||
if (this.data.shangpinList.length > 0) {
|
||||
this.setSelectedType(this.data.shangpinList[0]);
|
||||
}
|
||||
},
|
||||
|
||||
async onSubmit() {
|
||||
if (this.data.isSubmitting) return;
|
||||
if (!this.validateForm()) return;
|
||||
this.setData({ isSubmitting: true });
|
||||
|
||||
const postData = {
|
||||
shangpinTypeId: this.data.selectedTypeId,
|
||||
huiyuanId: this.data.selectedHuiyuanId,
|
||||
yaoqiuleixing: this.data.selectedYaoqiuleixing,
|
||||
dingdanJieshao: this.data.dingdanJieshao.trim(),
|
||||
dingdanBeizhu: this.data.dingdanBeizhu.trim() || '',
|
||||
laobanName: this.data.laobanName.trim(),
|
||||
zhidingUid: this.data.zhidingUid.trim() || '',
|
||||
waibuDingdanId: this.data.waibuDingdanId.trim() || '',
|
||||
jiage: this.data.jiage,
|
||||
labelId: this.data.selectedLabelId || '',
|
||||
labelName: this.data.selectedLabelName || '',
|
||||
commissionEnabled: this.data.commissionEnabled,
|
||||
commissionValue: this.data.commissionEnabled ? this.data.commissionValue : '',
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
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);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.resetForm();
|
||||
this.setData({ isSubmitting: false });
|
||||
}, 1500);
|
||||
} else {
|
||||
wx.showModal({
|
||||
title: '派单失败',
|
||||
content: res.data.msg || '请稍后重试',
|
||||
showCancel: false,
|
||||
});
|
||||
this.setData({ isSubmitting: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('派单请求失败:', error);
|
||||
wx.showModal({
|
||||
title: '请求失败',
|
||||
content: error.message || '网络错误,请检查连接',
|
||||
showCancel: false,
|
||||
});
|
||||
this.setData({ isSubmitting: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"popup-notice": "/components/popup-notice/popup-notice",
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag"
|
||||
},
|
||||
"backgroundTextStyle": "dark",
|
||||
"navigationBarBackgroundColor": "#fff8e1",
|
||||
"navigationBarTitleText": "常规发单",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f5f5f5",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<!-- pages/merchant-regular-dispatch/merchant-regular-dispatch.wxml -->
|
||||
<view class="page sj-form-theme">
|
||||
<view class="balance-card">
|
||||
<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">{{balanceTip}}</text>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<view class="card-dot"></view>
|
||||
<text class="card-title">选择订单类型</text>
|
||||
</view>
|
||||
<scroll-view class="type-scroll" scroll-x="true" enable-flex>
|
||||
<view
|
||||
class="type-chip {{selectedTypeId === item.id ? 'type-chip--active' : ''}}"
|
||||
wx:for="{{shangpinList}}"
|
||||
wx:key="id"
|
||||
data-id="{{item.id}}"
|
||||
data-item="{{item}}"
|
||||
bindtap="onSelectType"
|
||||
>
|
||||
{{item.jieshao}}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<view class="card-dot"></view>
|
||||
<text class="card-title">选择订单模板</text>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
<text class="field-label">订单介绍</text>
|
||||
<text class="field-required">*</text>
|
||||
</view>
|
||||
<view class="field-picker" bindtap="openTemplateSheet">
|
||||
<text class="{{dingdanJieshao ? '' : 'field-picker-ph'}}">{{dingdanJieshao || '点击选择模板'}}</text>
|
||||
<text class="field-picker-arrow">▸</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="field" wx:if="{{dingdanJieshao}}">
|
||||
<view class="field-head">
|
||||
<text class="field-label">订单价格</text>
|
||||
<text class="field-required">*</text>
|
||||
</view>
|
||||
<view class="price-display">
|
||||
<text class="price-prefix">¥</text>
|
||||
<text class="price-value">{{jiage || '0.00'}}</text>
|
||||
</view>
|
||||
<view class="template-meta" wx:if="{{selectedLabelName || commissionEnabled}}">
|
||||
<text class="template-meta-label" wx:if="{{selectedLabelName}}">标签:{{selectedLabelName}}</text>
|
||||
<text class="template-meta-label" wx:if="{{commissionEnabled}}">押金:¥{{commissionValue}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<view class="card-dot"></view>
|
||||
<text class="card-title">填写订单信息</text>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
<text class="field-label">订单备注</text>
|
||||
<text class="field-optional">选填</text>
|
||||
<text class="field-count">{{dingdanBeizhu.length}}/50</text>
|
||||
</view>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="选填,补充其他信息"
|
||||
maxlength="50"
|
||||
value="{{dingdanBeizhu}}"
|
||||
bindinput="onDingdanBeizhuInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
<text class="field-label">老板昵称/ID</text>
|
||||
<text class="field-required">*</text>
|
||||
<text class="field-count">{{laobanName.length}}/15</text>
|
||||
</view>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="请输入老板昵称或ID"
|
||||
maxlength="15"
|
||||
value="{{laobanName}}"
|
||||
bindinput="onLaobanNameInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
<text class="field-label">拼多多订单号</text>
|
||||
<text class="field-optional">选填</text>
|
||||
</view>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="外部平台订单号"
|
||||
maxlength="64"
|
||||
value="{{waibuDingdanId}}"
|
||||
bindinput="onWaibuDingdanIdInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="field-head">
|
||||
<text class="field-label">指定UID</text>
|
||||
<text class="field-optional">选填</text>
|
||||
</view>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="指定某位接单员接单"
|
||||
maxlength="10"
|
||||
value="{{zhidingUid}}"
|
||||
bindinput="onZhidingUidInput"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-space"></view>
|
||||
|
||||
<view class="submit-bar">
|
||||
<view
|
||||
class="submit-btn {{isSubmitting ? 'submit-btn--disabled' : ''}}"
|
||||
bindtap="onSubmit"
|
||||
hover-class="submit-btn--hover"
|
||||
>
|
||||
{{isSubmitting ? '派发中...' : '立即派发'}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="loading-mask" wx:if="{{isLoading}}">
|
||||
<view class="loading-box">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 模板选择底部弹层 -->
|
||||
<block wx:if="{{showTemplateSheet}}">
|
||||
<view class="sheet-mask" bindtap="closeTemplateSheet"></view>
|
||||
<view class="sheet-panel">
|
||||
<view class="sheet-hd">
|
||||
<text class="sheet-title">选择订单模板</text>
|
||||
<text class="sheet-close" bindtap="closeTemplateSheet">×</text>
|
||||
</view>
|
||||
<view class="sheet-search">
|
||||
<input
|
||||
class="sheet-search-input"
|
||||
placeholder="搜索模板ID/介绍..."
|
||||
value="{{templateKeyword}}"
|
||||
bindinput="onTemplateKeywordInput"
|
||||
bindconfirm="onTemplateSearch"
|
||||
/>
|
||||
<text class="sheet-search-btn" bindtap="onTemplateSearch">搜索</text>
|
||||
</view>
|
||||
<scroll-view class="sheet-list" scroll-y bindscrolltolower="onTemplateScrollLower">
|
||||
<block wx:for="{{templateList}}" wx:key="mobanId">
|
||||
<view class="sheet-item" data-item="{{item}}" bindtap="onSelectTemplate">
|
||||
<view class="sheet-item-intro">{{item.jieshao || '无介绍'}}</view>
|
||||
<view class="sheet-item-row">
|
||||
<chenghao-tag wx:if="{{item.labelName}}" mingcheng="{{item.labelName}}" texiaoJson="{{''}}"/>
|
||||
<text class="sheet-item-price">¥{{item.jiage || '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="sheet-empty" wx:if="{{!templateLoading && templateList.length === 0}}">暂无模板</view>
|
||||
<view class="sheet-empty" wx:if="{{templateLoading}}">加载中...</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<global-notification id="global-notification" />
|
||||
<popup-notice id="popupNotice" />
|
||||
</view>
|
||||
@@ -0,0 +1,343 @@
|
||||
@import '../../styles/shangjia-xym-form.wxss';
|
||||
|
||||
page {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.balance-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
margin: 12rpx 0 8rpx;
|
||||
}
|
||||
|
||||
.balance-value {
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.balance-unit {
|
||||
font-size: 26rpx;
|
||||
margin-left: 6rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.balance-tip {
|
||||
font-size: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-dot {
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.type-scroll {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
padding: 8rpx 0;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.field:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.field-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.field-required {
|
||||
font-size: 26rpx;
|
||||
color: #e04040;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.field-optional {
|
||||
font-size: 20rpx;
|
||||
color: #bbb;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.field-count {
|
||||
font-size: 20rpx;
|
||||
color: #ccc;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 100%;
|
||||
height: 76rpx;
|
||||
background: #f7f7f7;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
border: 1rpx solid #eee;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.field-readonly {
|
||||
background: #fafafa;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.field-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 76rpx;
|
||||
background: #f7f7f7;
|
||||
border-radius: 12rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.field-picker-ph {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.field-picker-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #ccc;
|
||||
flex-shrink: 0;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.price-display {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
height: 76rpx;
|
||||
background: #fafafa;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.price-prefix {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #e04040;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #e04040;
|
||||
}
|
||||
|
||||
.template-meta {
|
||||
margin-top: 12rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.template-meta-label {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.bottom-space {
|
||||
height: 20rpx;
|
||||
}
|
||||
|
||||
.submit-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding: 16rpx 24rpx;
|
||||
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
height: 88rpx;
|
||||
background: #e04040;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.submit-btn--hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.submit-btn--disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.loading-mask {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(255,255,255,0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
background: #fff;
|
||||
padding: 48rpx 64rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border: 4rpx solid #eee;
|
||||
border-top-color: #333;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 模板选择底部弹层 */
|
||||
.sheet-mask {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.45);
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.sheet-panel {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
max-height: 75vh;
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
z-index: 10002;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sheet-hd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 24rpx 16rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.sheet-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.sheet-close {
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
line-height: 1;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.sheet-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 16rpx 24rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 16rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.sheet-search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.sheet-search-btn {
|
||||
font-size: 26rpx;
|
||||
color: #e04040;
|
||||
font-weight: 600;
|
||||
padding-left: 16rpx;
|
||||
}
|
||||
|
||||
.sheet-list {
|
||||
flex: 1;
|
||||
min-height: 200rpx;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.sheet-item {
|
||||
padding: 24rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.sheet-item:active {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.sheet-item-intro {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.sheet-item-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sheet-item-price {
|
||||
font-size: 26rpx;
|
||||
color: #e04040;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sheet-empty {
|
||||
text-align: center;
|
||||
padding: 60rpx 24rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
@@ -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)}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "操作日志",
|
||||
"enablePullDownRefresh": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
@@ -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();
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "角色与权限",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
+285
-63
@@ -2,6 +2,14 @@
|
||||
|
||||
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 { fetchMerchantOrderStats, applyOrderStatsToPage } from '../../utils/merchant-order-stats.js';
|
||||
import { clearCacheAndEnterNormal } from '../../utils/base-page.js';
|
||||
import { ICON_KEYS, resolveMiniappIcon } from '../../utils/miniapp-icons.js';
|
||||
import { getDefaultAvatarUrl } from '../../utils/avatar.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
@@ -32,11 +40,20 @@ Page(createPage({
|
||||
|
||||
// 商家状态
|
||||
isShangjia: false,
|
||||
isStaffMode: false,
|
||||
staffRoleName: '',
|
||||
quotaAvailable: '0.00',
|
||||
canDispatch: true,
|
||||
canStaffManage: true,
|
||||
canAudit: true,
|
||||
showRechargeWithdraw: true,
|
||||
isAutoRegistering: false,
|
||||
staffBooting: false,
|
||||
|
||||
// 用户基础信息
|
||||
uid: '',
|
||||
avatarUrl: '',
|
||||
defaultAvatarUrl: '',
|
||||
nicheng: '',
|
||||
|
||||
// 商家核心数据
|
||||
@@ -48,16 +65,33 @@ Page(createPage({
|
||||
jinridingdan: 0,
|
||||
jinrituikuan: 0,
|
||||
chenghaoList: [],
|
||||
identityTagList: [],
|
||||
|
||||
// 交互控制
|
||||
inviteCode: '',
|
||||
pendingCount: 0,
|
||||
punishPending: 0,
|
||||
canViewFinance: true,
|
||||
orderStats: {
|
||||
pending_count: 0,
|
||||
pending_amount: '0.00',
|
||||
completed_count: 0,
|
||||
completed_amount: '0.00',
|
||||
refund_count: 0,
|
||||
refund_amount: '0.00',
|
||||
dispatch_count: 0,
|
||||
dispatch_amount: '0.00',
|
||||
},
|
||||
penaltyStats: {
|
||||
fakuan_pending: 0,
|
||||
fakuan_pending_amount: '0.00',
|
||||
jifen_pending: 0,
|
||||
},
|
||||
statusBar: 20,
|
||||
navBar: 44,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
async onLoad(options) {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
statusBar: sys.statusBarHeight || 20,
|
||||
@@ -65,7 +99,12 @@ Page(createPage({
|
||||
});
|
||||
const parsed = parseSceneOptions(options);
|
||||
this.setupImageUrls();
|
||||
this.setData({ defaultAvatarUrl: getDefaultAvatarUrl(app) });
|
||||
if (wx.getStorageSync('token')) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
}
|
||||
this.checkRoleStatus();
|
||||
syncStaffUi(this);
|
||||
|
||||
const inviteCode = parsed.inviteCode;
|
||||
if (inviteCode) {
|
||||
@@ -95,69 +134,185 @@ Page(createPage({
|
||||
}
|
||||
|
||||
this.checkColdStartPopup('shangjiaduan');
|
||||
this.loadMerchantDataIfNeeded(true);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
async onShow() {
|
||||
this.registerNotificationComponent();
|
||||
if (this.data.isShangjia) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
this.refreshShangjiaInfo();
|
||||
this.loadPendingCount();
|
||||
this.loadPunishPending();
|
||||
this.syncRoleStatusFromStorage();
|
||||
syncStaffUi(this);
|
||||
if (app.globalData.ossImageUrl) {
|
||||
this.setupImageUrls();
|
||||
}
|
||||
this.loadMerchantDataIfNeeded();
|
||||
},
|
||||
|
||||
syncRoleStatusFromStorage() {
|
||||
try {
|
||||
const uid = wx.getStorageSync('uid');
|
||||
const portal = isMerchantPortalUser();
|
||||
const staffFlag = Number(wx.getStorageSync('staffstatus')) === 1;
|
||||
if (portal || staffFlag) {
|
||||
this.setData({ isShangjia: portal || staffFlag, uid: uid || '', staffBooting: false, isLoading: false });
|
||||
} else {
|
||||
this.setData({ isShangjia: false, staffBooting: false, isLoading: false });
|
||||
}
|
||||
} catch (error) {
|
||||
this.setData({ isShangjia: false, staffBooting: false, isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
// 图片路径初始化
|
||||
async loadMerchantDataIfNeeded(force = false) {
|
||||
if (!this.data.isShangjia && !isMerchantPortalUser()) return;
|
||||
if (!force && this._merchantDataLoaded) return;
|
||||
try {
|
||||
const token = wx.getStorageSync('token');
|
||||
if (token && Number(wx.getStorageSync('shangjiastatus')) !== 1 && !isStaffMode()) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
} else if (isStaffMode()) {
|
||||
await refreshStaffContext(request);
|
||||
}
|
||||
this.syncRoleStatusFromStorage();
|
||||
syncStaffUi(this);
|
||||
if (this.data.isShangjia || isMerchantPortalUser()) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
if (isStaffMode()) {
|
||||
await this.loadStaffProfile();
|
||||
} else {
|
||||
await this.getShangjiaInfo();
|
||||
}
|
||||
this.loadPendingCount();
|
||||
if (!isStaffMode()) {
|
||||
this.loadPunishPending();
|
||||
}
|
||||
this.loadIdentityTags();
|
||||
this._merchantDataLoaded = true;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('商家个人中心加载', err);
|
||||
this.setData({ isLoading: false, staffBooting: false });
|
||||
}
|
||||
},
|
||||
|
||||
// 图片路径:样式不变,仅图标地址走存储桶 + 后台可配置
|
||||
setupImageUrls() {
|
||||
const ossBase = app.globalData.ossImageUrl || '';
|
||||
const imgDir = `${ossBase}beijing/shangjiaduan/`;
|
||||
const icon = (key, ossName) => resolveMiniappIcon(app, key, imgDir + ossName);
|
||||
this.setData({
|
||||
imgUrls: this.initImageUrls({
|
||||
pageBg: `${imgDir}page_bg.png`,
|
||||
avatarCardBg: `${imgDir}avatar_card_bg.png`,
|
||||
assetCardBg: `${imgDir}asset_card_bg.png`,
|
||||
statCardBg: `${imgDir}stat_card_bg.png`,
|
||||
funcItemBg: `${imgDir}func_item_bg.png`,
|
||||
primaryFuncBg: `${imgDir}primary_func_bg.png`,
|
||||
highlightFuncBg: `${imgDir}highlight_func_bg.png`,
|
||||
iconRelease: `${imgDir}icon_release.png`,
|
||||
iconFast: `${imgDir}icon_fast.png`,
|
||||
iconOrders: `${imgDir}icon_orders.png`,
|
||||
iconService: `${imgDir}icon_service.png`,
|
||||
iconPunish: `${imgDir}icon_punish.png`,
|
||||
iconRank: `${imgDir}icon_rank.png`,
|
||||
iconRecharge: `${imgDir}icon_recharge.png`,
|
||||
iconWithdraw: `${imgDir}icon_withdraw.png`,
|
||||
iconRefresh: `${imgDir}icon_refresh.png`,
|
||||
iconCopy: `${imgDir}icon_copy.png`,
|
||||
avatarFrame: `${imgDir}avatar_frame.png`,
|
||||
iconClear: `${ossBase}beijing/tubiao/grzx_qingchu.jpg`,
|
||||
pageBg: imgDir + 'page_bg.png',
|
||||
avatarCardBg: imgDir + 'avatar_card_bg.png',
|
||||
assetCardBg: imgDir + 'asset_card_bg.png',
|
||||
statCardBg: imgDir + 'stat_card_bg.png',
|
||||
funcItemBg: imgDir + 'func_item_bg.png',
|
||||
primaryFuncBg: imgDir + 'primary_func_bg.png',
|
||||
highlightFuncBg: imgDir + 'highlight_func_bg.png',
|
||||
iconRelease: icon(ICON_KEYS.MERCHANT_CUSTOM, 'icon_release.png'),
|
||||
iconFast: icon(ICON_KEYS.MERCHANT_REGULAR, 'icon_fast.png'),
|
||||
iconOrders: icon(ICON_KEYS.MERCHANT_LINK, 'icon_orders.png'),
|
||||
iconService: icon(ICON_KEYS.MERCHANT_KEFU_KEY, 'icon_service.png'),
|
||||
iconKefuList: icon(ICON_KEYS.MERCHANT_KEFU_LIST, 'icon_kefu_list.png'),
|
||||
iconPunish: icon(ICON_KEYS.MERCHANT_PUNISH, 'icon_punish.png'),
|
||||
iconRank: icon(ICON_KEYS.MERCHANT_RANK, 'icon_rank.png'),
|
||||
iconWithdraw: imgDir + 'icon_withdraw.png',
|
||||
iconRefresh: icon(ICON_KEYS.MERCHANT_REFRESH, 'icon_refresh.png'),
|
||||
iconCopy: imgDir + 'icon_copy.png',
|
||||
avatarFrame: imgDir + 'avatar_frame.png',
|
||||
iconClear: ossBase ? `${ossBase}beijing/tubiao/grzx_qingchu.jpg` : '',
|
||||
iconSwitch: '/images/_exit.png',
|
||||
})
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
// 商家状态检查(覆盖基类 checkRoleStatus,增加 getShangjiaInfo 调用)
|
||||
// 商家状态检查(首次进入拉数据;onShow 不重复请求)
|
||||
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);
|
||||
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',
|
||||
@@ -173,8 +328,8 @@ Page(createPage({
|
||||
this.setData({
|
||||
nicheng: data.nicheng || '',
|
||||
sjyue: data.sjyue || '0.00',
|
||||
fabu: data.fadanzong || 0,
|
||||
tuikuan: data.tuikuanzong || 0,
|
||||
fabu: data.fabu || data.fadanzong || 0,
|
||||
tuikuan: data.tuikuan || data.tuikuanzong || 0,
|
||||
jinriliushui: data.riliushui || '0.00',
|
||||
jinyueliushui: data.yueliushui || '0.00',
|
||||
jinridingdan: data.jinripaidan || 0,
|
||||
@@ -182,6 +337,7 @@ Page(createPage({
|
||||
chenghaoList: chenghaoList,
|
||||
isLoading: false,
|
||||
});
|
||||
this.loadAvatar();
|
||||
|
||||
if (this.data.isAutoRegistering) {
|
||||
this.setData({ isAutoRegistering: false });
|
||||
@@ -192,37 +348,60 @@ Page(createPage({
|
||||
},
|
||||
|
||||
refreshShangjiaInfo() {
|
||||
this.throttledRefresh(() => this.getShangjiaInfo());
|
||||
if (isStaffMode()) {
|
||||
this.throttledRefresh(() => this.loadStaffProfile());
|
||||
return;
|
||||
}
|
||||
this.throttledRefresh(() => {
|
||||
this.getShangjiaInfo();
|
||||
this.loadPendingCount();
|
||||
this.loadPunishPending();
|
||||
this.loadIdentityTags();
|
||||
});
|
||||
},
|
||||
|
||||
async loadPendingCount() {
|
||||
async onPullDownRefresh() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/sjdingdanhq',
|
||||
method: 'POST',
|
||||
data: { zhuangtai_list: [8], page: 1, page_size: 1 },
|
||||
});
|
||||
if (res && res.data && (res.data.code === 0 || res.data.code === 200)) {
|
||||
this.setData({ pendingCount: res.data.data.pending_count || 0 });
|
||||
if (isStaffMode()) {
|
||||
await this.loadStaffProfile();
|
||||
} else {
|
||||
await Promise.all([
|
||||
this.getShangjiaInfo(),
|
||||
this.loadDashboardStats(),
|
||||
this.loadIdentityTags(),
|
||||
]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('待结算数量失败', e);
|
||||
} finally {
|
||||
wx.stopPullDownRefresh();
|
||||
}
|
||||
},
|
||||
|
||||
async loadPunishPending() {
|
||||
async loadDashboardStats() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/sjcfjlhq',
|
||||
method: 'POST',
|
||||
data: { page: 1, page_size: 1, zhuangtai: 0 },
|
||||
});
|
||||
const data = await fetchMerchantOrderStats(request, {});
|
||||
applyOrderStatsToPage(this, data);
|
||||
} catch (e) {
|
||||
console.warn('经营统计失败', e);
|
||||
}
|
||||
},
|
||||
|
||||
async loadPendingCount() {
|
||||
return this.loadDashboardStats();
|
||||
},
|
||||
|
||||
async loadPunishPending() {
|
||||
return this.loadDashboardStats();
|
||||
},
|
||||
|
||||
async loadIdentityTags() {
|
||||
try {
|
||||
const res = await request({ url: '/jituan/identity-tag/my-tags', method: 'POST' });
|
||||
if (res && res.data && (res.data.code === 0 || res.data.code === 200)) {
|
||||
const d = res.data.data || {};
|
||||
this.setData({ punishPending: d.daichuli_count || d.pending_count || 0 });
|
||||
this.setData({ identityTagList: d.shangjia || [] });
|
||||
}
|
||||
} catch (e) {
|
||||
/* 接口字段未定时静默 */
|
||||
/* 静默 */
|
||||
}
|
||||
},
|
||||
|
||||
@@ -262,9 +441,44 @@ Page(createPage({
|
||||
}
|
||||
},
|
||||
|
||||
editNicheng() {
|
||||
if (!guardMerchantOwnerAction('子客服无法修改商家昵称')) return;
|
||||
wx.showModal({
|
||||
title: '修改昵称',
|
||||
editable: true,
|
||||
placeholderText: '请输入商家昵称',
|
||||
content: this.data.nicheng || '',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return;
|
||||
const nicheng = (res.content || '').trim();
|
||||
if (!nicheng) {
|
||||
wx.showToast({ title: '昵称不能为空', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const r = await request({
|
||||
url: '/yonghu/shangjiagxnc',
|
||||
method: 'POST',
|
||||
data: { nicheng },
|
||||
});
|
||||
if (r.data && r.data.code === 200) {
|
||||
wx.setStorageSync('nicheng', nicheng);
|
||||
this.setData({ nicheng });
|
||||
wx.showToast({ title: '昵称已更新', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: (r.data && r.data.msg) || '更新失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 页面跳转
|
||||
goToRegularPaiDan() { wx.navigateTo({ url: '/pages/merchant-regular-dispatch/merchant-regular-dispatch' }); },
|
||||
goToPaiDan() { wx.navigateTo({ url: '/pages/merchant-dispatch/merchant-dispatch' }); },
|
||||
goToJiSuPaiDan() {
|
||||
goToLinkPaiDan() {
|
||||
wx.vibrateShort({ type: 'medium' });
|
||||
wx.navigateTo({ url: '/pages/express-order/express-order' });
|
||||
},
|
||||
@@ -273,10 +487,18 @@ 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' }); },
|
||||
goToMessages() { wx.navigateTo({ url: '/pages/merchant-penalty/merchant-penalty' }); },
|
||||
}, {
|
||||
roleConfig: {
|
||||
role: 'shangjia',
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"navigationStyle": "custom",
|
||||
"backgroundColor": "#fff8e1",
|
||||
"backgroundTextStyle": "dark",
|
||||
"enablePullDownRefresh": false
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
+106
-29
@@ -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>
|
||||
@@ -22,21 +40,29 @@
|
||||
<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 class="nav-bar flexm sj-nav-bar" style="height:{{navBar - statusBar}}px">
|
||||
<text class="sj-nav-title">我的</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="refresh-float" bindtap="refreshShangjiaInfo">
|
||||
<view class="refresh-float" wx:if="{{false}}">
|
||||
<image class="refresh-float-ico" src="{{imgUrls.iconRefresh}}" mode="aspectFit"/>
|
||||
</view>
|
||||
|
||||
<scroll-view class="content" scroll-y enhanced show-scrollbar="{{false}}">
|
||||
<view class="user-info flexb">
|
||||
<view class="flex">
|
||||
<image class="avatar" src="{{avatarUrl || imgUrls.iconService}}" mode="aspectFill" bindtap="previewAvatar"/>
|
||||
<image class="avatar" src="{{avatarUrl || defaultAvatarUrl}}" mode="aspectFill" bindtap="previewAvatar"/>
|
||||
<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 wx:if="{{!isStaffMode}}" class="nickname-edit-btn flexm" bindtap="editNicheng">修改昵称</view>
|
||||
<view class="identity-tags-row flex" wx:if="{{identityTagList.length > 0}}">
|
||||
<view class="badge-tag-wrap" wx:for="{{identityTagList}}" wx:key="id">
|
||||
<chenghao-tag mingcheng="{{item.mingcheng}}" texiaoJson="{{item.texiao_json}}"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sn-row flex" bindtap="copyUid">
|
||||
<text class="sn">ID: {{uid || '--'}}</text>
|
||||
@@ -44,6 +70,9 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-header-actions">
|
||||
<image class="header-action-ico" src="{{imgUrls.iconRefresh}}" mode="aspectFit" bindtap="refreshShangjiaInfo"/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="badge-zone" wx:if="{{chenghaoList.length > 0}}">
|
||||
@@ -56,29 +85,65 @@
|
||||
|
||||
<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>
|
||||
</view>
|
||||
|
||||
<view class="sj-panel sj-mx20 data-board" wx:if="{{canViewFinance || !isStaffMode}}">
|
||||
<view class="sj-section-hd">经营数据</view>
|
||||
<view class="data-highlight flexb">
|
||||
<view class="data-hl-item" bindtap="goToPendingOrders">
|
||||
<text class="data-hl-num accent">{{orderStats.pending_count}}</text>
|
||||
<text class="data-hl-lbl">待结算(单)</text>
|
||||
<text class="data-hl-sub" wx:if="{{canViewFinance}}">¥{{orderStats.pending_amount}}</text>
|
||||
</view>
|
||||
<view class="data-hl-item">
|
||||
<text class="data-hl-num">{{orderStats.completed_count}}</text>
|
||||
<text class="data-hl-lbl">成交总量</text>
|
||||
<text class="data-hl-sub" wx:if="{{canViewFinance}}">¥{{orderStats.completed_amount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="data-grid">
|
||||
<view class="data-cell">
|
||||
<text class="data-cell-num">{{orderStats.refund_count}}</text>
|
||||
<text class="data-cell-lbl">退款总量</text>
|
||||
<text class="data-cell-sub" wx:if="{{canViewFinance}}">¥{{orderStats.refund_amount}}</text>
|
||||
</view>
|
||||
<view class="data-cell">
|
||||
<text class="data-cell-num">{{orderStats.dispatch_count}}</text>
|
||||
<text class="data-cell-lbl">发单总量</text>
|
||||
<text class="data-cell-sub" wx:if="{{canViewFinance}}">¥{{orderStats.dispatch_amount}}</text>
|
||||
</view>
|
||||
<view class="data-cell">
|
||||
<text class="data-cell-num">{{jinriliushui}}</text>
|
||||
<text class="data-cell-lbl">今日流水</text>
|
||||
</view>
|
||||
<view class="data-cell">
|
||||
<text class="data-cell-num">{{jinyueliushui}}</text>
|
||||
<text class="data-cell-lbl">今月流水</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="penalty-mini" wx:if="{{!isStaffMode}}">
|
||||
<text>处罚待处理:罚单 {{penaltyStats.fakuan_pending}}(¥{{penaltyStats.fakuan_pending_amount}})· 积分 {{penaltyStats.jifen_pending}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="freeze-row flexa">
|
||||
<view class="freeze-col flexmc">
|
||||
<text class="freeze-num">{{jinriliushui}}</text>
|
||||
<text class="freeze-lbl">今日流水</text>
|
||||
</view>
|
||||
<view class="freeze-col flexmc">
|
||||
<text class="freeze-num">{{jinyueliushui}}</text>
|
||||
<text class="freeze-lbl">今月流水</text>
|
||||
</view>
|
||||
<view class="freeze-col flexmc">
|
||||
<text class="freeze-num">{{jinridingdan}}</text>
|
||||
<text class="freeze-lbl">今日派单</text>
|
||||
</view>
|
||||
<view class="freeze-col flexmc">
|
||||
<text class="freeze-num">{{jinrituikuan}}</text>
|
||||
<text class="freeze-lbl">今日退款</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="sj-panel sj-mx20 order-nav-box">
|
||||
@@ -106,27 +171,39 @@
|
||||
<view class="sj-panel sj-mx20">
|
||||
<view class="sj-section-hd">交易与发单</view>
|
||||
<view class="sj-func-grid">
|
||||
<view class="sj-func-item" bindtap="goToPaiDan">
|
||||
<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="goToRegularPaiDan" wx:if="{{canDispatch}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconFast}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">常规单</text>
|
||||
<text class="sj-func-txt">常规派单</text>
|
||||
</view>
|
||||
<view class="sj-func-item" bindtap="goToKefuKey">
|
||||
<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="goToLinkPaiDan" wx:if="{{canDispatch}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconOrders}}" mode="aspectFit"/>
|
||||
<text class="sj-func-txt">链接派单</text>
|
||||
</view>
|
||||
<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">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconService}}" mode="aspectFit"/>
|
||||
<view class="sj-func-item" bindtap="goToKefuList" wx:if="{{canStaffManage}}">
|
||||
<image class="sj-func-icon" src="{{imgUrls.iconKefuList}}" 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>
|
||||
|
||||
@@ -15,6 +15,18 @@ page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sj-nav-bar {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-refresh-ico {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -42,6 +54,20 @@ page {
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.user-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-action-ico {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 6rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
padding: 10rpx 30rpx;
|
||||
}
|
||||
@@ -67,6 +93,19 @@ page {
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.nickname-edit-btn {
|
||||
display: inline-flex;
|
||||
margin-top: 10rpx;
|
||||
padding: 8rpx 22rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #7a5a12;
|
||||
background: linear-gradient(180deg, #fff8e1, #ffe9b8);
|
||||
border: 1rpx solid #e8c547;
|
||||
border-radius: 28rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(200, 160, 40, 0.15);
|
||||
}
|
||||
|
||||
.sn-row {
|
||||
margin-top: 8rpx;
|
||||
align-items: center;
|
||||
@@ -100,6 +139,16 @@ page {
|
||||
margin-right: -8rpx;
|
||||
}
|
||||
|
||||
.identity-tags-row {
|
||||
flex-wrap: wrap;
|
||||
gap: 4rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.identity-tags-row .badge-tag-wrap {
|
||||
transform: scale(0.68);
|
||||
}
|
||||
|
||||
.wallet-btns .sj-btn-outline {
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
@@ -132,6 +181,40 @@ page {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.data-board { margin-top: 16rpx; padding-bottom: 8rpx; }
|
||||
.data-highlight {
|
||||
display: flex;
|
||||
padding: 8rpx 0 16rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.data-hl-item { flex: 1; text-align: center; }
|
||||
.data-hl-num { font-size: 40rpx; font-weight: 700; color: #333; display: block; }
|
||||
.data-hl-num.accent { color: #E65100; }
|
||||
.data-hl-lbl { font-size: 24rpx; color: #888; display: block; margin-top: 4rpx; }
|
||||
.data-hl-sub { font-size: 22rpx; color: #1565C0; display: block; margin-top: 4rpx; }
|
||||
.data-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.data-cell {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
padding: 12rpx 8rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.data-cell-num { font-size: 30rpx; font-weight: 600; color: #333; display: block; }
|
||||
.data-cell-lbl { font-size: 22rpx; color: #999; display: block; margin-top: 4rpx; }
|
||||
.data-cell-sub { font-size: 20rpx; color: #1565C0; display: block; }
|
||||
.penalty-mini {
|
||||
margin-top: 8rpx;
|
||||
padding-top: 12rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
font-size: 22rpx;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.unreg-area {
|
||||
flex: 1;
|
||||
padding: 40rpx 0 16rpx;
|
||||
@@ -149,6 +232,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;
|
||||
|
||||
+129
-39
@@ -4,11 +4,23 @@ 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';
|
||||
import {
|
||||
ICON_KEYS,
|
||||
resolveMiniappIcon,
|
||||
getPindaoConfig,
|
||||
resolvePindaoImages,
|
||||
refreshPindaoConfig,
|
||||
warmupPindaoConfig,
|
||||
PINDAO_ICON_FALLBACK,
|
||||
} from '../../utils/miniapp-icons.js';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
@@ -17,55 +29,62 @@ Page(createPage({
|
||||
userUid: '',
|
||||
showLoginBtn: false,
|
||||
orderCounts: { daifuwu:0, fuwuzhong:0, yiwancheng:0, yituikuan:0 },
|
||||
// 图标
|
||||
settingIcon: '', daifuwuIcon: '', fuwuzhongIcon: '', yiwanchengIcon: '', yituikuanIcon: '',
|
||||
settingIcon: '', refreshIcon: '',
|
||||
daifuwuIcon: '', fuwuzhongIcon: '', yiwanchengIcon: '', yituikuanIcon: '',
|
||||
dashouIcon: '', shangjiaIcon: '', guanshiIcon: '', kefuIcon: '',
|
||||
zuzhangIcon: '', guanzhualongIcon: '', arrowRightIcon: '', qingchuIcon: '',
|
||||
zuzhangIcon: '', guanzhualongIcon: '', pindaoIcon: '', arrowRightIcon: '', qingchuIcon: '',
|
||||
dashouCertified: false,
|
||||
shangjiaCertified: false,
|
||||
bottomSafePadding: 0
|
||||
staffCertified: false,
|
||||
staffTag: '入驻',
|
||||
bottomSafePadding: 0,
|
||||
pindaoVisible: false,
|
||||
pindaoTitle: '频道',
|
||||
pindaoChannelNo: '',
|
||||
pindaoImages: [],
|
||||
showPindaoEntry: false,
|
||||
_profileRefreshing: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.initIconUrls()
|
||||
this.loadUserData()
|
||||
this.setBottomSafe()
|
||||
this.initIconUrls();
|
||||
this.loadUserData();
|
||||
this.setBottomSafe();
|
||||
this.updatePindaoEntry();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.initIconUrls();
|
||||
this.registerNotificationComponent();
|
||||
this.checkAvatarPrompt();
|
||||
backfillUserProfileCache(app);
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
ensurePhoneAuth({
|
||||
redirect: true,
|
||||
returnPage: '/pages/mine/mine',
|
||||
}).then((phoneOk) => {
|
||||
if (!phoneOk) return;
|
||||
this.registerNotificationComponent();
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
this.updatePindaoEntry();
|
||||
});
|
||||
},
|
||||
|
||||
updatePindaoEntry() {
|
||||
const dashou = isRoleStatusActive(wx.getStorageSync('dashoustatus'));
|
||||
this.setData({ showPindaoEntry: dashou });
|
||||
},
|
||||
|
||||
setBottomSafe() {
|
||||
const sys = wx.getSystemInfoSync()
|
||||
const safeBottom = sys.screenHeight - sys.safeArea.bottom // 底部安全区高度(px)
|
||||
const tabBarHeight = (sys.windowWidth / 750) * 100 // 100rpx 转为 px
|
||||
const sys = wx.getSystemInfoSync();
|
||||
const safeBottom = sys.screenHeight - sys.safeArea.bottom;
|
||||
const tabBarHeight = (sys.windowWidth / 750) * 100;
|
||||
this.setData({
|
||||
bottomSafePadding: safeBottom + tabBarHeight + 4 // 4px 微调,确保不留缝隙
|
||||
})
|
||||
},
|
||||
|
||||
checkAvatarPrompt() {
|
||||
const ds = wx.getStorageSync('dashoustatus')
|
||||
const tx = wx.getStorageSync('touxiang') || ''
|
||||
if (ds === 1 && (!tx || tx === 'a_long/morentouxiang.jpg')) {
|
||||
wx.showModal({
|
||||
title: '提示', content: '请先设置头像',
|
||||
confirmText: '去设置', confirmColor: '#C9A962',
|
||||
success: r => r.confirm && wx.navigateTo({ url: '/pages/edit/edit' })
|
||||
})
|
||||
}
|
||||
bottomSafePadding: safeBottom + tabBarHeight + 4,
|
||||
});
|
||||
},
|
||||
|
||||
initIconUrls() {
|
||||
const base = (app.globalData.ossImageUrl || '') + '/beijing/tubiao/'
|
||||
const base = (app.globalData.ossImageUrl || '') + 'beijing/tubiao/';
|
||||
this.setData({
|
||||
settingIcon: base + 'grzx_shezhi.jpg',
|
||||
refreshIcon: resolveMiniappIcon(app, ICON_KEYS.ICON_REFRESH, base + 'grzx_shezhi.jpg'),
|
||||
daifuwuIcon: base + 'grzx_daifuwu.jpg',
|
||||
fuwuzhongIcon: base + 'grzx_fuwuzhong.jpg',
|
||||
yiwanchengIcon: base + 'grzx_yiwancheng.jpg',
|
||||
@@ -76,14 +95,17 @@ Page(createPage({
|
||||
kefuIcon: base + 'grzx_kefu.jpg',
|
||||
zuzhangIcon: base + 'grzx_zuzhang.jpg',
|
||||
guanzhualongIcon: base + 'grzx_guanzhualong.jpg',
|
||||
pindaoIcon: resolveMiniappIcon(app, ICON_KEYS.MINE_PINDAO, base + 'grzx_pindao.jpg'),
|
||||
arrowRightIcon: base + 'grzx_arrow_right.jpg',
|
||||
qingchuIcon: base + 'grzx_qingchu.jpg',
|
||||
kaoheguanIcon: base + 'kaoheguan.png',
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
loadUserData() {
|
||||
const profile = getUserProfileForDisplay(app);
|
||||
const ctx = getStaffContext();
|
||||
const staffActive = isStaffMode();
|
||||
this.setData({
|
||||
avatarUrl: profile.avatarUrl,
|
||||
userNicheng: profile.nick,
|
||||
@@ -91,22 +113,72 @@ Page(createPage({
|
||||
showLoginBtn: !profile.isLoggedIn,
|
||||
dashouCertified: isRoleStatusActive(wx.getStorageSync('dashoustatus')),
|
||||
shangjiaCertified: isRoleStatusActive(wx.getStorageSync('shangjiastatus')),
|
||||
staffCertified: staffActive,
|
||||
staffTag: staffActive ? (ctx.role_name || '已入驻') : '入驻',
|
||||
});
|
||||
},
|
||||
|
||||
updateOrderCounts() {
|
||||
const c = app.globalData.dingdanTiaoshu || {}
|
||||
const c = app.globalData.dingdanTiaoshu || {};
|
||||
this.setData({ orderCounts: {
|
||||
daifuwu: c.daifuwu||0, fuwuzhong: c.fuwuzhong||0,
|
||||
yiwancheng: c.yiwancheng||0, yituikuan: c.yituikuan||0
|
||||
}})
|
||||
yiwancheng: c.yiwancheng||0, yituikuan: c.yituikuan||0,
|
||||
}});
|
||||
},
|
||||
|
||||
goToSetting() { wx.navigateTo({ url: '/pages/edit/edit' }) },
|
||||
async onTapRefresh() {
|
||||
if (this._profileRefreshing) return;
|
||||
this._profileRefreshing = true;
|
||||
wx.showLoading({ title: '刷新中', mask: true });
|
||||
try {
|
||||
if (getSessionToken(app)) {
|
||||
await restoreStaffContextAfterAuth();
|
||||
}
|
||||
backfillUserProfileCache(app);
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
wx.showToast({ title: '已刷新', icon: 'success' });
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '刷新失败', icon: 'none' });
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
this._profileRefreshing = false;
|
||||
}
|
||||
},
|
||||
|
||||
openPindaoModal() {
|
||||
const cfg = getPindaoConfig(app);
|
||||
const images = resolvePindaoImages(app);
|
||||
this.setData({
|
||||
pindaoVisible: true,
|
||||
pindaoTitle: cfg.title || '频道',
|
||||
pindaoChannelNo: cfg.channel_no || '',
|
||||
pindaoImages: images,
|
||||
});
|
||||
refreshPindaoConfig(app).then(({ cfg: latest, images: latestImages }) => {
|
||||
if (!this.data.pindaoVisible) return;
|
||||
this.setData({
|
||||
pindaoTitle: latest.title || '频道',
|
||||
pindaoChannelNo: latest.channel_no || '',
|
||||
pindaoImages: latestImages,
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
closePindaoModal() {
|
||||
this.setData({ pindaoVisible: false });
|
||||
},
|
||||
|
||||
goToGuanzhuA() {
|
||||
wx.navigateTo({ url: '/pages/manager-assign/manager-assign' });
|
||||
},
|
||||
|
||||
goToSetting() { wx.navigateTo({ url: '/pages/edit/edit' }); },
|
||||
|
||||
handleWechatLogin() {
|
||||
wx.showLoading({ title: '登录中...', mask: true });
|
||||
ensureLogin({ silent: false, page: this })
|
||||
.then(() => restoreStaffContextAfterAuth())
|
||||
.then(() => {
|
||||
this.loadUserData();
|
||||
this.updateOrderCounts();
|
||||
@@ -116,14 +188,19 @@ Page(createPage({
|
||||
.finally(() => wx.hideLoading());
|
||||
},
|
||||
|
||||
goToOrder(e) { wx.navigateTo({ url: `/pages/orders/orders?type=${e.currentTarget.dataset.type}` }) },
|
||||
goToOrder(e) { wx.navigateTo({ url: `/pages/orders/orders?type=${e.currentTarget.dataset.type}` }); },
|
||||
|
||||
goToAuth(e) {
|
||||
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 +213,18 @@ Page(createPage({
|
||||
},
|
||||
|
||||
goToCustomService() { this.goToKefu() },
|
||||
goToGuanzhuA() { wx.navigateTo({ url: '/pages/manager-assign/manager-assign' }) },
|
||||
}))
|
||||
|
||||
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(() => {});
|
||||
},
|
||||
}));
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"tab-bar": "/tab-bar/index"
|
||||
"tab-bar": "/tab-bar/index",
|
||||
"pindao-modal": "/components/pindao-modal/pindao-modal"
|
||||
},
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"navigationBarBackgroundColor": "#F7F3ED",
|
||||
|
||||
+27
-4
@@ -5,8 +5,13 @@
|
||||
<view class="main-content">
|
||||
<!-- 头部 -->
|
||||
<view class="hero">
|
||||
<view class="settings-icon" bindtap="goToSetting">
|
||||
<image src="{{settingIcon}}" mode="aspectFit" class="icon-dark" />
|
||||
<view class="hero-actions">
|
||||
<view class="hero-action-btn" bindtap="onTapRefresh">
|
||||
<image src="{{refreshIcon}}" mode="aspectFit" class="icon-dark" />
|
||||
</view>
|
||||
<view class="hero-action-btn" bindtap="goToSetting">
|
||||
<image src="{{settingIcon}}" mode="aspectFit" class="icon-dark" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-block">
|
||||
<view class="avatar-outside">
|
||||
@@ -63,7 +68,11 @@
|
||||
</view>
|
||||
<view class="service-unit" bindtap="goToGuanzhuA">
|
||||
<view class="icon-bg"><image src="{{guanzhualongIcon}}" mode="aspectFit" class="icon-color icon-size" /></view>
|
||||
<text class="service-name">关注星阙</text>
|
||||
<text class="service-name">关注快手</text>
|
||||
</view>
|
||||
<view class="service-unit" wx:if="{{showPindaoEntry}}" bindtap="openPindaoModal">
|
||||
<view class="icon-bg"><image src="{{pindaoIcon}}" mode="aspectFit" class="icon-color icon-size" /></view>
|
||||
<text class="service-name">频道</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -88,6 +97,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>
|
||||
|
||||
@@ -114,4 +130,11 @@
|
||||
</view>
|
||||
|
||||
<tab-bar />
|
||||
<global-notification id="global-notification" />
|
||||
<global-notification id="global-notification" />
|
||||
<pindao-modal
|
||||
visible="{{pindaoVisible}}"
|
||||
title="{{pindaoTitle}}"
|
||||
channelNo="{{pindaoChannelNo}}"
|
||||
images="{{pindaoImages}}"
|
||||
bind:close="closePindaoModal"
|
||||
/>
|
||||
+19
-2
@@ -67,8 +67,25 @@ page {
|
||||
width: 56rpx; height: 56rpx;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.settings-icon .icon-dark {
|
||||
width: 44rpx; height: 44rpx;
|
||||
.hero-actions {
|
||||
position: absolute;
|
||||
top: 24rpx;
|
||||
right: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
.hero-action-btn {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.hero-action-btn .icon-dark {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
.user-block {
|
||||
display: flex;
|
||||
|
||||
+128
-44
@@ -1,7 +1,9 @@
|
||||
// pages/order-pool/order-pool.js
|
||||
const app = getApp();
|
||||
import request from '../../utils/request.js';
|
||||
import { normalizeOrderTags } from '../../utils/order-tags.js';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { ICON_KEYS, resolveMiniappIcon, MERCHANT_GOLD_BANNER_FALLBACK } from '../../utils/miniapp-icons.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -28,7 +30,7 @@ Page({
|
||||
// 4. 刷新控制字段
|
||||
scrollViewRefreshing: false,
|
||||
lastRefreshTime: 0,
|
||||
refreshCooldown: 2000,
|
||||
refreshCooldown: 0,
|
||||
isLoadingMore: false,
|
||||
|
||||
// 5. 切换类型冷却时间控制
|
||||
@@ -45,12 +47,34 @@ Page({
|
||||
|
||||
// 商品轮播展示(只读 globalData,无额外接口)
|
||||
lunboList: [],
|
||||
gonggao: '',
|
||||
|
||||
// 标签筛选
|
||||
bankuaiBiaoqian: [],
|
||||
xuanzhongBiaoqianId: 0,
|
||||
|
||||
iconRefresh: '',
|
||||
goldBannerUrl: '',
|
||||
},
|
||||
|
||||
async onLoad(options) {
|
||||
const oss = app.globalData.ossImageUrl || '';
|
||||
this.syncConfiguredIcons(oss);
|
||||
this.syncShopBannerFromGlobal();
|
||||
this.loadGlobalStatus();
|
||||
this.checkAuthorization();
|
||||
const needLoadOrders = !app.globalData._acceptOrderDataLoaded;
|
||||
this.checkAuthorization(false, needLoadOrders);
|
||||
if (needLoadOrders && this.data.xuanzhongLeixingId) {
|
||||
app.globalData._acceptOrderDataLoaded = true;
|
||||
}
|
||||
},
|
||||
|
||||
syncConfiguredIcons(oss) {
|
||||
const ossBase = oss || app.globalData.ossImageUrl || '';
|
||||
this.setData({
|
||||
iconRefresh: resolveMiniappIcon(app, ICON_KEYS.ICON_REFRESH, ossBase + 'beijing/guanshiduan/icon-refresh.png'),
|
||||
goldBannerUrl: resolveMiniappIcon(app, ICON_KEYS.MERCHANT_GOLD_BANNER, MERCHANT_GOLD_BANNER_FALLBACK),
|
||||
});
|
||||
},
|
||||
// 页面隐藏时清理弹窗视图
|
||||
onHide: function () {
|
||||
@@ -62,9 +86,10 @@ Page({
|
||||
|
||||
onShow() {
|
||||
this.syncShopBannerFromGlobal();
|
||||
this.syncConfiguredIcons();
|
||||
this.registerNotificationComponent();
|
||||
this.loadGlobalStatus();
|
||||
this.checkAuthorization();
|
||||
this.checkAuthorization(true);
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
@@ -77,14 +102,17 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
/** 同步点单端已加载的商品轮播图,仅展示用 */
|
||||
/** 同步轮播与公告(仅读缓存) */
|
||||
syncShopBannerFromGlobal() {
|
||||
const oss = app.globalData.ossImageUrl || '';
|
||||
const lunboList = (app.globalData.shangpinlunbo || []).map((url) => {
|
||||
if (!url) return '';
|
||||
return url.startsWith('http') ? url : oss + url;
|
||||
}).filter(Boolean);
|
||||
this.setData({ lunboList });
|
||||
this.setData({
|
||||
lunboList,
|
||||
gonggao: app.globalData.shangpingonggao || '',
|
||||
});
|
||||
},
|
||||
|
||||
loadGlobalStatus() {
|
||||
@@ -108,7 +136,7 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
checkAuthorization() {
|
||||
checkAuthorization(skipReload = false, loadOrdersOnAuth = false) {
|
||||
const { dashoustatus, zhuanghaoStatus } = this.data;
|
||||
|
||||
const isDashouValid = (dashoustatus && dashoustatus == 1) || false;
|
||||
@@ -123,16 +151,19 @@ Page({
|
||||
this.setData({ unauthorizedMsg: msg || '无法使用抢单功能' });
|
||||
} else {
|
||||
this.setData({ showUnauthorized: false });
|
||||
if (!this.data.shangpinleixing.length) {
|
||||
this.loadShangpinLeixing();
|
||||
} else if (this.data.xuanzhongLeixingId) {
|
||||
this.loadDingdanList(true);
|
||||
if (!skipReload) {
|
||||
if (!this.data.shangpinleixing.length) {
|
||||
this.loadShangpinLeixing(loadOrdersOnAuth);
|
||||
} else if (loadOrdersOnAuth && this.data.xuanzhongLeixingId) {
|
||||
this.loadDingdanList(true);
|
||||
this.loadBankuaiBiaoqian();
|
||||
}
|
||||
PopupService.checkAndShow(this, 'jiedanchi');
|
||||
}
|
||||
PopupService.checkAndShow(this, 'jiedanchi');
|
||||
}
|
||||
},
|
||||
|
||||
async loadShangpinLeixing() {
|
||||
async loadShangpinLeixing(autoLoadOrders = false) {
|
||||
wx.showLoading({ title: '加载商品类型...' });
|
||||
try {
|
||||
const res = await request({
|
||||
@@ -160,8 +191,9 @@ Page({
|
||||
shangpinleixing: processedList,
|
||||
xuanzhongLeixingId: processedList[0]?.id || null
|
||||
});
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
if (autoLoadOrders && this.data.xuanzhongLeixingId) {
|
||||
this.loadDingdanList(true);
|
||||
this.loadBankuaiBiaoqian();
|
||||
}
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '加载商品类型失败', icon: 'none', duration: 2000 });
|
||||
@@ -182,6 +214,38 @@ Page({
|
||||
}));
|
||||
},
|
||||
|
||||
async loadBankuaiBiaoqian() {
|
||||
const leixingId = this.data.xuanzhongLeixingId;
|
||||
if (!leixingId) return;
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dengji/bkqhbq',
|
||||
method: 'POST',
|
||||
data: { leixing_id: leixingId }
|
||||
});
|
||||
if (res && res.data.code === 0) {
|
||||
this.setData({
|
||||
bankuaiBiaoqian: res.data.data.biaoqian_list || [],
|
||||
xuanzhongBiaoqianId: 0
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// 标签加载失败不影响主流程
|
||||
}
|
||||
},
|
||||
|
||||
async selectBiaoqian(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
if (id === this.data.xuanzhongBiaoqianId) return;
|
||||
this.setData({
|
||||
xuanzhongBiaoqianId: id,
|
||||
dingdanList: [],
|
||||
page: 1,
|
||||
hasMore: true
|
||||
});
|
||||
await this.loadDingdanList(true);
|
||||
},
|
||||
|
||||
async selectLeixing(e) {
|
||||
const now = Date.now();
|
||||
if (now - this.data.lastSwitchTime < this.data.switchCooldown) {
|
||||
@@ -202,9 +266,11 @@ Page({
|
||||
xuanzhongLeixingId: leixingId,
|
||||
dingdanList: [],
|
||||
page: 1,
|
||||
hasMore: true
|
||||
hasMore: true,
|
||||
xuanzhongBiaoqianId: 0
|
||||
});
|
||||
await this.loadDingdanList(true);
|
||||
await this.loadBankuaiBiaoqian();
|
||||
} catch (error) {
|
||||
console.error('切换类型失败:', error);
|
||||
} finally {
|
||||
@@ -231,7 +297,8 @@ Page({
|
||||
data: {
|
||||
leixing_id: this.data.xuanzhongLeixingId,
|
||||
page: loadPage,
|
||||
page_size: this.data.pageSize
|
||||
page_size: this.data.pageSize,
|
||||
biaoqian_id: this.data.xuanzhongBiaoqianId || undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -276,30 +343,56 @@ Page({
|
||||
|
||||
processDingdanItem(item) {
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
let fullTupianUrl = '';
|
||||
if (item.tupian) {
|
||||
fullTupianUrl = !item.tupian.startsWith('http') ? ossUrl + item.tupian : item.tupian;
|
||||
const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg');
|
||||
|
||||
const leixing = this.data.shangpinleixing.find((l) => l.id == item.leixing_id);
|
||||
let leixingIconUrl = '';
|
||||
if (leixing && leixing.full_tupian_url) {
|
||||
leixingIconUrl = leixing.full_tupian_url;
|
||||
} else if (leixing && leixing.tupian_url) {
|
||||
leixingIconUrl = leixing.tupian_url.startsWith('http')
|
||||
? leixing.tupian_url
|
||||
: ossUrl + leixing.tupian_url;
|
||||
} else {
|
||||
const leixing = this.data.shangpinleixing.find(l => l.id === item.leixing_id);
|
||||
fullTupianUrl = leixing ? leixing.full_tupian_url : '/images/default-order.png';
|
||||
leixingIconUrl = '/images/default-order.png';
|
||||
}
|
||||
|
||||
const isZhiding = item.zhuangtai === 7;
|
||||
|
||||
let zhidingAvatar = '';
|
||||
if (item.zhiding_avatar) {
|
||||
zhidingAvatar = !item.zhiding_avatar.startsWith('http') ? ossUrl + item.zhiding_avatar : item.zhiding_avatar;
|
||||
zhidingAvatar = !item.zhiding_avatar.startsWith('http')
|
||||
? ossUrl + item.zhiding_avatar
|
||||
: item.zhiding_avatar;
|
||||
}
|
||||
const zhidingNicheng = item.zhiding_nicheng || '';
|
||||
|
||||
let shangjiaAvatar = defaultAvatar;
|
||||
const sjAvatar = (item.sj_avatar || '').trim();
|
||||
if (sjAvatar) {
|
||||
shangjiaAvatar = sjAvatar.startsWith('http') ? sjAvatar : ossUrl + sjAvatar;
|
||||
}
|
||||
|
||||
let hasRequiredMember = true;
|
||||
if (item.yaoqiuleixing == 1 && item.huiyuan_id) {
|
||||
hasRequiredMember = this.data.huiyuanList.some((h) => h.huiyuanid == item.huiyuan_id);
|
||||
}
|
||||
|
||||
const tags = normalizeOrderTags(item);
|
||||
|
||||
return {
|
||||
...item,
|
||||
full_tupian_url: fullTupianUrl,
|
||||
isZhiding: isZhiding,
|
||||
full_tupian_url: leixingIconUrl,
|
||||
leixing_icon_url: leixingIconUrl,
|
||||
shangjia_avatar_full: shangjiaAvatar,
|
||||
isZhiding,
|
||||
isPingtai: item.pingtai == 1,
|
||||
isShangjia: item.pingtai == 2,
|
||||
zhiding_avatar_full: zhidingAvatar,
|
||||
zhiding_nicheng: zhidingNicheng
|
||||
zhiding_nicheng: zhidingNicheng,
|
||||
...tags,
|
||||
shangjia_youzhi: !!item.shangjia_youzhi,
|
||||
hasRequiredMember,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -422,15 +515,6 @@ Page({
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
const now = Date.now();
|
||||
if (now - this.data.lastRefreshTime < this.data.refreshCooldown) {
|
||||
wx.showToast({
|
||||
title: `操作太快,请${Math.ceil((this.data.refreshCooldown - (now - this.data.lastRefreshTime)) / 1000)}秒后再试`,
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.data.isLoading || this.data.isLoadingMore) return;
|
||||
if (this.data.hasMore && !this.data.isLoading && this.data.xuanzhongLeixingId) {
|
||||
this.setData({ page: this.data.page + 1 });
|
||||
@@ -438,24 +522,24 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
const now = Date.now();
|
||||
if (now - this.data.lastRefreshTime < this.data.refreshCooldown) {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
wx.showToast({
|
||||
title: `操作太快,请${Math.ceil((this.data.refreshCooldown - (now - this.data.lastRefreshTime)) / 1000)}秒后再试`,
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
onTapRefresh() {
|
||||
if (!this.data.xuanzhongLeixingId) {
|
||||
wx.showToast({ title: '请先选择订单类型', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (this.data.isLoading) return;
|
||||
this.setData({ page: 1, hasMore: true, scrollViewRefreshing: true });
|
||||
this.loadDingdanList(true);
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
if (this.data.isLoading) {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
return;
|
||||
}
|
||||
this.setData({
|
||||
this.setData({
|
||||
scrollViewRefreshing: true,
|
||||
lastRefreshTime: now
|
||||
lastRefreshTime: Date.now(),
|
||||
});
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
this.setData({ page: 1, hasMore: true });
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"navigationBarTitleText": "接单池",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "dark",
|
||||
"backgroundColor": "#f5f7fa",
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
}
|
||||
}
|
||||
"navigationBarTitleText": "接单池",
|
||||
"navigationBarBackgroundColor": "#f7dc51",
|
||||
"navigationBarTextStyle": "black",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "dark",
|
||||
"backgroundColor": "#fff8e1",
|
||||
"usingComponents": {
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag",
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
}
|
||||
}
|
||||
+299
-256
@@ -1,6 +1,5 @@
|
||||
<!--pages/qiangdan/qiangdan.wxml-->
|
||||
<!-- pages/order-pool/order-pool.wxml 抢单池(逍遥梦布局) -->
|
||||
<view class="qiangdan-page">
|
||||
<!-- 授权提示页(未开启打手功能时显示) -->
|
||||
<view class="unauthorized-container" wx:if="{{showUnauthorized}}">
|
||||
<view class="unauthorized-card">
|
||||
<view class="card-glow"></view>
|
||||
@@ -17,266 +16,310 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 正常内容(授权后显示) -->
|
||||
<block wx:else>
|
||||
<!-- 商品轮播(只读 globalData,与点单端同源) -->
|
||||
<view class="xym-lunbo-container" wx:if="{{lunboList.length > 0}}">
|
||||
<swiper
|
||||
class="xym-lunbo-swiper"
|
||||
indicator-dots="{{lunboList.length > 1}}"
|
||||
indicator-color="rgba(255,255,255,0.6)"
|
||||
indicator-active-color="#ffd061"
|
||||
autoplay="{{lunboList.length > 1}}"
|
||||
interval="4000"
|
||||
circular
|
||||
>
|
||||
<block wx:for="{{lunboList}}" wx:key="index">
|
||||
<swiper-item>
|
||||
<image class="xym-lunbo-image" src="{{item}}" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</block>
|
||||
</swiper>
|
||||
<view class="refresh-float" wx:if="{{xuanzhongLeixingId}}" bindtap="onTapRefresh">
|
||||
<image class="refresh-float-ico" src="{{iconRefresh}}" mode="aspectFit"/>
|
||||
</view>
|
||||
|
||||
<!-- 顶部区域:商品类型选择 -->
|
||||
<view class="leixing-quyu">
|
||||
<scroll-view class="leixing-scroll" scroll-x enhanced show-scrollbar="{{false}}">
|
||||
<view class="leixing-container">
|
||||
<block wx:for="{{shangpinleixing}}" wx:key="id">
|
||||
<view
|
||||
class="leixing-item {{xuanzhongLeixingId == item.id ? 'leixing-active' : ''}}"
|
||||
data-id="{{item.id}}"
|
||||
bindtap="selectLeixing"
|
||||
>
|
||||
<!-- 选中态的光晕特效 -->
|
||||
<view wx:if="{{xuanzhongLeixingId == item.id}}" class="guangyun-effect"></view>
|
||||
<image
|
||||
class="leixing-tupian"
|
||||
src="{{item.full_tupian_url}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="leixing-jieshao">{{item.jieshao}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="fenge-xian"></view>
|
||||
|
||||
<!-- 订单列表容器 -->
|
||||
<view class="dingdan-container">
|
||||
<scroll-view
|
||||
class="dingdan-scroll-view"
|
||||
scroll-y
|
||||
enhanced
|
||||
show-scrollbar="{{false}}"
|
||||
refresher-enabled="{{xuanzhongLeixingId != null}}"
|
||||
refresher-threshold="80"
|
||||
refresher-default-style="black"
|
||||
refresher-background="#f5f5f5"
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
bindscrolltolower="onReachBottom"
|
||||
>
|
||||
<!-- 下拉刷新区域 -->
|
||||
<view class="refresher-container" slot="refresher">
|
||||
<text wx:if="{{scrollViewRefreshing}}" class="refreshing-text">正在刷新...</text>
|
||||
<text wx:else class="pull-down-text">下拉刷新订单</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<view class="dingdan-list">
|
||||
<!-- 状态提示 -->
|
||||
<view wx:if="{{!xuanzhongLeixingId}}" class="empty-tip">
|
||||
<image src="/images/icon_select.png" class="tip-icon"></image>
|
||||
<text class="tip-text">请先选择上方订单类型</text>
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{dingdanList.length === 0 && !isLoading}}" class="empty-tip">
|
||||
<image src="/images/icon_empty_order.png" class="tip-icon"></image>
|
||||
<text class="tip-text">当前类型暂无待抢订单</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单卡片列表 -->
|
||||
<block wx:for="{{dingdanList}}" wx:key="dingdan_id">
|
||||
<!-- 平台订单卡片 (pingtai == 1) -->
|
||||
<view
|
||||
wx:if="{{item.isPingtai}}"
|
||||
class="dingdan-card pingtai-card"
|
||||
data-item="{{item}}"
|
||||
>
|
||||
<!-- 顶部信息区 -->
|
||||
<view class="card-top">
|
||||
<text class="creat-time">{{item.creat_time}}</text>
|
||||
<view class="top-line"></view>
|
||||
<text class="pingtai-tag">平台订单</text>
|
||||
</view>
|
||||
|
||||
<!-- 指定单标识行 - 新增头像和昵称 -->
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-row">
|
||||
<text class="zhiding-icon">指定</text>
|
||||
<image
|
||||
wx:if="{{item.zhiding_avatar_full}}"
|
||||
class="zhiding-avatar"
|
||||
src="{{item.zhiding_avatar_full}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="zhiding-nicheng">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
<text class="zhiding-uid">UID: {{item.zhiding_uid || '未知'}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区:圆形图片 + 介绍 -->
|
||||
<view class="card-content">
|
||||
<image
|
||||
class="neirong-tupian"
|
||||
src="{{item.full_tupian_url}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view
|
||||
class="jieshao-box {{item.jieshao && item.jieshao.length > 60 ? 'jieshao-ellipsis' : ''}}"
|
||||
data-type="jieshao"
|
||||
data-content="{{item.jieshao}}"
|
||||
bindtap="onViewDetail"
|
||||
>
|
||||
<text class="jieshao-label">介绍:</text>
|
||||
<text class="jieshao-text">{{item.jieshao || '暂无介绍'}}</text>
|
||||
<text wx:if="{{item.jieshao && item.jieshao.length > 60}}" class="view-more">[点击查看]</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备注区 -->
|
||||
<view
|
||||
wx:if="{{item.beizhu}}"
|
||||
class="beizhu-box {{item.beizhu.length > 40 ? 'beizhu-ellipsis' : ''}}"
|
||||
data-type="beizhu"
|
||||
data-content="{{item.beizhu}}"
|
||||
bindtap="onViewDetail"
|
||||
>
|
||||
<text class="beizhu-label">备注:</text>
|
||||
<text class="beizhu-text">{{item.beizhu}}</text>
|
||||
<text wx:if="{{item.beizhu.length > 40}}" class="view-more">[点击查看]</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部:分佣 + 抢单按钮 -->
|
||||
<view class="card-bottom">
|
||||
<view class="fenyong-box">
|
||||
<view class="fenyong-mark">佣</view>
|
||||
<text class="fenyong-text">分佣</text>
|
||||
<text class="fenyong-price">{{item.dashou_fencheng || 0}}</text>
|
||||
<text class="fenyong-unit">元</text>
|
||||
</view>
|
||||
<view
|
||||
class="qiangdan-btn pingtai-btn"
|
||||
data-item="{{item}}"
|
||||
bindtap="onQiangdanTap"
|
||||
>
|
||||
<text class="btn-text">立即抢单</text>
|
||||
<view class="btn-shine"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家订单卡片 (pingtai == 2) -->
|
||||
<view
|
||||
wx:elif="{{item.isShangjia}}"
|
||||
class="dingdan-card shangjia-card"
|
||||
data-item="{{item}}"
|
||||
>
|
||||
<!-- 顶部信息区 -->
|
||||
<view class="card-top">
|
||||
<text class="creat-time">{{item.creat_time}}</text>
|
||||
<view class="top-line"></view>
|
||||
<text class="pingtai-tag">商家发单</text>
|
||||
</view>
|
||||
|
||||
<!-- 指定单标识行 - 新增头像和昵称 -->
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-row">
|
||||
<text class="zhiding-icon">指定</text>
|
||||
<image
|
||||
wx:if="{{item.zhiding_avatar_full}}"
|
||||
class="zhiding-avatar"
|
||||
src="{{item.zhiding_avatar_full}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="zhiding-nicheng">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
<text class="zhiding-uid">UID: {{item.zhiding_uid || '未知'}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区:圆形商家头像 + 昵称 -->
|
||||
<view class="shangjia-info">
|
||||
<image
|
||||
class="shangjia-touxiang"
|
||||
src="{{item.full_tupian_url}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="shangjia-nicheng">{{item.sjnicheng || '未知商家'}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 介绍区 -->
|
||||
<view
|
||||
class="shangjia-jieshao {{item.jieshao && item.jieshao.length > 50 ? 'jieshao-ellipsis' : ''}}"
|
||||
data-type="jieshao"
|
||||
data-content="{{item.jieshao}}"
|
||||
bindtap="onViewDetail"
|
||||
>
|
||||
<text class="jieshao-label">订单介绍:</text>
|
||||
<text class="jieshao-text">{{item.jieshao || '暂无介绍'}}</text>
|
||||
<text wx:if="{{item.jieshao && item.jieshao.length > 50}}" class="view-more">[点击查看]</text>
|
||||
</view>
|
||||
|
||||
<!-- 备注区 -->
|
||||
<view
|
||||
wx:if="{{item.beizhu}}"
|
||||
class="shangjia-beizhu {{item.beizhu.length > 30 ? 'beizhu-ellipsis' : ''}}"
|
||||
data-type="beizhu"
|
||||
data-content="{{item.beizhu}}"
|
||||
bindtap="onViewDetail"
|
||||
>
|
||||
<text class="beizhu-label">商家备注:</text>
|
||||
<text class="beizhu-text">{{item.beizhu}}</text>
|
||||
<text wx:if="{{item.beizhu.length > 30}}" class="view-more">[点击查看]</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部:分佣 + 抢单按钮 -->
|
||||
<view class="card-bottom">
|
||||
<view class="fenyong-box">
|
||||
<view class="fenyong-mark">佣</view>
|
||||
<text class="fenyong-text">分佣</text>
|
||||
<text class="fenyong-price">{{item.dashou_fencheng || 0}}</text>
|
||||
<text class="fenyong-unit">元</text>
|
||||
</view>
|
||||
<view
|
||||
class="qiangdan-btn shangjia-btn"
|
||||
data-item="{{item}}"
|
||||
bindtap="onQiangdanTap"
|
||||
>
|
||||
<text class="btn-text">立即抢单</text>
|
||||
<view class="btn-shine"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 加载更多状态 -->
|
||||
<view wx:if="{{isLoadingMore && dingdanList.length > 0}}" class="loading-more">
|
||||
<text class="loading-text">加载更多订单中...</text>
|
||||
</view>
|
||||
<view wx:if="{{!hasMore && dingdanList.length > 0}}" class="no-more">
|
||||
<text class="no-more-text">没有更多订单了</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 全局加载遮罩 -->
|
||||
<view wx:if="{{isLoading && dingdanList.length === 0 && xuanzhongLeixingId}}" class="loading-mask">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-mask-text">订单加载中...</text>
|
||||
<scroll-view
|
||||
class="page-main-scroll"
|
||||
scroll-y
|
||||
enhanced
|
||||
show-scrollbar="{{false}}"
|
||||
refresher-enabled="{{xuanzhongLeixingId != null}}"
|
||||
refresher-threshold="80"
|
||||
refresher-default-style="black"
|
||||
refresher-background="#fff8e1"
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
bindscrolltolower="onReachBottom"
|
||||
>
|
||||
<view class="refresher-container" slot="refresher">
|
||||
<text wx:if="{{scrollViewRefreshing}}" class="refreshing-text">正在刷新...</text>
|
||||
<text wx:else class="pull-down-text">下拉刷新订单</text>
|
||||
</view>
|
||||
|
||||
<!-- 公告置顶(逍遥梦:轮播之上) -->
|
||||
<view class="xym-gonggao-bar" wx:if="{{gonggao}}">
|
||||
<image class="xym-gonggao-ico" src="/images/notice.png" mode="aspectFit"/>
|
||||
<text class="xym-gonggao-txt">{{gonggao}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 轮播 -->
|
||||
<view class="xym-lunbo-container" wx:if="{{lunboList.length > 0}}">
|
||||
<swiper
|
||||
class="xym-lunbo-swiper"
|
||||
indicator-dots="{{lunboList.length > 1}}"
|
||||
indicator-color="rgba(255,255,255,0.6)"
|
||||
indicator-active-color="#ffd061"
|
||||
autoplay="{{lunboList.length > 1}}"
|
||||
interval="4000"
|
||||
circular
|
||||
>
|
||||
<block wx:for="{{lunboList}}" wx:key="index">
|
||||
<swiper-item>
|
||||
<image class="xym-lunbo-image" src="{{item}}" mode="aspectFill"/>
|
||||
</swiper-item>
|
||||
</block>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<!-- 商品类型 -->
|
||||
<view class="leixing-quyu">
|
||||
<scroll-view class="leixing-scroll" scroll-x enhanced show-scrollbar="{{false}}">
|
||||
<view class="leixing-container">
|
||||
<block wx:for="{{shangpinleixing}}" wx:key="id">
|
||||
<view
|
||||
class="leixing-item {{xuanzhongLeixingId == item.id ? 'leixing-active' : ''}}"
|
||||
data-id="{{item.id}}"
|
||||
bindtap="selectLeixing"
|
||||
>
|
||||
<view wx:if="{{xuanzhongLeixingId == item.id}}" class="guangyun-effect"></view>
|
||||
<image class="leixing-tupian" src="{{item.full_tupian_url}}" mode="aspectFill"/>
|
||||
<text class="leixing-jieshao">{{item.jieshao}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 标签筛选 -->
|
||||
<view class="biaoqian-quyu" wx:if="{{bankuaiBiaoqian.length > 0}}">
|
||||
<scroll-view class="biaoqian-scroll" scroll-x enhanced show-scrollbar="{{false}}">
|
||||
<view class="biaoqian-container">
|
||||
<view class="biaoqian-item {{xuanzhongBiaoqianId == 0 ? 'biaoqian-active' : ''}}" data-id="0" bindtap="selectBiaoqian">
|
||||
<text class="quanbu-text">全部</text>
|
||||
</view>
|
||||
<block wx:for="{{bankuaiBiaoqian}}" wx:key="id">
|
||||
<view class="biaoqian-item {{xuanzhongBiaoqianId == item.id ? 'biaoqian-active' : ''}}" data-id="{{item.id}}" bindtap="selectBiaoqian">
|
||||
<chenghao-tag mingcheng="{{item.mingcheng}}" texiaoJson="{{item.texiao_json}}" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="fenge-xian"></view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<view class="dingdan-list">
|
||||
<view wx:if="{{!xuanzhongLeixingId}}" class="empty-tip">
|
||||
<image src="/images/icon_select.png" class="tip-icon"></image>
|
||||
<text class="tip-text">请先选择上方订单类型</text>
|
||||
</view>
|
||||
<view wx:elif="{{dingdanList.length === 0 && !isLoading}}" class="empty-tip">
|
||||
<image src="/images/icon_empty_order.png" class="tip-icon"></image>
|
||||
<text class="tip-text">当前类型暂无待抢订单</text>
|
||||
</view>
|
||||
|
||||
<block wx:for="{{dingdanList}}" wx:key="dingdan_id">
|
||||
<!-- 平台订单 -->
|
||||
<view wx:if="{{item.isPingtai}}" class="xym-order-item xym-pingtai-order" data-item="{{item}}">
|
||||
<view class="xym-pingtai-head flexb">
|
||||
<text class="xym-head-time">{{item.creat_time}}</text>
|
||||
<view class="xym-head-tag">平台订单</view>
|
||||
</view>
|
||||
<view class="xym-pingtai-body">
|
||||
<view class="order-con">
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip light">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="order-row myflex">
|
||||
<image class="o-logo" src="{{item.full_tupian_url}}" mode="aspectFill"/>
|
||||
<view class="o-col">
|
||||
<view class="o-goods light line2" data-type="jieshao" data-content="{{item.jieshao}}" bindtap="onViewDetail">{{item.jieshao || '暂无介绍'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="reward-badge platform-reward" wx:if="{{item.hasRequiredMember}}">
|
||||
<text class="reward-yen sm">¥</text>
|
||||
<text class="reward-num">{{item.dashou_fencheng || 0}}</text>
|
||||
</view>
|
||||
<view class="reward-badge member-tip" wx:else>
|
||||
<text class="reward-tip">未开通会员</text>
|
||||
</view>
|
||||
<view class="remark-block gold" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
<text class="remark-label">备注:</text>{{item.beizhu || '暂无'}}
|
||||
</view>
|
||||
<view wx:if="{{item.xuqiu_biaoqian.length > 0}}" class="xuqiu-tags-row">
|
||||
<text class="xuqiu-label">需求标签</text>
|
||||
<scroll-view class="biaoqian-inline-scroll" scroll-x enable-flex>
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="o-foot flexb">
|
||||
<text class="o-amt light">平台派单</text>
|
||||
<view class="qiangdan flexa btn-bg btn-qiang btn-qiang-brown" data-item="{{item}}" bindtap="onQiangdanTap">抢单</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 优质商家金牌卡 -->
|
||||
<view wx:elif="{{item.isShangjia && item.shangjia_youzhi}}" class="xym-order-item gold-card" data-item="{{item}}">
|
||||
<view class="gold-head flexb">
|
||||
<view class="kehuduan-banner-wrap">
|
||||
<image class="gold-banner" src="{{goldBannerUrl}}" mode="widthFix"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gold-body-wrap">
|
||||
<view class="order-con gold-inner">
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="order-row myflex">
|
||||
<image class="o-logo" src="{{item.leixing_icon_url}}" mode="aspectFill"/>
|
||||
<view class="o-col">
|
||||
<view class="o-goods line2" data-type="jieshao" data-content="{{item.jieshao}}" bindtap="onViewDetail">{{item.jieshao || '暂无介绍'}}</view>
|
||||
<view class="tag-row myflex">
|
||||
<text class="tag-pill tag-youzhi">优质商家</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="reward-badge normal-reward" wx:if="{{item.hasRequiredMember}}">
|
||||
<text class="reward-yen sm">¥</text>
|
||||
<text class="reward-num dark">{{item.dashou_fencheng || 0}}</text>
|
||||
</view>
|
||||
<view class="reward-badge member-tip" wx:else>
|
||||
<text class="reward-tip">未开通会员</text>
|
||||
</view>
|
||||
<view class="remark-block dark" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
<text class="remark-label">商家备注:</text>{{item.beizhu || '暂无'}}
|
||||
</view>
|
||||
<view wx:if="{{item.shangjia_biaoqian.length > 0 || item.shangjia_identity_biaoqian.length > 0 || item.xuqiu_biaoqian.length > 0}}" class="xuqiu-tags-row">
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex>
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.shangjia_identity_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="merchant-plain flexb">
|
||||
<view class="merchant-user myflex">
|
||||
<image class="m-avatar-sm" src="{{item.shangjia_avatar_full}}" mode="aspectFill"/>
|
||||
<view class="m-info">
|
||||
<text class="m-name-sm">{{item.sjnicheng || '未知商家'}}</text>
|
||||
<text class="m-sn-light">发布于 {{item.creat_time}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="qiangdan flexa btn-bg btn-qiang" data-item="{{item}}" bindtap="onQiangdanTap">抢单</view>
|
||||
</view>
|
||||
<view class="o-foot flexb gold-foot-text-only">
|
||||
<text class="o-amt">商家派单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 普通商家订单 -->
|
||||
<view wx:elif="{{item.isShangjia}}" class="xym-order-item xym-shangjia-order" data-item="{{item}}">
|
||||
<view class="xym-shangjia-head flexb">
|
||||
<text class="xym-head-time">{{item.creat_time}}</text>
|
||||
<view class="xym-head-tag">商家发单</view>
|
||||
</view>
|
||||
<view class="xym-shangjia-body">
|
||||
<view class="order-con">
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image wx:if="{{item.zhiding_avatar_full}}" class="zhiding-avatar" src="{{item.zhiding_avatar_full}}" mode="aspectFill"/>
|
||||
<text class="zhiding-name">{{item.zhiding_nicheng || '指定接单员'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{item.dashou_biaoqian.length > 0 || item.zhiding_identity_biaoqian.length > 0}}" class="zhiding-strip-tags">
|
||||
<chenghao-tag wx:for="{{item.dashou_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
<chenghao-tag wx:for="{{item.zhiding_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="order-row myflex">
|
||||
<view class="o-col">
|
||||
<view class="o-goods line2" data-type="jieshao" data-content="{{item.jieshao}}" bindtap="onViewDetail">{{item.jieshao || '暂无介绍'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="reward-badge normal-reward" wx:if="{{item.hasRequiredMember}}">
|
||||
<text class="reward-yen sm">¥</text>
|
||||
<text class="reward-num dark">{{item.dashou_fencheng || 0}}</text>
|
||||
</view>
|
||||
<view class="reward-badge member-tip" wx:else>
|
||||
<text class="reward-tip">未开通会员</text>
|
||||
</view>
|
||||
<view class="remark-block dark" data-type="beizhu" data-content="{{item.beizhu}}" bindtap="onViewDetail">
|
||||
<text class="remark-label">商家备注:</text>{{item.beizhu || '暂无'}}
|
||||
</view>
|
||||
<view wx:if="{{item.shangjia_biaoqian.length > 0 || item.shangjia_identity_biaoqian.length > 0 || item.xuqiu_biaoqian.length > 0}}" class="xuqiu-tags-row">
|
||||
<scroll-view wx:if="{{item.shangjia_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex>
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.shangjia_identity_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.shangjia_identity_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view wx:if="{{item.xuqiu_biaoqian.length > 0}}" class="biaoqian-inline-scroll" scroll-x enable-flex style="margin-top:8rpx">
|
||||
<view class="biaoqian-inline-inner">
|
||||
<chenghao-tag wx:for="{{item.xuqiu_biaoqian}}" wx:for-item="tag" wx:key="id" mingcheng="{{tag.mingcheng}}" texiaoJson="{{tag.texiao_json}}" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="o-foot flexb">
|
||||
<text class="o-amt">商家派单</text>
|
||||
<view class="qiangdan flexa btn-bg btn-qiang" data-item="{{item}}" bindtap="onQiangdanTap">抢单</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view wx:if="{{isLoadingMore && dingdanList.length > 0}}" class="loading-more">
|
||||
<text class="loading-text">加载更多订单中...</text>
|
||||
</view>
|
||||
<view wx:if="{{!hasMore && dingdanList.length > 0}}" class="no-more">
|
||||
<text class="no-more-text">—— 没有更多订单了 ——</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="scroll-bottom-spacer"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view wx:if="{{isLoading && dingdanList.length === 0 && xuanzhongLeixingId}}" class="loading-mask">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-mask-text">订单加载中...</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<global-notification id="global-notification" />
|
||||
<popup-notice id="popupNotice" />
|
||||
</view>
|
||||
</view>
|
||||
+186
-663
@@ -1,665 +1,188 @@
|
||||
/* pages/qiangdan/qiangdan.wxss */
|
||||
/* pages/order-pool/order-pool.wxss 抢单池(逍遥梦风格) */
|
||||
@import '../../styles/dashou-xym-theme.wxss';
|
||||
.qiangdan-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f5f7fa;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 授权提示页 */
|
||||
.unauthorized-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(ellipse at top, #0b0f1a, #03050a);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.unauthorized-card {
|
||||
width: 600rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
background: linear-gradient(145deg, #1a1f30, #0f1422);
|
||||
border-radius: 60rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid rgba(0, 247, 255, 0.3);
|
||||
box-shadow: 0 30rpx 60rpx rgba(0, 0, 0, 0.8), 0 0 30rpx rgba(0, 247, 255, 0.2);
|
||||
}
|
||||
|
||||
.card-glow {
|
||||
position: absolute;
|
||||
top: -20%;
|
||||
left: -20%;
|
||||
width: 140%;
|
||||
height: 140%;
|
||||
background: radial-gradient(circle at 30% 30%, rgba(0, 247, 255, 0.2), transparent 60%);
|
||||
animation: rotateGlow 10s linear infinite;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@keyframes rotateGlow {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.card-content-unauth {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.icon-lock {
|
||||
position: relative;
|
||||
width: 72rpx;
|
||||
height: 52rpx;
|
||||
margin-bottom: 30rpx;
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(180, 130, 20, 0.25);
|
||||
}
|
||||
@import '../../styles/dashou-xym-order-card.wxss';
|
||||
|
||||
.icon-lock::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -34rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 44rpx;
|
||||
height: 36rpx;
|
||||
border: 8rpx solid #ffd061;
|
||||
border-bottom: none;
|
||||
border-radius: 22rpx 22rpx 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48rpx;
|
||||
font-weight: 700;
|
||||
letter-spacing: 4rpx;
|
||||
margin-bottom: 20rpx;
|
||||
text-shadow: 0 0 15rpx #00f7ff;
|
||||
background: linear-gradient(135deg, #ffffff, #aaddff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 32rpx;
|
||||
color: #ffaa00;
|
||||
margin-bottom: 16rpx;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 0 10rpx #ffaa00;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 28rpx;
|
||||
color: #a0b0d0;
|
||||
margin-bottom: 50rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-register {
|
||||
position: relative;
|
||||
padding: 24rpx 80rpx;
|
||||
background: linear-gradient(145deg, #2a3050, #181e38);
|
||||
border-radius: 60rpx;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid #00f7ff;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 247, 255, 0.4);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.btn-register:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
letter-spacing: 4rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-shadow: 0 0 10rpx #00f7ff;
|
||||
}
|
||||
|
||||
.btn-shine {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.2) 50%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
transform: rotate(30deg);
|
||||
animation: btn-shimmer 2.8s infinite linear;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes btn-shimmer {
|
||||
0% { transform: translateX(-100%) rotate(30deg); }
|
||||
100% { transform: translateX(100%) rotate(30deg); }
|
||||
}
|
||||
|
||||
/* 原有样式 */
|
||||
.leixing-quyu {
|
||||
padding: 30rpx 0 20rpx;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||
border-bottom: 1rpx solid #eaeaea;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.leixing-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
height: 180rpx;
|
||||
}
|
||||
|
||||
.leixing-container {
|
||||
display: inline-flex;
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.leixing-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 160rpx;
|
||||
margin-right: 24rpx;
|
||||
position: relative;
|
||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.leixing-active {
|
||||
transform: translateY(-6rpx);
|
||||
}
|
||||
|
||||
.guangyun-effect {
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
left: -12rpx;
|
||||
right: -12rpx;
|
||||
bottom: -12rpx;
|
||||
background: radial-gradient(circle at center, rgba(82, 196, 26, 0.15) 0%, transparent 70%);
|
||||
border-radius: 40rpx;
|
||||
z-index: 0;
|
||||
animation: guangyun-pulse 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes guangyun-pulse {
|
||||
0%, 100% { opacity: 0.6; transform: scale(1); }
|
||||
50% { opacity: 0.9; transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.leixing-tupian {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 28rpx;
|
||||
z-index: 1;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.leixing-active .leixing-tupian {
|
||||
box-shadow: 0 16rpx 40rpx rgba(82, 196, 26, 0.3);
|
||||
border: 4rpx solid #52c41a;
|
||||
}
|
||||
|
||||
.leixing-jieshao {
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.leixing-active .leixing-jieshao {
|
||||
color: #52c41a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fenge-xian {
|
||||
height: 16rpx;
|
||||
background: linear-gradient(to bottom, #f0f2f5, #f8f9fa);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dingdan-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dingdan-scroll-view {
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.refresher-container {
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.refreshing-text,
|
||||
.pull-down-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
padding: 120rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
opacity: 0.4;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 28rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.dingdan-card {
|
||||
margin: 20rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.06);
|
||||
transition: all 0.2s ease;
|
||||
border: 1rpx solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.dingdan-card:active {
|
||||
transform: scale(0.995);
|
||||
}
|
||||
|
||||
.pingtai-card {
|
||||
background: linear-gradient(145deg, #f8fff8 0%, #f0f9f0 100%);
|
||||
}
|
||||
|
||||
.shangjia-card {
|
||||
background: linear-gradient(145deg, #f9f0ff 0%, #f3e8ff 100%);
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 1rpx dashed #e0e0e0;
|
||||
}
|
||||
|
||||
.creat-time {
|
||||
font-size: 26rpx;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.top-line {
|
||||
flex: 1;
|
||||
height: 1rpx;
|
||||
background: linear-gradient(to right, transparent, #ddd, transparent);
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.pingtai-tag {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
padding: 6rpx 16rpx;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.zhiding-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 10rpx 16rpx;
|
||||
background: rgba(255, 77, 79, 0.08);
|
||||
border-radius: 12rpx;
|
||||
border-left: 4rpx solid #ff4d4f;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.zhiding-icon {
|
||||
font-size: 26rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.zhiding-avatar {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #ff4d4f;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.zhiding-nicheng {
|
||||
font-size: 24rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 500;
|
||||
max-width: 120rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.zhiding-uid {
|
||||
font-size: 24rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 400;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.neirong-tupian {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.jieshao-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: 140rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.jieshao-label {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.jieshao-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.jieshao-ellipsis .jieshao-text {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.shangjia-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.shangjia-touxiang {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
border: 2rpx solid rgba(114, 46, 209, 0.15);
|
||||
box-shadow: 0 6rpx 18rpx rgba(114, 46, 209, 0.12);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.shangjia-nicheng {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.shangjia-jieshao,
|
||||
.shangjia-beizhu {
|
||||
margin-bottom: 16rpx;
|
||||
padding: 14rpx 18rpx;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.shangjia-jieshao .jieshao-label,
|
||||
.shangjia-beizhu .beizhu-label {
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.shangjia-jieshao .jieshao-text,
|
||||
.shangjia-beizhu .beizhu-text {
|
||||
font-size: 24rpx;
|
||||
color: #555;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.shangjia-jieshao.jieshao-ellipsis .jieshao-text {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.shangjia-beizhu.beizhu-ellipsis .beizhu-text {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.beizhu-box {
|
||||
margin-bottom: 20rpx;
|
||||
padding: 14rpx 18rpx;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.beizhu-label {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.beizhu-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.beizhu-ellipsis .beizhu-text {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.view-more {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #1890ff;
|
||||
margin-top: 6rpx;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.fenyong-box {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.fenyong-icon {
|
||||
font-size: 30rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.fenyong-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
.fenyong-price {
|
||||
font-size: 40rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 700;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.fenyong-unit {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.qiangdan-btn {
|
||||
position: relative;
|
||||
padding: 18rpx 40rpx;
|
||||
border-radius: 50rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.pingtai-btn {
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
}
|
||||
|
||||
.shangjia-btn {
|
||||
background: linear-gradient(135deg, #722ed1 0%, #9254de 100%);
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 28rpx;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.btn-shine {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.3) 50%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
transform: rotate(30deg);
|
||||
animation: btn-shimmer 3s infinite linear;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes btn-shimmer {
|
||||
0% { transform: translateX(-100%) rotate(30deg); }
|
||||
100% { transform: translateX(100%) rotate(30deg); }
|
||||
}
|
||||
|
||||
.qiangdan-btn:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.loading-more,
|
||||
.no-more {
|
||||
padding: 30rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.no-more-text {
|
||||
font-size: 26rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.loading-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border: 6rpx solid #f0f0f0;
|
||||
border-top: 6rpx solid #52c41a;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-mask-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
.qiangdan-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.page-main-scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.scroll-bottom-spacer {
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.refresher-container {
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.leixing-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.leixing-container {
|
||||
display: inline-flex;
|
||||
padding: 0 30rpx;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.leixing-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.biaoqian-scroll {
|
||||
white-space: nowrap;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.biaoqian-container {
|
||||
display: inline-flex;
|
||||
gap: 16rpx;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.biaoqian-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 12rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.quanbu-text {
|
||||
font-size: 26rpx;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
.xuqiu-tags-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 12rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.xuqiu-label {
|
||||
font-size: 24rpx;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.biaoqian-inline-scroll {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
padding: 160rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
opacity: 0.5;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.loading-more,
|
||||
.no-more {
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
border: 6rpx solid rgba(255, 208, 97, 0.3);
|
||||
border-top: 6rpx solid #ffd061;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 未授权页 */
|
||||
.unauthorized-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.unauthorized-card {
|
||||
width: 600rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
border-radius: 60rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-content-unauth {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon-lock {
|
||||
width: 72rpx;
|
||||
height: 52rpx;
|
||||
margin-bottom: 30rpx;
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-register {
|
||||
padding: 24rpx 80rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-register .btn-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -75,7 +75,27 @@ Component({
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
const newList = isLoadMore ? this.data.list.concat(data.list || []) : (data.list || []);
|
||||
const rows = (data.list || []).map(item => {
|
||||
const z = Number(item.zhuangtai);
|
||||
let display_status = '未知', status_class = 'zhuangtai-weizhi';
|
||||
if (z === 1) { display_status = '待缴纳'; status_class = 'zhuangtai-daijiaona'; }
|
||||
else if (z === 2) { display_status = '已缴纳'; status_class = 'zhuangtai-yijiaona'; }
|
||||
else if (z === 3) { display_status = '申诉中'; status_class = 'zhuangtai-shensuzhong'; }
|
||||
else if (z === 4) { display_status = '申诉成功'; status_class = 'zhuangtai-yibohui'; }
|
||||
const zhengju = (item.zhengju_tupian || []).map(url => this.getFullUrl(url));
|
||||
const shensu = (item.shensu_tupian || []).map(url => this.getFullUrl(url));
|
||||
return {
|
||||
...item,
|
||||
create_time: item.CreateTime || '',
|
||||
display_status,
|
||||
status_class,
|
||||
zhengju_tupian: item.zhengju_tupian || [],
|
||||
shensu_tupian: item.shensu_tupian || [],
|
||||
zhengju_full: zhengju,
|
||||
shensu_full: shensu
|
||||
};
|
||||
});
|
||||
const newList = isLoadMore ? this.data.list.concat(rows) : rows;
|
||||
this.setData({
|
||||
list: newList,
|
||||
hasMore: data.has_more || false, // 以后端返回为准
|
||||
@@ -231,7 +251,7 @@ Component({
|
||||
return null;
|
||||
}
|
||||
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.js');
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.min.js');
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const cos = new COS({
|
||||
SimpleUploadMethod: 'putObject',
|
||||
|
||||
@@ -39,23 +39,23 @@
|
||||
<view wx:if="{{ detailItem.shensuliyou }}" class="row"><text class="lbl">申诉理由</text><text class="val">{{ detailItem.shensuliyou }}</text></view>
|
||||
|
||||
<!-- 证据图片 -->
|
||||
<view wx:if="{{ detailItem.zhengju_tupian && detailItem.zhengju_tupian.length }}" class="section">
|
||||
<view wx:if="{{ detailItem.zhengju_full && detailItem.zhengju_full.length }}" class="section">
|
||||
<text class="sec-title">证据图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.zhengju_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ ossImageUrl + item }}">
|
||||
<image class="img" src="{{ ossImageUrl + item }}" mode="aspectFill" />
|
||||
<block wx:for="{{ detailItem.zhengju_full }}" wx:key="*this">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ item }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 申诉图片 -->
|
||||
<view wx:if="{{ detailItem.shensu_tupian && detailItem.shensu_tupian.length }}" class="section">
|
||||
<view wx:if="{{ detailItem.shensu_full && detailItem.shensu_full.length }}" class="section">
|
||||
<text class="sec-title">申诉图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.shensu_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ ossImageUrl + item }}">
|
||||
<image class="img" src="{{ ossImageUrl + item }}" mode="aspectFill" />
|
||||
<block wx:for="{{ detailItem.shensu_full }}" wx:key="*this">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ item }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
@@ -150,7 +150,7 @@ getFullImageUrl(relativeUrl) {
|
||||
throw new Error(res?.data?.msg || '获取上传凭证失败');
|
||||
},
|
||||
initCOSClient(tokenData) {
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.js');
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.min.js');
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063';
|
||||
const region = tokenData.region || 'ap-shanghai';
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
+236
-281
@@ -1,281 +1,236 @@
|
||||
// pages/poster/poster.js
|
||||
import request from '../../utils/request.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
qrcodeUrl: '',
|
||||
isLoading: false,
|
||||
showPosterCanvas: false,
|
||||
posterImageTemp: '',
|
||||
avatarUrl: '',
|
||||
uid: '',
|
||||
nickName: '',
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadQRCodeFromCache();
|
||||
this.loadUserInfo();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
loadQRCodeFromCache() {
|
||||
try {
|
||||
const relativeUrl = wx.getStorageSync('haibao_url');
|
||||
if (relativeUrl) {
|
||||
const fullUrl = this.getFullImageUrl(relativeUrl);
|
||||
this.setData({ qrcodeUrl: fullUrl });
|
||||
} else {
|
||||
this.setData({ qrcodeUrl: '' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取缓存失败', error);
|
||||
}
|
||||
},
|
||||
|
||||
loadUserInfo() {
|
||||
const touxiang = wx.getStorageSync('touxiang');
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg');
|
||||
let avatarUrl = defaultAvatar;
|
||||
if (touxiang && typeof touxiang === 'string' && touxiang.trim() !== '') {
|
||||
avatarUrl = this.getFullImageUrl(touxiang);
|
||||
}
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
let nickName = app.globalData.nicheng || wx.getStorageSync('nicheng') || '管事';
|
||||
this.setData({ avatarUrl, uid, nickName });
|
||||
},
|
||||
|
||||
getFullImageUrl(url) {
|
||||
if (!url) return '';
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
return url.startsWith('http') ? url : ossUrl + url;
|
||||
},
|
||||
|
||||
refreshQRCode() {
|
||||
if (this.data.isLoading) return;
|
||||
this.setData({ isLoading: true });
|
||||
request({
|
||||
url: '/peizhi/guanshiewm',
|
||||
method: 'POST',
|
||||
}).then(res => {
|
||||
if (res.data && res.data.code === 0) {
|
||||
const relativeUrl = res.data.data.url;
|
||||
if (relativeUrl) {
|
||||
wx.setStorageSync('haibao_url', relativeUrl);
|
||||
const fullUrl = this.getFullImageUrl(relativeUrl);
|
||||
this.setData({ qrcodeUrl: fullUrl });
|
||||
wx.showToast({ title: '获取成功', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: '返回数据异常', icon: 'none' });
|
||||
}
|
||||
} else {
|
||||
wx.showToast({ title: res.data.msg || '获取失败', icon: 'none' });
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取海报失败', err);
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
}).finally(() => {
|
||||
this.setData({ isLoading: false });
|
||||
});
|
||||
},
|
||||
|
||||
// 🔥【修改】保存二维码到相册(先下载网络图片)
|
||||
saveToAlbum() {
|
||||
if (!this.data.qrcodeUrl) {
|
||||
wx.showToast({ title: '暂无二维码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '保存中...', mask: true });
|
||||
wx.downloadFile({
|
||||
url: this.data.qrcodeUrl,
|
||||
success: (res) => {
|
||||
wx.hideLoading();
|
||||
if (res.statusCode === 200) {
|
||||
// 调用已有的保存函数(传入临时路径)
|
||||
this.downloadAndSave(res.tempFilePath);
|
||||
} else {
|
||||
wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
wx.hideLoading();
|
||||
console.error('下载二维码失败', err);
|
||||
wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
generateMyPoster() {
|
||||
if (!this.data.qrcodeUrl) {
|
||||
wx.showToast({ title: '请先生成二维码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '生成海报中...', mask: true });
|
||||
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const bgUrl = ossUrl + 'beijing/haibaobeijing.jpg';
|
||||
const urls = [bgUrl, this.data.qrcodeUrl, this.data.avatarUrl];
|
||||
|
||||
const promises = urls.map(url => this.loadImage(url));
|
||||
Promise.all(promises)
|
||||
.then((images) => {
|
||||
wx.hideLoading();
|
||||
this.setData({ showPosterCanvas: true }, () => {
|
||||
setTimeout(() => {
|
||||
this.drawPoster(images[0], images[1], images[2]);
|
||||
}, 200);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '图片加载失败', icon: 'none' });
|
||||
console.error('图片加载失败', err);
|
||||
});
|
||||
},
|
||||
|
||||
loadImage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getImageInfo({
|
||||
src: url,
|
||||
success: (res) => resolve(res.path),
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
drawPoster(bgPath, qrPath, avatarPath) {
|
||||
const ctx = wx.createCanvasContext('posterCanvas', this);
|
||||
const query = wx.createSelectorQuery().in(this);
|
||||
query.select('.poster-canvas').boundingClientRect(rect => {
|
||||
if (!rect) {
|
||||
wx.showToast({ title: '画布获取失败', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const canvasWidth = rect.width;
|
||||
const canvasHeight = rect.height;
|
||||
|
||||
// 1. 绘制背景
|
||||
ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight);
|
||||
|
||||
const qrSize = 100;
|
||||
const margin = 5;
|
||||
const qrX = canvasWidth - qrSize - margin;
|
||||
const qrY = canvasHeight - qrSize - margin;
|
||||
ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
|
||||
|
||||
const avatarSize = 60;
|
||||
const avatarX = margin + 20;
|
||||
const avatarY = canvasHeight - avatarSize - margin;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(avatarX + avatarSize/2, avatarY + avatarSize/2, avatarSize/2, 0, 2 * Math.PI);
|
||||
ctx.clip();
|
||||
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
|
||||
ctx.restore();
|
||||
|
||||
ctx.setFontSize(16);
|
||||
ctx.setFillStyle('#333333');
|
||||
ctx.setTextAlign('left');
|
||||
|
||||
let nick = this.data.nickName || '管事';
|
||||
if (nick.length > 3) {
|
||||
nick = nick.substring(0, 3) + '...';
|
||||
}
|
||||
const nameText = nick;
|
||||
|
||||
const textX = avatarX + avatarSize + 8;
|
||||
const textY = avatarY + avatarSize/2 + 6;
|
||||
ctx.fillText(nameText, textX, textY);
|
||||
|
||||
ctx.setFontSize(18);
|
||||
ctx.setFillStyle('#ffaa00');
|
||||
ctx.setTextAlign('left');
|
||||
const slogan = '快来加入我们 ✦';
|
||||
const sloganX = avatarX;
|
||||
const sloganY = avatarY - 12;
|
||||
ctx.fillText(slogan, sloganX, sloganY);
|
||||
|
||||
ctx.draw(false, () => {
|
||||
wx.canvasToTempFilePath({
|
||||
canvasId: 'posterCanvas',
|
||||
success: (res) => {
|
||||
this.setData({ posterImageTemp: res.tempFilePath });
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('生成海报临时文件失败', err);
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
}).exec();
|
||||
},
|
||||
|
||||
savePoster() {
|
||||
if (!this.data.posterImageTemp) {
|
||||
wx.showToast({ title: '海报尚未生成', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.downloadAndSave(this.data.posterImageTemp);
|
||||
},
|
||||
|
||||
hidePoster() {
|
||||
this.setData({ showPosterCanvas: false, posterImageTemp: '' });
|
||||
},
|
||||
|
||||
downloadAndSave(filePath) {
|
||||
wx.getSetting({
|
||||
success: (res) => {
|
||||
if (!res.authSetting['scope.writePhotosAlbum']) {
|
||||
wx.authorize({
|
||||
scope: 'scope.writePhotosAlbum',
|
||||
success: () => {
|
||||
this._saveImage(filePath);
|
||||
},
|
||||
fail: () => {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '需要您授权保存到相册',
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
wx.openSetting();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._saveImage(filePath);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_saveImage(filePath) {
|
||||
wx.saveImageToPhotosAlbum({
|
||||
filePath: filePath,
|
||||
success: () => {
|
||||
wx.showToast({ title: '保存成功', icon: 'success' });
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('保存失败', err);
|
||||
wx.showToast({ title: '保存失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
// pages/poster/poster.js — 管事推广海报
|
||||
import request from '../../utils/request.js';
|
||||
import {
|
||||
getFullImageUrl,
|
||||
loadPosterPageConfig,
|
||||
posterQrCacheKey,
|
||||
} from '../../utils/poster-page.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
qrcodeUrl: '',
|
||||
isLoading: false,
|
||||
showPosterCanvas: false,
|
||||
posterImageTemp: '',
|
||||
avatarUrl: '',
|
||||
uid: '',
|
||||
nickName: '',
|
||||
posterBgUrl: '',
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
const { bgUrl, qrCacheKey } = await loadPosterPageConfig('guanshi', app);
|
||||
this._qrCacheKey = qrCacheKey;
|
||||
this.setData({ posterBgUrl: bgUrl });
|
||||
this.loadQRCodeFromCache();
|
||||
this.loadUserInfo();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification(),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
loadQRCodeFromCache() {
|
||||
try {
|
||||
const key = this._qrCacheKey || posterQrCacheKey('guanshi');
|
||||
const relativeUrl = wx.getStorageSync(key);
|
||||
if (relativeUrl) {
|
||||
this.setData({ qrcodeUrl: getFullImageUrl(relativeUrl, app) });
|
||||
} else {
|
||||
this.setData({ qrcodeUrl: '' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取缓存失败', error);
|
||||
}
|
||||
},
|
||||
|
||||
loadUserInfo() {
|
||||
const touxiang = wx.getStorageSync('touxiang');
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg');
|
||||
let avatarUrl = defaultAvatar;
|
||||
if (touxiang && typeof touxiang === 'string' && touxiang.trim() !== '') {
|
||||
avatarUrl = getFullImageUrl(touxiang, app);
|
||||
}
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
const nickName = app.globalData.nicheng || wx.getStorageSync('nicheng') || '管事';
|
||||
this.setData({ avatarUrl, uid, nickName });
|
||||
},
|
||||
|
||||
refreshQRCode() {
|
||||
if (this.data.isLoading) return;
|
||||
this.setData({ isLoading: true });
|
||||
request({
|
||||
url: '/peizhi/guanshiewm',
|
||||
method: 'POST',
|
||||
}).then((res) => {
|
||||
if (res.data && res.data.code === 0) {
|
||||
const relativeUrl = res.data.data.url;
|
||||
if (relativeUrl) {
|
||||
const key = this._qrCacheKey || posterQrCacheKey('guanshi');
|
||||
wx.setStorageSync(key, relativeUrl);
|
||||
this.setData({ qrcodeUrl: getFullImageUrl(relativeUrl, app) });
|
||||
wx.showToast({ title: '获取成功', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: '返回数据异常', icon: 'none' });
|
||||
}
|
||||
} else {
|
||||
wx.showToast({ title: res.data.msg || res.data.message || '获取失败', icon: 'none' });
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error('获取海报失败', err);
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
}).finally(() => {
|
||||
this.setData({ isLoading: false });
|
||||
});
|
||||
},
|
||||
|
||||
saveToAlbum() {
|
||||
if (!this.data.qrcodeUrl) {
|
||||
wx.showToast({ title: '暂无二维码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '保存中...', mask: true });
|
||||
wx.downloadFile({
|
||||
url: this.data.qrcodeUrl,
|
||||
success: (res) => {
|
||||
wx.hideLoading();
|
||||
if (res.statusCode === 200) {
|
||||
this.downloadAndSave(res.tempFilePath);
|
||||
} else {
|
||||
wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '下载失败', icon: 'none' });
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
generateMyPoster() {
|
||||
if (!this.data.qrcodeUrl) {
|
||||
wx.showToast({ title: '请先生成二维码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '生成海报中...', mask: true });
|
||||
const bgUrl = this.data.posterBgUrl;
|
||||
const urls = [bgUrl, this.data.qrcodeUrl, this.data.avatarUrl];
|
||||
Promise.all(urls.map((url) => this.loadImage(url)))
|
||||
.then((images) => {
|
||||
wx.hideLoading();
|
||||
this.setData({ showPosterCanvas: true }, () => {
|
||||
setTimeout(() => this.drawPoster(images[0], images[1], images[2]), 200);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '图片加载失败', icon: 'none' });
|
||||
console.error('图片加载失败', err);
|
||||
});
|
||||
},
|
||||
|
||||
loadImage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getImageInfo({ src: url, success: (res) => resolve(res.path), fail: reject });
|
||||
});
|
||||
},
|
||||
|
||||
drawPoster(bgPath, qrPath, avatarPath) {
|
||||
const ctx = wx.createCanvasContext('posterCanvas', this);
|
||||
const query = wx.createSelectorQuery().in(this);
|
||||
query.select('.poster-canvas').boundingClientRect((rect) => {
|
||||
if (!rect) {
|
||||
wx.showToast({ title: '画布获取失败', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const canvasWidth = rect.width;
|
||||
const canvasHeight = rect.height;
|
||||
ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight);
|
||||
const qrSize = 100;
|
||||
const margin = 5;
|
||||
const qrX = canvasWidth - qrSize - margin;
|
||||
const qrY = canvasHeight - qrSize - margin;
|
||||
ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
|
||||
const avatarSize = 60;
|
||||
const avatarX = margin + 20;
|
||||
const avatarY = canvasHeight - avatarSize - margin;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, 2 * Math.PI);
|
||||
ctx.clip();
|
||||
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
|
||||
ctx.restore();
|
||||
ctx.setFontSize(16);
|
||||
ctx.setFillStyle('#333333');
|
||||
ctx.setTextAlign('left');
|
||||
let nick = this.data.nickName || '管事';
|
||||
if (nick.length > 3) nick = nick.substring(0, 3) + '...';
|
||||
ctx.fillText(nick, avatarX + avatarSize + 8, avatarY + avatarSize / 2 + 6);
|
||||
ctx.setFontSize(18);
|
||||
ctx.setFillStyle('#ffaa00');
|
||||
ctx.fillText('快来加入我们 ✦', avatarX, avatarY - 12);
|
||||
ctx.draw(false, () => {
|
||||
wx.canvasToTempFilePath({
|
||||
canvasId: 'posterCanvas',
|
||||
success: (res) => this.setData({ posterImageTemp: res.tempFilePath }),
|
||||
fail: (err) => console.error('生成海报临时文件失败', err),
|
||||
}, this);
|
||||
});
|
||||
}).exec();
|
||||
},
|
||||
|
||||
savePoster() {
|
||||
if (!this.data.posterImageTemp) {
|
||||
wx.showToast({ title: '海报尚未生成', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.downloadAndSave(this.data.posterImageTemp);
|
||||
},
|
||||
|
||||
hidePoster() {
|
||||
this.setData({ showPosterCanvas: false, posterImageTemp: '' });
|
||||
},
|
||||
|
||||
downloadAndSave(filePath) {
|
||||
wx.getSetting({
|
||||
success: (res) => {
|
||||
if (!res.authSetting['scope.writePhotosAlbum']) {
|
||||
wx.authorize({
|
||||
scope: 'scope.writePhotosAlbum',
|
||||
success: () => this._saveImage(filePath),
|
||||
fail: () => {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '需要您授权保存到相册',
|
||||
success: (modalRes) => { if (modalRes.confirm) wx.openSetting(); },
|
||||
});
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this._saveImage(filePath);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_saveImage(filePath) {
|
||||
wx.saveImageToPhotosAlbum({
|
||||
filePath,
|
||||
success: () => wx.showToast({ title: '保存成功', icon: 'success' }),
|
||||
fail: () => wx.showToast({ title: '保存失败', icon: 'none' }),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -1,252 +1,251 @@
|
||||
// pages/product-detail/product-detail.js
|
||||
const app = getApp()
|
||||
import { ensureLogin } from '../../utils/login'
|
||||
import { backfillUserProfileCache } from '../../utils/role-tab-bar'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
ossImageUrl: '',
|
||||
apiBaseUrl: '',
|
||||
|
||||
shangpinId: '',
|
||||
shangpinTitle: '',
|
||||
shangpinPrice: '',
|
||||
|
||||
shangpinData: null,
|
||||
isLoading: true,
|
||||
loadError: false,
|
||||
|
||||
// 页面字段映射
|
||||
fieldMapping: {
|
||||
tupian: 'tupian',
|
||||
biaoti: 'biaoti',
|
||||
jiage: 'jiage',
|
||||
xiangqing: 'xiangqing',
|
||||
xiadanxuzhi: 'xiadanxuzhi',
|
||||
guize: 'guize',
|
||||
xiaoliang: 'xiaoliang',
|
||||
kucun: 'kucun',
|
||||
fenlei: 'fenlei',
|
||||
pingfen: 'pingfen'
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.setData({
|
||||
ossImageUrl: app.globalData.ossImageUrl,
|
||||
apiBaseUrl: app.globalData.apiBaseUrl,
|
||||
shangpinId: options.id || '',
|
||||
shangpinTitle: options.title || '',
|
||||
shangpinPrice: options.price || ''
|
||||
})
|
||||
|
||||
if (!this.data.shangpinId) {
|
||||
wx.showToast({
|
||||
title: '商品不存在',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 1500)
|
||||
return
|
||||
}
|
||||
|
||||
this.loadShangpinDetail()
|
||||
},
|
||||
onShow() {
|
||||
backfillUserProfileCache(app);
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
loadShangpinDetail() {
|
||||
const that = this
|
||||
const { shangpinId, apiBaseUrl } = this.data
|
||||
|
||||
this.setData({ isLoading: true, loadError: false })
|
||||
|
||||
wx.request({
|
||||
url: apiBaseUrl + '/shangpin/xiangqinghuoqu/',
|
||||
method: 'POST',
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
shangpin_id: shangpinId
|
||||
},
|
||||
success(res) {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
const shangpinData = res.data.data || {}
|
||||
|
||||
// 处理价格
|
||||
if (shangpinData.jiage) {
|
||||
const jiage = parseFloat(shangpinData.jiage) || 0
|
||||
const priceInteger = Math.floor(jiage)
|
||||
let priceDecimal = '00'
|
||||
|
||||
const decimalPart = (jiage - priceInteger).toFixed(2)
|
||||
if (decimalPart > 0) {
|
||||
priceDecimal = decimalPart.toString().split('.')[1]
|
||||
}
|
||||
|
||||
shangpinData.priceInteger = priceInteger
|
||||
shangpinData.priceDecimal = priceDecimal
|
||||
}
|
||||
|
||||
// 处理图片URL
|
||||
if (shangpinData.tupian && !shangpinData.tupian.startsWith('http')) {
|
||||
shangpinData.tupian = that.data.ossImageUrl + shangpinData.tupian
|
||||
}
|
||||
|
||||
// 处理规则图片URL
|
||||
if (shangpinData.guize && !shangpinData.guize.startsWith('http')) {
|
||||
shangpinData.guize = that.data.ossImageUrl + shangpinData.guize
|
||||
}
|
||||
|
||||
// 处理销量显示
|
||||
if (shangpinData.xiaoliang) {
|
||||
shangpinData.xiaoliangText = that.formatXiaoliang(shangpinData.xiaoliang)
|
||||
}
|
||||
|
||||
// 处理库存显示
|
||||
if (shangpinData.kucun) {
|
||||
shangpinData.kucunText = shangpinData.kucun + '件'
|
||||
shangpinData.kucunClass = shangpinData.kucun > 0 ? 'kucun-normal' : 'kucun-none'
|
||||
}
|
||||
|
||||
// 设置页面标题
|
||||
if (shangpinData.biaoti) {
|
||||
wx.setNavigationBarTitle({
|
||||
title: '商品详情'
|
||||
})
|
||||
}
|
||||
|
||||
that.setData({
|
||||
shangpinData: shangpinData,
|
||||
isLoading: false
|
||||
})
|
||||
} else {
|
||||
that.handleLoadError(res.data?.msg || '加载失败')
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
that.handleLoadError('网络错误,请重试')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
formatXiaoliang(xiaoliang) {
|
||||
const num = parseInt(xiaoliang) || 0
|
||||
|
||||
if (num >= 10000) {
|
||||
return (num / 10000).toFixed(1) + '万+'
|
||||
} else if (num >= 1000) {
|
||||
return (num / 1000).toFixed(1) + '千+'
|
||||
} else {
|
||||
return num + ''
|
||||
}
|
||||
},
|
||||
|
||||
handleLoadError(errorMsg) {
|
||||
this.setData({
|
||||
isLoading: false,
|
||||
loadError: true
|
||||
})
|
||||
|
||||
wx.showToast({
|
||||
title: errorMsg || '加载失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
// 3秒后自动返回
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 3000)
|
||||
},
|
||||
|
||||
previewImage() {
|
||||
const { shangpinData } = this.data
|
||||
if (!shangpinData || !shangpinData.tupian) return
|
||||
|
||||
wx.previewImage({
|
||||
current: shangpinData.tupian,
|
||||
urls: [shangpinData.tupian]
|
||||
})
|
||||
},
|
||||
|
||||
async onOrderClick() {
|
||||
const { shangpinData, shangpinId } = this.data
|
||||
|
||||
// 检查库存
|
||||
if (shangpinData && shangpinData.kucun <= 0) {
|
||||
wx.showToast({
|
||||
title: '商品已售罄',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必要数据
|
||||
if (!shangpinData || !shangpinData.biaoti) {
|
||||
wx.showToast({
|
||||
title: '商品信息不完整',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await ensureLogin()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
const orderData = {
|
||||
id: shangpinId,
|
||||
title: shangpinData.biaoti,
|
||||
price: shangpinData.jiage,
|
||||
priceInteger: shangpinData.priceInteger,
|
||||
priceDecimal: shangpinData.priceDecimal,
|
||||
image: shangpinData.tupian,
|
||||
kucun: shangpinData.kucun || 0,
|
||||
xiaoliang: shangpinData.xiaoliang || 0
|
||||
}
|
||||
|
||||
wx.navigateTo({
|
||||
url: `/pages/submit/submit?data=${encodeURIComponent(JSON.stringify(orderData))}`
|
||||
})
|
||||
},
|
||||
|
||||
onRefresh() {
|
||||
this.loadShangpinDetail()
|
||||
},
|
||||
|
||||
goToKefu() {
|
||||
wx.navigateTo({ url: '/pages/cs-chat/cs-chat' })
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const { shangpinData, shangpinId } = this.data
|
||||
|
||||
return {
|
||||
title: shangpinData?.biaoti || '阿龙电竞 - 商品详情',
|
||||
path: `/pages/product-detail/product-detail?id=${shangpinId}`,
|
||||
imageUrl: shangpinData?.tupian || ''
|
||||
}
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
}
|
||||
// pages/product-detail/product-detail.js
|
||||
const app = getApp()
|
||||
import { ensureLogin } from '../../utils/login'
|
||||
import { backfillUserProfileCache } from '../../utils/role-tab-bar'
|
||||
import { buildClubHeaders } from '../../utils/club-context.js'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
ossImageUrl: '',
|
||||
apiBaseUrl: '',
|
||||
|
||||
shangpinId: '',
|
||||
shangpinTitle: '',
|
||||
shangpinPrice: '',
|
||||
|
||||
shangpinData: null,
|
||||
isLoading: true,
|
||||
loadError: false,
|
||||
|
||||
// 页面字段映射
|
||||
fieldMapping: {
|
||||
tupian: 'tupian',
|
||||
biaoti: 'biaoti',
|
||||
jiage: 'jiage',
|
||||
xiangqing: 'xiangqing',
|
||||
xiadanxuzhi: 'xiadanxuzhi',
|
||||
guize: 'guize',
|
||||
xiaoliang: 'xiaoliang',
|
||||
kucun: 'kucun',
|
||||
fenlei: 'fenlei',
|
||||
pingfen: 'pingfen'
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.setData({
|
||||
ossImageUrl: app.globalData.ossImageUrl,
|
||||
apiBaseUrl: app.globalData.apiBaseUrl,
|
||||
shangpinId: options.id || '',
|
||||
shangpinTitle: options.title || '',
|
||||
shangpinPrice: options.price || ''
|
||||
})
|
||||
|
||||
if (!this.data.shangpinId) {
|
||||
wx.showToast({
|
||||
title: '商品不存在',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 1500)
|
||||
return
|
||||
}
|
||||
|
||||
this.loadShangpinDetail()
|
||||
},
|
||||
onShow() {
|
||||
backfillUserProfileCache(app);
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
loadShangpinDetail() {
|
||||
const that = this
|
||||
const { shangpinId, apiBaseUrl } = this.data
|
||||
|
||||
this.setData({ isLoading: true, loadError: false })
|
||||
|
||||
wx.request({
|
||||
url: apiBaseUrl + '/shangpin/xiangqinghuoqu/',
|
||||
method: 'POST',
|
||||
header: buildClubHeaders({ 'content-type': 'application/json' }),
|
||||
data: {
|
||||
shangpin_id: shangpinId
|
||||
},
|
||||
success(res) {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
const shangpinData = res.data.data || {}
|
||||
|
||||
// 处理价格
|
||||
if (shangpinData.jiage) {
|
||||
const jiage = parseFloat(shangpinData.jiage) || 0
|
||||
const priceInteger = Math.floor(jiage)
|
||||
let priceDecimal = '00'
|
||||
|
||||
const decimalPart = (jiage - priceInteger).toFixed(2)
|
||||
if (decimalPart > 0) {
|
||||
priceDecimal = decimalPart.toString().split('.')[1]
|
||||
}
|
||||
|
||||
shangpinData.priceInteger = priceInteger
|
||||
shangpinData.priceDecimal = priceDecimal
|
||||
}
|
||||
|
||||
// 处理图片URL
|
||||
if (shangpinData.tupian && !shangpinData.tupian.startsWith('http')) {
|
||||
shangpinData.tupian = that.data.ossImageUrl + shangpinData.tupian
|
||||
}
|
||||
|
||||
// 处理规则图片URL
|
||||
if (shangpinData.guize && !shangpinData.guize.startsWith('http')) {
|
||||
shangpinData.guize = that.data.ossImageUrl + shangpinData.guize
|
||||
}
|
||||
|
||||
// 处理销量显示
|
||||
if (shangpinData.xiaoliang) {
|
||||
shangpinData.xiaoliangText = that.formatXiaoliang(shangpinData.xiaoliang)
|
||||
}
|
||||
|
||||
// 处理库存显示
|
||||
if (shangpinData.kucun) {
|
||||
shangpinData.kucunText = shangpinData.kucun + '件'
|
||||
shangpinData.kucunClass = shangpinData.kucun > 0 ? 'kucun-normal' : 'kucun-none'
|
||||
}
|
||||
|
||||
// 设置页面标题
|
||||
if (shangpinData.biaoti) {
|
||||
wx.setNavigationBarTitle({
|
||||
title: '商品详情'
|
||||
})
|
||||
}
|
||||
|
||||
that.setData({
|
||||
shangpinData: shangpinData,
|
||||
isLoading: false
|
||||
})
|
||||
} else {
|
||||
that.handleLoadError(res.data?.msg || '加载失败')
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
that.handleLoadError('网络错误,请重试')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
formatXiaoliang(xiaoliang) {
|
||||
const num = parseInt(xiaoliang) || 0
|
||||
|
||||
if (num >= 10000) {
|
||||
return (num / 10000).toFixed(1) + '万+'
|
||||
} else if (num >= 1000) {
|
||||
return (num / 1000).toFixed(1) + '千+'
|
||||
} else {
|
||||
return num + ''
|
||||
}
|
||||
},
|
||||
|
||||
handleLoadError(errorMsg) {
|
||||
this.setData({
|
||||
isLoading: false,
|
||||
loadError: true
|
||||
})
|
||||
|
||||
wx.showToast({
|
||||
title: errorMsg || '加载失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
// 3秒后自动返回
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 3000)
|
||||
},
|
||||
|
||||
previewImage() {
|
||||
const { shangpinData } = this.data
|
||||
if (!shangpinData || !shangpinData.tupian) return
|
||||
|
||||
wx.previewImage({
|
||||
current: shangpinData.tupian,
|
||||
urls: [shangpinData.tupian]
|
||||
})
|
||||
},
|
||||
|
||||
async onOrderClick() {
|
||||
const { shangpinData, shangpinId } = this.data
|
||||
|
||||
// 检查库存
|
||||
if (shangpinData && shangpinData.kucun <= 0) {
|
||||
wx.showToast({
|
||||
title: '商品已售罄',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必要数据
|
||||
if (!shangpinData || !shangpinData.biaoti) {
|
||||
wx.showToast({
|
||||
title: '商品信息不完整',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await ensureLogin()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
const orderData = {
|
||||
id: shangpinId,
|
||||
title: shangpinData.biaoti,
|
||||
price: shangpinData.jiage,
|
||||
priceInteger: shangpinData.priceInteger,
|
||||
priceDecimal: shangpinData.priceDecimal,
|
||||
image: shangpinData.tupian,
|
||||
kucun: shangpinData.kucun || 0,
|
||||
xiaoliang: shangpinData.xiaoliang || 0
|
||||
}
|
||||
|
||||
wx.navigateTo({
|
||||
url: `/pages/submit/submit?data=${encodeURIComponent(JSON.stringify(orderData))}`
|
||||
})
|
||||
},
|
||||
|
||||
onRefresh() {
|
||||
this.loadShangpinDetail()
|
||||
},
|
||||
|
||||
goToKefu() {
|
||||
wx.navigateTo({ url: '/pages/cs-chat/cs-chat' })
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const { shangpinData, shangpinId } = this.data
|
||||
|
||||
return {
|
||||
title: shangpinData?.biaoti || '阿龙电竞 - 商品详情',
|
||||
path: `/pages/product-detail/product-detail?id=${shangpinId}`,
|
||||
imageUrl: shangpinData?.tupian || ''
|
||||
}
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
}
|
||||
})
|
||||
@@ -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 });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "客服入驻",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user