restore: 恢复橙色逍遥梦UI版本(2ea2860)到主分支
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
112
app.js
112
app.js
@@ -3,6 +3,11 @@ 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 { getDefaultAvatarUrl, resolveAvatarFromStorage } = require('./utils/avatar.js');
|
||||
|
||||
// 三端固定首页
|
||||
const roleDefaultPage = PRIMARY_DEFAULT_PAGES;
|
||||
@@ -36,7 +41,8 @@ App({
|
||||
guanshiqun: '',
|
||||
guanshiqunid: '',
|
||||
|
||||
appId:'wx0e4be86faac4a8d1',
|
||||
appId: WX_APP_ID,
|
||||
clubId: CLUB_ID,
|
||||
|
||||
shangjiastatus: 0,
|
||||
dashoustatus: 0,
|
||||
@@ -49,11 +55,7 @@ App({
|
||||
yajin: null,
|
||||
chenghao: '',
|
||||
jinfen: null,
|
||||
clumber: [{
|
||||
huiyuanid: '',
|
||||
huiyuanming: '',
|
||||
daoqi: ''
|
||||
}],
|
||||
clumber: [],
|
||||
chengjiaoliang: null,
|
||||
zaixianZhuangtai: null,
|
||||
|
||||
@@ -80,6 +82,8 @@ App({
|
||||
xshenfen: 1,
|
||||
|
||||
goEasyConfig: null,
|
||||
chatEnabled: false,
|
||||
groupInfoMap: {},
|
||||
kefuConfig: {
|
||||
link: '',
|
||||
enterpriseId: ''
|
||||
@@ -151,6 +155,7 @@ App({
|
||||
pageState: {
|
||||
currentPage: '',
|
||||
isInChatPage: false,
|
||||
isInGroupChat: false,
|
||||
currentChatId: '',
|
||||
lastPageUpdate: 0
|
||||
},
|
||||
@@ -159,11 +164,23 @@ App({
|
||||
debugMode: false,
|
||||
currentUser: null,
|
||||
currentRole: 'normal',
|
||||
primaryRole: 'normal'
|
||||
primaryRole: 'normal',
|
||||
/** 客服浮钮:full | mini,onLaunch 重置为 full */
|
||||
kefuFloatMode: 'full',
|
||||
/** 本次启动内隐藏客服浮钮(不持久化,杀进程后恢复) */
|
||||
kefuFloatSessionHidden: false,
|
||||
kefuFloatY: null,
|
||||
/** 从「我的」跳抢单页时高亮指定单 */
|
||||
_acceptOrderHighlight: '',
|
||||
},
|
||||
|
||||
// 核心启动流程
|
||||
async onLaunch() {
|
||||
this.globalData.kefuFloatMode = 'full';
|
||||
this.globalData.kefuFloatSessionHidden = false;
|
||||
try { wx.removeStorageSync('kefuFloatPermanentHidden'); } catch (e) {}
|
||||
setClubId(getConfiguredClubId(), this);
|
||||
this.globalData.clubId = CLUB_ID;
|
||||
// ① 隐藏返回首页按钮
|
||||
wx.hideHomeButton();
|
||||
wx.onAppRoute((res) => {
|
||||
@@ -193,6 +210,11 @@ App({
|
||||
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(() => {
|
||||
@@ -214,13 +236,11 @@ App({
|
||||
this.emitEvent('staffContextChanged', {});
|
||||
}
|
||||
|
||||
// ⑤ 建立连接
|
||||
const saved = this.getSavedConnection();
|
||||
console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
|
||||
'userId:', saved ? saved.userId : '无');
|
||||
// ⑤ 建立连接(文赫式:全局监听,不反复 disconnect)
|
||||
setTimeout(() => {
|
||||
if (this.startImWhenReady) this.startImWhenReady();
|
||||
else if (this.ensureConnection) this.ensureConnection();
|
||||
if (this.globalData.chatEnabled && this.ensureConnection) {
|
||||
this.ensureConnection();
|
||||
}
|
||||
}, 300);
|
||||
|
||||
// ⑥ 获取远程配置
|
||||
@@ -228,10 +248,16 @@ App({
|
||||
.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();
|
||||
if (this.globalData.chatEnabled && this.ensureConnection) {
|
||||
this.ensureConnection();
|
||||
}
|
||||
console.log('远程配置更新完成');
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -239,10 +265,25 @@ App({
|
||||
});
|
||||
},
|
||||
|
||||
/** 从后台切回前台时再次向后端校验 */
|
||||
/** 从后台切回前台时再次向后端校验,并维持 IM 连接 */
|
||||
async onShow() {
|
||||
if (!wx.getStorageSync('token')) return;
|
||||
await ensurePhoneAuth({ redirect: true });
|
||||
if (this.globalData.chatEnabled && typeof this.startImWhenReady === 'function') {
|
||||
this.startImWhenReady();
|
||||
} else if (this.globalData.chatEnabled && typeof this.ensureConnection === 'function') {
|
||||
this.globalData.goEasyConnection.autoReconnect = true;
|
||||
if (typeof this.restoreTabBarBadge === 'function') {
|
||||
this.restoreTabBarBadge();
|
||||
}
|
||||
this.ensureConnection();
|
||||
if (typeof this.syncConnectionStatus === 'function') {
|
||||
setTimeout(() => this.syncConnectionStatus(), 800);
|
||||
}
|
||||
if (typeof this.loadConversations === 'function') {
|
||||
setTimeout(() => this.loadConversations(), 300);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 连接管理方法
|
||||
@@ -258,11 +299,16 @@ App({
|
||||
},
|
||||
|
||||
// 配置缓存读写
|
||||
CONFIG_CACHE_KEY: 'app_dynamic_config',
|
||||
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.CONFIG_CACHE_KEY);
|
||||
const data = wx.getStorageSync(this.getConfigCacheKey());
|
||||
if (data && typeof data === 'object') {
|
||||
return data;
|
||||
}
|
||||
@@ -273,7 +319,7 @@ App({
|
||||
saveConfigToStorage(config) {
|
||||
try {
|
||||
if (config && typeof config === 'object') {
|
||||
wx.setStorageSync(this.CONFIG_CACHE_KEY, config);
|
||||
wx.setStorageSync(this.getConfigCacheKey(), config);
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
@@ -284,7 +330,7 @@ App({
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
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);
|
||||
@@ -307,7 +353,7 @@ App({
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
header: buildClubHeaders({ 'content-type': 'application/json' }),
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
resolve(res.data.data);
|
||||
@@ -350,6 +396,19 @@ App({
|
||||
enterpriseId: config.kefu.enterpriseId || ''
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
applyMiniappAssetsFromConfig(this, config);
|
||||
} catch (e) {
|
||||
console.warn('miniapp assets apply failed', e);
|
||||
}
|
||||
|
||||
if (this.emitEvent) {
|
||||
this.emitEvent('configApplied', {
|
||||
ossImageUrl: this.globalData.ossImageUrl,
|
||||
morentouxiang: this.globalData.morentouxiang,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 仅在有效 appkey 时初始化
|
||||
@@ -373,9 +432,10 @@ App({
|
||||
modules: ['im', 'pubsub']
|
||||
});
|
||||
wx.GoEasy = GoEasy;
|
||||
this.globalData.chatEnabled = true;
|
||||
console.log('GoEasy 初始化成功');
|
||||
if (this.startImWhenReady && wx.getStorageSync('uid')) {
|
||||
setTimeout(() => this.startImWhenReady(), 100);
|
||||
if (this.ensureConnection && wx.getStorageSync('uid')) {
|
||||
setTimeout(() => this.ensureConnection(), 100);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('GoEasy 初始化失败:', error);
|
||||
@@ -385,10 +445,12 @@ App({
|
||||
initCurrentUser() {
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (uid) {
|
||||
const def = getDefaultAvatarUrl(this);
|
||||
const avatar = resolveAvatarFromStorage(this) || def;
|
||||
this.globalData.currentUser = {
|
||||
id: uid,
|
||||
name: '用户' + uid.substring(0, 6),
|
||||
avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
|
||||
name: wx.getStorageSync('nicheng') || ('用户' + uid.substring(0, 6)),
|
||||
avatar: avatar || def,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
323
app.json
323
app.json
@@ -2,82 +2,252 @@
|
||||
"pages": [
|
||||
"pages/index/index",
|
||||
"pages/category/category",
|
||||
"pages/pick-user/pick-user",
|
||||
"pages/messages/messages",
|
||||
"pages/mine/mine",
|
||||
"pages/product-detail/product-detail",
|
||||
"pages/submit/submit",
|
||||
"pages/edit/edit",
|
||||
"pages/fighter/fighter",
|
||||
"pages/merchant/merchant",
|
||||
"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",
|
||||
"pages/fighter-rules/fighter-rules",
|
||||
"pages/fighter-edit/fighter-edit",
|
||||
"pages/fighter-rank/fighter-rank",
|
||||
"pages/fighter-recharge/fighter-recharge",
|
||||
"pages/fighter-msg/fighter-msg",
|
||||
"pages/fighter-orders/fighter-orders",
|
||||
"pages/accept-order/accept-order",
|
||||
"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/fighter-orders/fighter-orders",
|
||||
"pages/fighter/fighter",
|
||||
"pages/merchant-home/merchant-home",
|
||||
"pages/merchant-orders/merchant-orders",
|
||||
"pages/merchant-msg/merchant-msg",
|
||||
"pages/merchant-rank/merchant-rank",
|
||||
"pages/merchant-recharge/merchant-recharge",
|
||||
"pages/merchant-order-detail/merchant-order-detail",
|
||||
"pages/fighter-order-detail/fighter-order-detail",
|
||||
"pages/chat/chat",
|
||||
"pages/express-order/express-order",
|
||||
"pages/order-pool/order-pool",
|
||||
"pages/poster/poster",
|
||||
"pages/gold-fighter/gold-fighter",
|
||||
"pages/leader/leader",
|
||||
"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",
|
||||
"pages/merchant/merchant",
|
||||
"components/global-notification/global-notification",
|
||||
"components/popup-notice/popup-notice",
|
||||
"components/order-card/order-card",
|
||||
"components/order-sender/order-sender",
|
||||
"pages/penalty/penalty/penalty",
|
||||
"pages/penalty/components/fakuan-list/fakuan-list",
|
||||
"pages/penalty/components/jifen-list/jifen-list",
|
||||
"pages/penalty/components/fakuan-pay/fakuan-pay",
|
||||
"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"
|
||||
|
||||
|
||||
"components/chenghao-tag/chenghao-tag"
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/dashouduan",
|
||||
"pages": ["dashouduan"]
|
||||
},
|
||||
{
|
||||
"root": "pages/guanshiduan",
|
||||
"pages": ["guanshiduan"]
|
||||
},
|
||||
{
|
||||
"root": "pages/pick-user",
|
||||
"pages": ["pick-user"]
|
||||
},
|
||||
{
|
||||
"root": "pages/product-detail",
|
||||
"pages": ["product-detail"]
|
||||
},
|
||||
{
|
||||
"root": "pages/submit",
|
||||
"pages": ["submit"]
|
||||
},
|
||||
{
|
||||
"root": "pages/edit",
|
||||
"pages": ["edit"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-kefu-key",
|
||||
"pages": ["merchant-kefu-key"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-kefu-list",
|
||||
"pages": ["merchant-kefu-list"]
|
||||
},
|
||||
{
|
||||
"root": "pages/staff-join",
|
||||
"pages": ["staff-join"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-staff-audit",
|
||||
"pages": ["merchant-staff-audit"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-staff-role",
|
||||
"pages": ["merchant-staff-role"]
|
||||
},
|
||||
{
|
||||
"root": "pages/manager",
|
||||
"pages": ["manager"]
|
||||
},
|
||||
{
|
||||
"root": "pages/orders",
|
||||
"pages": ["orders"]
|
||||
},
|
||||
{
|
||||
"root": "pages/order-detail",
|
||||
"pages": ["order-detail"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-rules",
|
||||
"pages": ["fighter-rules"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-edit",
|
||||
"pages": ["fighter-edit"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-rank",
|
||||
"pages": ["fighter-rank"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-recharge",
|
||||
"pages": ["fighter-recharge", "fighter-deposit"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-ledger",
|
||||
"pages": ["fighter-ledger"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-msg",
|
||||
"pages": ["fighter-msg"]
|
||||
},
|
||||
{
|
||||
"root": "pages/dashou-exam",
|
||||
"pages": ["dashou-exam"]
|
||||
},
|
||||
{
|
||||
"root": "pages/withdraw",
|
||||
"pages": ["withdraw", "withdraw-records", "components/mode1/mode1", "components/mode2/mode2"]
|
||||
},
|
||||
{
|
||||
"root": "pages/invite-fighter",
|
||||
"pages": ["invite-fighter"]
|
||||
},
|
||||
{
|
||||
"root": "pages/my-fighter",
|
||||
"pages": ["my-fighter"]
|
||||
},
|
||||
{
|
||||
"root": "pages/recharge-log",
|
||||
"pages": ["recharge-log"]
|
||||
},
|
||||
{
|
||||
"root": "pages/manager-rank",
|
||||
"pages": ["manager-rank"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-data-stats",
|
||||
"pages": ["merchant-data-stats"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-dispatch",
|
||||
"pages": ["merchant-dispatch"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-regular-dispatch",
|
||||
"pages": ["merchant-regular-dispatch"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-msg",
|
||||
"pages": ["merchant-msg"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-penalty",
|
||||
"pages": ["merchant-penalty"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-rank",
|
||||
"pages": ["merchant-rank"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-recharge",
|
||||
"pages": ["merchant-recharge"]
|
||||
},
|
||||
{
|
||||
"root": "pages/merchant-order-detail",
|
||||
"pages": ["merchant-order-detail"]
|
||||
},
|
||||
{
|
||||
"root": "pages/fighter-order-detail",
|
||||
"pages": ["fighter-order-detail"]
|
||||
},
|
||||
{
|
||||
"root": "pages/chat",
|
||||
"pages": ["chat"]
|
||||
},
|
||||
{
|
||||
"root": "pages/express-order",
|
||||
"pages": ["express-order"]
|
||||
},
|
||||
{
|
||||
"root": "pages/order-pool",
|
||||
"pages": ["order-pool"]
|
||||
},
|
||||
{
|
||||
"root": "pages/poster",
|
||||
"pages": ["poster"]
|
||||
},
|
||||
{
|
||||
"root": "pages/gold-fighter",
|
||||
"pages": ["gold-fighter"]
|
||||
},
|
||||
{
|
||||
"root": "pages/leader",
|
||||
"pages": ["leader"]
|
||||
},
|
||||
{
|
||||
"root": "pages/manager-assign",
|
||||
"pages": ["manager-assign"]
|
||||
},
|
||||
{
|
||||
"root": "pages/leader-bonus-log",
|
||||
"pages": ["leader-bonus-log"]
|
||||
},
|
||||
{
|
||||
"root": "pages/invite-manager",
|
||||
"pages": ["invite-manager"]
|
||||
},
|
||||
{
|
||||
"root": "pages/group-chat",
|
||||
"pages": ["group-chat"]
|
||||
},
|
||||
{
|
||||
"root": "pages/cs-chat",
|
||||
"pages": ["cs-chat"]
|
||||
},
|
||||
{
|
||||
"root": "pages/verify",
|
||||
"pages": ["verify"]
|
||||
},
|
||||
{
|
||||
"root": "pages/penalty",
|
||||
"pages": [
|
||||
"penalty/penalty",
|
||||
"components/fakuan-list/fakuan-list",
|
||||
"components/jifen-list/jifen-list",
|
||||
"components/fakuan-pay/fakuan-pay"
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/phone-auth",
|
||||
"pages": ["phone-auth"]
|
||||
},
|
||||
{
|
||||
"root": "pages/assessor",
|
||||
"pages": ["assessor"]
|
||||
},
|
||||
{
|
||||
"root": "pages/escort-orders",
|
||||
"pages": ["escort-orders"]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -88,25 +258,32 @@
|
||||
"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"
|
||||
|
||||
61
app.wxss
61
app.wxss
@@ -2,15 +2,6 @@
|
||||
|
||||
/* ========== CSS 变量 ========== */
|
||||
page {
|
||||
/* 星阙紫色主题 */
|
||||
--theme-primary: #9333ea;
|
||||
--theme-primary-light: #a855f7;
|
||||
--theme-primary-lighter: #c4b5fd;
|
||||
--theme-primary-lightest: #ede9fe;
|
||||
--theme-bg-tint: #f5f3ff;
|
||||
--theme-gradient: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
--theme-gradient-bg: linear-gradient(180deg, #c4b5fd 0%, #fff 28%, #f5f3ff 100%);
|
||||
|
||||
--color-price: #ff4444;
|
||||
--color-text-main: #333;
|
||||
--color-text-gray: #999;
|
||||
@@ -467,7 +458,7 @@ page {
|
||||
|
||||
.tag-warning {
|
||||
background: rgba(250, 173, 20, 0.1);
|
||||
color: #a855f7;
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.tag-danger {
|
||||
@@ -477,7 +468,7 @@ page {
|
||||
|
||||
.tag-gold {
|
||||
background: rgba(201, 169, 98, 0.1);
|
||||
color: #9333ea;
|
||||
color: #C9A962;
|
||||
}
|
||||
|
||||
/* ========== 页面容器 ========== */
|
||||
@@ -524,8 +515,8 @@ page {
|
||||
}
|
||||
|
||||
.btn-primary--gold {
|
||||
background: linear-gradient(135deg, #9333ea, #a855f7);
|
||||
box-shadow: 0 8rpx 20rpx rgba(147, 51, 234, 0.3);
|
||||
background: linear-gradient(135deg, #C9A962, #D4B56A);
|
||||
box-shadow: 0 8rpx 20rpx rgba(201, 169, 98, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary--green {
|
||||
@@ -539,8 +530,8 @@ page {
|
||||
}
|
||||
|
||||
.btn-primary--orange {
|
||||
background: linear-gradient(135deg, #a855f7, #7c3aed);
|
||||
box-shadow: 0 8rpx 20rpx rgba(124, 58, 237, 0.3);
|
||||
background: linear-gradient(135deg, #ffb347, #ff8c1a);
|
||||
box-shadow: 0 8rpx 20rpx rgba(255, 140, 26, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary--purple {
|
||||
@@ -574,8 +565,8 @@ page {
|
||||
}
|
||||
|
||||
.btn-sm--gold {
|
||||
background: linear-gradient(135deg, #c4b5fd, #9333ea);
|
||||
box-shadow: 0 6rpx 16rpx rgba(147, 51, 234, 0.3);
|
||||
background: linear-gradient(135deg, #ffcc00, #ffaa00);
|
||||
box-shadow: 0 6rpx 16rpx rgba(255, 170, 0, 0.3);
|
||||
}
|
||||
|
||||
.btn-sm--green {
|
||||
@@ -613,8 +604,8 @@ page {
|
||||
}
|
||||
|
||||
.btn-ghost--gold {
|
||||
color: #9333ea;
|
||||
border-color: #9333ea;
|
||||
color: #C9A962;
|
||||
border-color: #C9A962;
|
||||
}
|
||||
|
||||
/* 禁用状态 */
|
||||
@@ -863,8 +854,8 @@ page {
|
||||
}
|
||||
|
||||
.status-tag--warning {
|
||||
background: #faf5ff;
|
||||
color: #a855f7;
|
||||
background: #fffbe6;
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.status-tag--info {
|
||||
@@ -971,8 +962,8 @@ page {
|
||||
}
|
||||
|
||||
@keyframes crownFloat {
|
||||
0%, 100% { transform: translateY(0) rotate(5deg) scale(1); filter: drop-shadow(0 0 10rpx rgba(196, 181, 253, 0.8)); }
|
||||
50% { transform: translateY(-15rpx) rotate(10deg) scale(1.1); filter: drop-shadow(0 0 25rpx rgba(196, 181, 253, 1)); }
|
||||
0%, 100% { transform: translateY(0) rotate(5deg) scale(1); filter: drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.8)); }
|
||||
50% { transform: translateY(-15rpx) rotate(10deg) scale(1.1); filter: drop-shadow(0 0 25rpx rgba(255, 215, 0, 1)); }
|
||||
}
|
||||
|
||||
@keyframes haloRotate {
|
||||
@@ -1112,14 +1103,14 @@ page {
|
||||
}
|
||||
|
||||
.chat-order-bubble {
|
||||
background: #f5f3ff;
|
||||
background: #fff7e0;
|
||||
padding: 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.chat-order-label {
|
||||
font-size: 24rpx;
|
||||
color: #a855f7;
|
||||
color: #e6a23c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -1614,7 +1605,7 @@ page {
|
||||
|
||||
.rank-card--1 .rank-podium-base { height: 80rpx; }
|
||||
|
||||
.rank-podium-gold { background: linear-gradient(180deg, rgba(196, 181, 253, 0.3) 0%, rgba(196, 181, 253, 0.1) 100%); border: 1rpx solid rgba(196, 181, 253, 0.4); }
|
||||
.rank-podium-gold { background: linear-gradient(180deg, rgba(255, 215, 0, 0.3) 0%, rgba(255, 215, 0, 0.1) 100%); border: 1rpx solid rgba(255, 215, 0, 0.4); }
|
||||
.rank-podium-silver { background: linear-gradient(180deg, rgba(192, 192, 192, 0.3) 0%, rgba(192, 192, 192, 0.1) 100%); border: 1rpx solid rgba(192, 192, 192, 0.4); }
|
||||
.rank-podium-bronze { background: linear-gradient(180deg, rgba(205, 127, 50, 0.3) 0%, rgba(205, 127, 50, 0.1) 100%); border: 1rpx solid rgba(205, 127, 50, 0.4); }
|
||||
|
||||
@@ -1642,7 +1633,7 @@ page {
|
||||
font-size: 85rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
filter: drop-shadow(0 0 10rpx rgba(196, 181, 253, 0.8));
|
||||
filter: drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.8));
|
||||
animation: crownFloat 3s ease-in-out infinite;
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
@@ -1654,7 +1645,7 @@ page {
|
||||
transform: translate(-50%, -50%);
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
background: radial-gradient(circle, rgba(196, 181, 253, 0.3) 0%, rgba(196, 181, 253, 0.1) 40%, transparent 70%);
|
||||
background: radial-gradient(circle, rgba(255, 215, 0, 0.3) 0%, rgba(255, 215, 0, 0.1) 40%, transparent 70%);
|
||||
z-index: 0;
|
||||
animation: glowPulse 2s ease-in-out infinite;
|
||||
}
|
||||
@@ -1686,7 +1677,7 @@ page {
|
||||
|
||||
.rank-card--1 .rank-avatar-container { border-width: 6rpx; }
|
||||
|
||||
.rank-frame-gold .rank-avatar-container { border-color: rgba(196, 181, 253, 0.3); filter: drop-shadow(0 0 30rpx rgba(196, 181, 253, 0.5)); }
|
||||
.rank-frame-gold .rank-avatar-container { border-color: rgba(255, 215, 0, 0.3); filter: drop-shadow(0 0 30rpx rgba(255, 215, 0, 0.5)); }
|
||||
.rank-frame-silver .rank-avatar-container { border-color: rgba(192, 192, 192, 0.3); filter: drop-shadow(0 0 25rpx rgba(192, 192, 192, 0.5)); }
|
||||
.rank-frame-bronze .rank-avatar-container { border-color: rgba(205, 127, 50, 0.3); filter: drop-shadow(0 0 25rpx rgba(205, 127, 50, 0.5)); }
|
||||
|
||||
@@ -1710,7 +1701,7 @@ page {
|
||||
animation: haloRotate 10s linear infinite;
|
||||
}
|
||||
|
||||
.rank-halo-gold { width: 220rpx; height: 220rpx; background: radial-gradient(circle, rgba(196, 181, 253, 0.4) 0%, rgba(196, 181, 253, 0.2) 30%, transparent 70%); }
|
||||
.rank-halo-gold { width: 220rpx; height: 220rpx; background: radial-gradient(circle, rgba(255, 215, 0, 0.4) 0%, rgba(255, 215, 0, 0.2) 30%, transparent 70%); }
|
||||
.rank-card--1 .rank-halo-gold { width: 260rpx; height: 260rpx; }
|
||||
.rank-halo-silver { width: 200rpx; height: 200rpx; background: radial-gradient(circle, rgba(192, 192, 192, 0.4) 0%, rgba(192, 192, 192, 0.2) 30%, transparent 70%); }
|
||||
.rank-halo-bronze { width: 200rpx; height: 200rpx; background: radial-gradient(circle, rgba(205, 127, 50, 0.4) 0%, rgba(205, 127, 50, 0.2) 30%, transparent 70%); }
|
||||
@@ -1748,7 +1739,7 @@ page {
|
||||
|
||||
.rank-card--1 .rank-badge { width: 70rpx; height: 70rpx; font-size: 32rpx; bottom: -15rpx; right: -15rpx; }
|
||||
|
||||
.rank-badge-gold { background: linear-gradient(135deg, #c4b5fd, #9333ea); }
|
||||
.rank-badge-gold { background: linear-gradient(135deg, #ffd700, #ff9800); }
|
||||
.rank-badge-silver { background: linear-gradient(135deg, #c0c0c0, #808080); }
|
||||
.rank-badge-bronze { background: linear-gradient(135deg, #cd7f32, #a0522d); }
|
||||
|
||||
@@ -1763,7 +1754,7 @@ page {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rank-card--1 .rank-user-info-card { border-color: rgba(196, 181, 253, 0.3); background: rgba(20, 15, 5, 0.95); }
|
||||
.rank-card--1 .rank-user-info-card { border-color: rgba(255, 215, 0, 0.3); background: rgba(20, 15, 5, 0.95); }
|
||||
.rank-card--2 .rank-user-info-card { border-color: rgba(192, 192, 192, 0.3); background: rgba(25, 25, 35, 0.95); }
|
||||
.rank-card--3 .rank-user-info-card { border-color: rgba(205, 127, 50, 0.3); background: rgba(30, 20, 10, 0.95); }
|
||||
|
||||
@@ -1778,7 +1769,7 @@ page {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rank-card--1 .rank-user-name { color: #c4b5fd; text-shadow: 0 0 15rpx rgba(196, 181, 253, 0.7); font-size: 34rpx; }
|
||||
.rank-card--1 .rank-user-name { color: #ffd700; text-shadow: 0 0 15rpx rgba(255, 215, 0, 0.7); font-size: 34rpx; }
|
||||
.rank-card--2 .rank-user-name { color: #c0c0c0; text-shadow: 0 0 10rpx rgba(192, 192, 192, 0.7); }
|
||||
.rank-card--3 .rank-user-name { color: #cd7f32; text-shadow: 0 0 10rpx rgba(205, 127, 50, 0.7); }
|
||||
|
||||
@@ -1816,7 +1807,7 @@ page {
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.rank-card--1 .rank-currency { color: #c4b5fd; }
|
||||
.rank-card--1 .rank-currency { color: #ffd700; }
|
||||
.rank-card--2 .rank-currency { color: #c0c0c0; }
|
||||
.rank-card--3 .rank-currency { color: #cd7f32; }
|
||||
|
||||
@@ -1827,7 +1818,7 @@ page {
|
||||
text-shadow: 0 0 10rpx rgba(74, 222, 128, 0.5);
|
||||
}
|
||||
|
||||
.rank-card--1 .rank-amount { color: #c4b5fd; text-shadow: 0 0 15rpx rgba(196, 181, 253, 0.7); }
|
||||
.rank-card--1 .rank-amount { color: #ffd700; text-shadow: 0 0 15rpx rgba(255, 215, 0, 0.7); }
|
||||
.rank-card--2 .rank-amount { color: #c0c0c0; text-shadow: 0 0 10rpx rgba(192, 192, 192, 0.7); }
|
||||
.rank-card--3 .rank-amount { color: #cd7f32; text-shadow: 0 0 10rpx rgba(205, 127, 50, 0.7); }
|
||||
|
||||
|
||||
@@ -1,35 +1,90 @@
|
||||
// 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: null, value: null },
|
||||
},
|
||||
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 || {};
|
||||
this._applyConfig(this.properties.texiaoJson);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
_applyConfig(raw) {
|
||||
let cfg = raw || {};
|
||||
if (typeof cfg === 'string') {
|
||||
try {
|
||||
cfg = JSON.parse(cfg);
|
||||
} catch (e) {
|
||||
cfg = {};
|
||||
}
|
||||
}
|
||||
if (Array.isArray(cfg)) {
|
||||
cfg = cfg[0] || {};
|
||||
}
|
||||
if (!cfg || typeof cfg !== 'object') {
|
||||
cfg = {};
|
||||
}
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
|
||||
// 1. 尺寸(稍大一些,一行能放3~4个)
|
||||
const width = cfg.width || 152;
|
||||
const height = cfg.height || 52;
|
||||
// 无 shape:身份装饰标签 / 自定义色圆角标;有 shape:考核称号等特殊形状
|
||||
const isPill = !cfg.shape || PILL_SHAPES.includes(cfg.shape);
|
||||
|
||||
// 2. 背景处理:优先背景图,否则用渐变/纯色
|
||||
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
|
||||
: ossImageUrl + cfg.image_url;
|
||||
@@ -38,36 +93,27 @@ Component({
|
||||
} else if (cfg.bg_color) {
|
||||
bgStyle = `background: ${cfg.bg_color};`;
|
||||
} else {
|
||||
// 默认紫色渐变
|
||||
bgStyle = `background: linear-gradient(135deg, #c4b5fd, #9333ea);`;
|
||||
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,
|
||||
});
|
||||
},
|
||||
|
||||
onImageError() {
|
||||
this.setData({ imageUrl: '' });
|
||||
if (!this.data.inlineStyle.includes('background')) {
|
||||
this.setData({
|
||||
inlineStyle: this.data.inlineStyle + ' background: linear-gradient(135deg, #FFD700, #FF8C00);',
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 背景图加载失败时回退为纯色背景
|
||||
onImageError() {
|
||||
this.setData({ imageUrl: '' });
|
||||
// 如果 bgStyle 为空,给个默认背景
|
||||
if (!this.data.bgStyle) {
|
||||
this.setData({ bgStyle: 'background: linear-gradient(135deg, #c4b5fd, #9333ea);' });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,18 +1,15 @@
|
||||
<!-- components/chenghao-tag/chenghao-tag.wxml -->
|
||||
<view
|
||||
class="tag-root {{shapeClass}} {{animationClass}}"
|
||||
style="width: {{width}}rpx; height: {{height}}rpx; {{bgStyle}}"
|
||||
style="{{inlineStyle}}"
|
||||
>
|
||||
<!-- 背景图(如果有) -->
|
||||
<image
|
||||
wx:if="{{imageUrl}}"
|
||||
wx:if="{{imageUrl && !isPill}}"
|
||||
class="tag-bg-image"
|
||||
src="{{imageUrl}}"
|
||||
mode="aspectFill"
|
||||
binderror="onImageError"
|
||||
></image>
|
||||
|
||||
<!-- 文字层 -->
|
||||
/>
|
||||
<text
|
||||
class="tag-text"
|
||||
style="color: {{textColor}}; font-size: {{textSize}}rpx;"
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
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 {
|
||||
position: absolute;
|
||||
@@ -85,6 +97,21 @@
|
||||
animation: glow-anim 2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes glow-anim {
|
||||
0%, 100% { box-shadow: 0 0 8rpx rgba(196, 181, 253, 0.4); }
|
||||
50% { box-shadow: 0 0 20rpx rgba(196, 181, 253, 0.8), 0 0 40rpx rgba(196, 181, 253, 0.4); }
|
||||
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;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
const { resolveAvatarUrl } = require('../../utils/avatar.js');
|
||||
const { resolveAvatarUrl, getDefaultAvatarUrl } = require('../../utils/avatar.js');
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
@@ -53,6 +53,7 @@ Component({
|
||||
|
||||
// 静音状态
|
||||
isMuted: false,
|
||||
formattedTime: '',
|
||||
|
||||
// 自动隐藏计时器
|
||||
autoHideTimeout: null
|
||||
@@ -123,7 +124,7 @@ Component({
|
||||
let avatar = resolveAvatarUrl(
|
||||
data.avatar || data.message?.senderData?.avatar,
|
||||
app
|
||||
);
|
||||
) || getDefaultAvatarUrl(app);
|
||||
|
||||
this.setData({
|
||||
show: true,
|
||||
@@ -131,6 +132,7 @@ Component({
|
||||
message: data.content || '[消息内容]',
|
||||
avatar: avatar,
|
||||
notificationData: data,
|
||||
formattedTime: this.formatTime(data.timestamp),
|
||||
progress: 100,
|
||||
swipingClass: '',
|
||||
swipeStyle: ''
|
||||
@@ -149,6 +151,7 @@ Component({
|
||||
this.clearTimers();
|
||||
this.setData({
|
||||
show: false,
|
||||
formattedTime: '',
|
||||
progress: 100,
|
||||
swipingClass: '',
|
||||
swipeStyle: ''
|
||||
@@ -266,6 +269,13 @@ Component({
|
||||
this.hideNotification();
|
||||
},
|
||||
|
||||
onAvatarError() {
|
||||
const def = getDefaultAvatarUrl(getApp());
|
||||
if (def && this.data.avatar !== def) {
|
||||
this.setData({ avatar: def });
|
||||
}
|
||||
},
|
||||
|
||||
onMute(e) {
|
||||
e.stopPropagation();
|
||||
const newMutedState = !this.data.isMuted;
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
<!-- 通知内容 -->
|
||||
<view class="notification-content">
|
||||
<!-- 左侧头像 -->
|
||||
<image class="notification-avatar" src="{{avatar}}" mode="aspectFill"></image>
|
||||
<image class="notification-avatar" src="{{avatar}}" mode="aspectFill" binderror="onAvatarError"></image>
|
||||
|
||||
<!-- 中间内容区域 -->
|
||||
<view class="notification-body">
|
||||
<view class="notification-tag">新消息</view>
|
||||
<view class="notification-title">{{title}}</view>
|
||||
<view class="notification-message">{{message}}</view>
|
||||
<view class="notification-time" wx:if="{{showTime}}">
|
||||
{{formatTime(notificationData ? notificationData.timestamp : '')}}
|
||||
<view class="notification-time" wx:if="{{show && showTime && formattedTime}}">
|
||||
{{formattedTime}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,29 +1,40 @@
|
||||
.global-notification {
|
||||
position: fixed;
|
||||
left: 20rpx;
|
||||
right: 20rpx;
|
||||
top: 0rpx;
|
||||
left: 24rpx;
|
||||
right: 24rpx;
|
||||
top: calc(env(safe-area-inset-top) + 108rpx);
|
||||
z-index: 99999;
|
||||
opacity: 0;
|
||||
transform: translateY(-120%);
|
||||
transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
transform: translateY(-160%);
|
||||
transition: all 0.42s cubic-bezier(0.34, 1.2, 0.64, 1);
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(41, 47, 61, 0.98) 0%,
|
||||
rgba(31, 36, 48, 0.98) 100%);
|
||||
backdrop-filter: blur(30rpx) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(30rpx) saturate(180%);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 28rpx;
|
||||
background: linear-gradient(145deg,
|
||||
rgba(28, 32, 42, 0.97) 0%,
|
||||
rgba(22, 26, 36, 0.98) 100%);
|
||||
backdrop-filter: blur(40rpx) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(40rpx) saturate(180%);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 20rpx 60rpx rgba(0, 0, 0, 0.4),
|
||||
0 0 0 1rpx rgba(255, 255, 255, 0.03),
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.1);
|
||||
min-height: 140rpx;
|
||||
0 24rpx 64rpx rgba(0, 0, 0, 0.45),
|
||||
0 0 0 1rpx rgba(255, 255, 255, 0.04),
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.12);
|
||||
min-height: 148rpx;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
.global-notification::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 24rpx;
|
||||
bottom: 24rpx;
|
||||
width: 6rpx;
|
||||
border-radius: 0 6rpx 6rpx 0;
|
||||
background: linear-gradient(180deg, #07c160 0%, #D4AF37 100%);
|
||||
}
|
||||
|
||||
.global-notification.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
@@ -58,8 +69,8 @@
|
||||
.notification-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx;
|
||||
min-height: 140rpx;
|
||||
padding: 28rpx 28rpx 28rpx 36rpx;
|
||||
min-height: 148rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -84,6 +95,18 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.notification-tag {
|
||||
display: inline-block;
|
||||
align-self: flex-start;
|
||||
font-size: 20rpx;
|
||||
color: #07c160;
|
||||
background: rgba(7, 193, 96, 0.15);
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.notification-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
|
||||
170
components/kefu-float/kefu-float.js
Normal file
170
components/kefu-float/kefu-float.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import { openCustomerServiceChat, resolveKefuIconUrl } from '../../utils/kefu-nav.js';
|
||||
|
||||
const CS_HOUR_START = 8;
|
||||
const CS_HOUR_END = 23;
|
||||
|
||||
function getCsOnlineStatus() {
|
||||
const hour = new Date().getHours();
|
||||
const online = hour >= CS_HOUR_START && hour < CS_HOUR_END;
|
||||
return {
|
||||
csOnline: online,
|
||||
csHoursText: '8:00-23:00',
|
||||
csStatusText: online ? '在线' : '在线时间',
|
||||
};
|
||||
}
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
bottom: { type: String, value: '200rpx' },
|
||||
},
|
||||
|
||||
data: {
|
||||
mode: 'full',
|
||||
sessionHidden: false,
|
||||
csUnread: 0,
|
||||
floatY: 520,
|
||||
iconUrl: '',
|
||||
csOnline: false,
|
||||
csHoursText: '8:00-23:00',
|
||||
csStatusText: '在线时间',
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const app = getApp();
|
||||
this._syncIcon();
|
||||
this._syncFromGlobal();
|
||||
this._initFloatY();
|
||||
this._convHandler = () => this.syncCsUnread();
|
||||
this._configHandler = () => this._syncIcon();
|
||||
if (app.on) {
|
||||
app.on('conversationsUpdated', this._convHandler);
|
||||
app.on('unreadCountChanged', this._convHandler);
|
||||
app.on('configApplied', this._configHandler);
|
||||
}
|
||||
this.syncCsUnread();
|
||||
this._syncOnlineStatus();
|
||||
},
|
||||
detached() {
|
||||
const app = getApp();
|
||||
if (app.off) {
|
||||
if (this._convHandler) {
|
||||
app.off('conversationsUpdated', this._convHandler);
|
||||
app.off('unreadCountChanged', this._convHandler);
|
||||
}
|
||||
if (this._configHandler) {
|
||||
app.off('configApplied', this._configHandler);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
this._syncIcon();
|
||||
this._syncFromGlobal();
|
||||
this.syncCsUnread();
|
||||
this._syncOnlineStatus();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
_syncOnlineStatus() {
|
||||
this.setData(getCsOnlineStatus());
|
||||
},
|
||||
_syncIcon() {
|
||||
const url = resolveKefuIconUrl(getApp());
|
||||
if (url && url !== this.data.iconUrl) {
|
||||
this.setData({ iconUrl: url });
|
||||
}
|
||||
},
|
||||
|
||||
_initFloatY() {
|
||||
const app = getApp();
|
||||
if (app.globalData.kefuFloatY != null) {
|
||||
this.setData({ floatY: app.globalData.kefuFloatY });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
const defaultY = Math.floor((sys.windowHeight || 600) * 0.55);
|
||||
this.setData({ floatY: defaultY });
|
||||
} catch (e) {
|
||||
this.setData({ floatY: 520 });
|
||||
}
|
||||
},
|
||||
|
||||
_syncFromGlobal() {
|
||||
const app = getApp();
|
||||
const sessionHidden = !!app.globalData.kefuFloatSessionHidden;
|
||||
const mode = app.globalData.kefuFloatMode || 'full';
|
||||
this.setData({ sessionHidden, mode });
|
||||
},
|
||||
|
||||
onFloatMove(e) {
|
||||
const y = e.detail && e.detail.y;
|
||||
if (y == null) return;
|
||||
const app = getApp();
|
||||
app.globalData.kefuFloatY = y;
|
||||
},
|
||||
|
||||
syncCsUnread() {
|
||||
if (this.data.sessionHidden) return;
|
||||
if (!wx.goEasy?.im?.latestConversations) return;
|
||||
wx.goEasy.im.latestConversations({
|
||||
onSuccess: (res) => {
|
||||
const list = res.content?.conversations || res.conversations || [];
|
||||
let unread = 0;
|
||||
list.forEach((c) => {
|
||||
if (c.type === 'cs' || c.teamId === 'support_team') {
|
||||
unread += c.unread || 0;
|
||||
}
|
||||
});
|
||||
if (unread !== this.data.csUnread) {
|
||||
this.setData({ csUnread: unread });
|
||||
}
|
||||
},
|
||||
onFailed: () => {},
|
||||
});
|
||||
},
|
||||
|
||||
onIconError() {
|
||||
this._syncIcon();
|
||||
},
|
||||
|
||||
onContact() {
|
||||
openCustomerServiceChat();
|
||||
},
|
||||
|
||||
onHideTap(e) {
|
||||
if (e && e.stopPropagation) e.stopPropagation();
|
||||
const app = getApp();
|
||||
app.globalData.kefuFloatMode = 'mini';
|
||||
this.setData({ mode: 'mini' });
|
||||
},
|
||||
|
||||
onExpand(e) {
|
||||
if (e && e.stopPropagation) e.stopPropagation();
|
||||
const app = getApp();
|
||||
app.globalData.kefuFloatMode = 'full';
|
||||
this.setData({ mode: 'full' });
|
||||
},
|
||||
|
||||
onSessionDismiss(e) {
|
||||
if (e && e.stopPropagation) e.stopPropagation();
|
||||
wx.showModal({
|
||||
title: '隐藏联系客服',
|
||||
content: '本次使用期间不再显示此按钮。清空后台重新进入小程序后会再次出现。',
|
||||
confirmText: '确定隐藏',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const app = getApp();
|
||||
app.globalData.kefuFloatSessionHidden = true;
|
||||
this.setData({ sessionHidden: true });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
4
components/kefu-float/kefu-float.json
Normal file
4
components/kefu-float/kefu-float.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
44
components/kefu-float/kefu-float.wxml
Normal file
44
components/kefu-float/kefu-float.wxml
Normal file
@@ -0,0 +1,44 @@
|
||||
<movable-area class="kefu-movable-area" wx:if="{{!sessionHidden}}">
|
||||
<movable-view
|
||||
class="kefu-movable-view"
|
||||
direction="vertical"
|
||||
y="{{floatY}}"
|
||||
damping="40"
|
||||
friction="2"
|
||||
bindchange="onFloatMove"
|
||||
>
|
||||
<block wx:if="{{mode === 'full'}}">
|
||||
<view class="kefu-wrap">
|
||||
<view class="kefu-float kefu-float--full">
|
||||
<view class="kefu-unread-badge" wx:if="{{csUnread > 0}}">{{csUnread > 99 ? '99+' : csUnread}}</view>
|
||||
<view class="kefu-float-main" catchtap="onContact">
|
||||
<image class="kefu-float-icon-img" src="{{iconUrl}}" mode="aspectFit" binderror="onIconError"/>
|
||||
<view class="kefu-text-col">
|
||||
<text class="kefu-float-text">联系客服</text>
|
||||
<text class="kefu-float-sub kefu-float-sub--warn" wx:if="{{csUnread > 0}}">有新消息</text>
|
||||
<text class="kefu-float-sub {{csOnline ? 'kefu-float-sub--online' : ''}}" wx:else>{{csStatusText}} {{csHoursText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="kefu-float-close" catchtap="onHideTap">
|
||||
<text>×</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="kefu-permanent-row" catchtap="onSessionDismiss">
|
||||
<text class="kefu-permanent-label">本次不再显示</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
<view class="kefu-wrap kefu-wrap--mini">
|
||||
<view class="kefu-float kefu-float--mini" catchtap="onContact" bindlongpress="onExpand">
|
||||
<view class="kefu-unread-badge kefu-unread-badge--mini" wx:if="{{csUnread > 0}}">{{csUnread > 99 ? '99+' : csUnread}}</view>
|
||||
<image class="kefu-float-mini-icon-img" src="{{iconUrl}}" mode="aspectFit" binderror="onIconError"/>
|
||||
</view>
|
||||
<view class="kefu-permanent-row kefu-permanent-row--mini" catchtap="onSessionDismiss">
|
||||
<text class="kefu-permanent-label">隐藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
186
components/kefu-float/kefu-float.wxss
Normal file
186
components/kefu-float/kefu-float.wxss
Normal file
@@ -0,0 +1,186 @@
|
||||
.kefu-movable-area {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 900;
|
||||
}
|
||||
|
||||
.kefu-movable-view {
|
||||
width: auto;
|
||||
height: auto;
|
||||
pointer-events: auto;
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.kefu-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.kefu-float {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.kefu-float--full {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06ad56 100%);
|
||||
border-radius: 999rpx;
|
||||
box-shadow: 0 8rpx 28rpx rgba(7, 193, 96, 0.45);
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.85);
|
||||
overflow: visible;
|
||||
animation: kefu-pulse 2.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes kefu-pulse {
|
||||
0%, 100% { box-shadow: 0 8rpx 28rpx rgba(7, 193, 96, 0.45); }
|
||||
50% { box-shadow: 0 8rpx 36rpx rgba(7, 193, 96, 0.65), 0 0 0 8rpx rgba(7, 193, 96, 0.12); }
|
||||
}
|
||||
|
||||
.kefu-unread-badge {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -6rpx;
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
padding: 0 8rpx;
|
||||
background: #fa5151;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
border-radius: 18rpx;
|
||||
border: 3rpx solid #fff;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.kefu-unread-badge--mini {
|
||||
top: -6rpx;
|
||||
right: -4rpx;
|
||||
}
|
||||
|
||||
.kefu-float-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 18rpx 24rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.kefu-float-main:active {
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.kefu-float-icon-img {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 4rpx;
|
||||
}
|
||||
|
||||
.kefu-text-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.kefu-float-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kefu-float-sub {
|
||||
font-size: 20rpx;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
margin-top: 2rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.kefu-float-sub--online {
|
||||
color: #e8ffe8;
|
||||
}
|
||||
|
||||
.kefu-float-sub--warn {
|
||||
color: #fff3e0;
|
||||
}
|
||||
|
||||
.kefu-float-close {
|
||||
width: 52rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.12);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 34rpx;
|
||||
line-height: 1;
|
||||
border-left: 1rpx solid rgba(255, 255, 255, 0.25);
|
||||
border-radius: 0 999rpx 999rpx 0;
|
||||
}
|
||||
|
||||
.kefu-float-close:active {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.kefu-permanent-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 6rpx;
|
||||
padding: 4rpx 10rpx;
|
||||
}
|
||||
|
||||
.kefu-permanent-row:active {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.kefu-permanent-row--mini {
|
||||
margin-top: 4rpx;
|
||||
padding: 2rpx 8rpx;
|
||||
}
|
||||
|
||||
.kefu-permanent-label {
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.kefu-wrap--mini {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.kefu-float--mini {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06ad56 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.45);
|
||||
border: 3rpx solid #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: kefu-pulse 2.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.kefu-float--mini:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.kefu-float-mini-icon-img {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 4rpx;
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
.card-id { font-size: 24rpx; color: #999; margin-left: 15rpx; }
|
||||
.cross-tag {
|
||||
margin-left: auto;
|
||||
background: #8b5cf6;
|
||||
background: #ff9900;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 12rpx;
|
||||
|
||||
@@ -1,16 +1,87 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
function mapIdentityForApi() {
|
||||
const role = app.globalData.currentRole || wx.getStorageSync('currentRole') || 'normal';
|
||||
if (role === 'dashou') return 'dashou';
|
||||
if (role === 'shangjia') return 'shangjia';
|
||||
return 'normal';
|
||||
}
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
visible: Boolean,
|
||||
showDetail: { type: Boolean, value: false }
|
||||
visible: { type: Boolean, value: false },
|
||||
groupId: { type: String, value: '' },
|
||||
orderId: { type: String, value: '' },
|
||||
},
|
||||
|
||||
data: {},
|
||||
data: {
|
||||
keyword: '',
|
||||
loading: false,
|
||||
orders: [],
|
||||
filteredOrders: [],
|
||||
},
|
||||
|
||||
observers: {
|
||||
visible(v) {
|
||||
if (v) {
|
||||
this.setData({ keyword: '' });
|
||||
this.loadOrders('');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
this.triggerEvent('close');
|
||||
},
|
||||
|
||||
onSearchInput(e) {
|
||||
const keyword = (e.detail.value || '').trim();
|
||||
this.setData({ keyword });
|
||||
if (this._searchTimer) clearTimeout(this._searchTimer);
|
||||
this._searchTimer = setTimeout(() => {
|
||||
this.loadOrders(keyword);
|
||||
}, 350);
|
||||
},
|
||||
|
||||
async loadOrders(keyword) {
|
||||
const { groupId, orderId } = this.properties;
|
||||
if (!groupId && !orderId) return;
|
||||
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/ltpddd',
|
||||
method: 'POST',
|
||||
data: {
|
||||
groupId,
|
||||
dingdan_id: orderId,
|
||||
keyword: keyword || '',
|
||||
identityType: mapIdentityForApi(),
|
||||
},
|
||||
});
|
||||
const body = res?.data || {};
|
||||
const list = body.data?.list || [];
|
||||
this.setData({
|
||||
orders: list,
|
||||
filteredOrders: list,
|
||||
loading: false,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('加载历史订单失败', e);
|
||||
this.setData({ loading: false, orders: [], filteredOrders: [] });
|
||||
wx.showToast({ title: '加载订单失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSelectOrder(e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const order = this.data.filteredOrders[index];
|
||||
if (!order) return;
|
||||
this.triggerEvent('send', { order });
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,12 +1,28 @@
|
||||
<view class="order-sender-mask" wx:if="{{visible}}" catchtap="close"></view>
|
||||
<view class="order-sender {{visible ? 'show' : ''}}">
|
||||
<view class="header">
|
||||
<text class="title">提示</text>
|
||||
<text class="title">选择订单发送</text>
|
||||
<text class="close" bindtap="close">✕</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="icon">📋</view>
|
||||
<text class="notice">此功能暂未开放</text>
|
||||
<text class="sub">敬请期待</text>
|
||||
<view class="search-row">
|
||||
<input class="search-input" placeholder="搜索订单号/内容" value="{{keyword}}" bindinput="onSearchInput" confirm-type="search" />
|
||||
</view>
|
||||
<scroll-view scroll-y class="order-list" enable-flex>
|
||||
<view class="loading-tip" wx:if="{{loading}}">加载中...</view>
|
||||
<block wx:for="{{filteredOrders}}" wx:key="dingdan_id">
|
||||
<view class="order-item" data-index="{{index}}" bindtap="onSelectOrder">
|
||||
<view class="order-item-top">
|
||||
<text class="order-id">{{item.dingdan_id}}</text>
|
||||
<text class="order-status">{{item.zhuangtaiText}}</text>
|
||||
</view>
|
||||
<text class="order-desc">{{item.jieshao || '暂无介绍'}}</text>
|
||||
<view class="order-item-bottom">
|
||||
<text class="order-price">¥{{item.jine || '0'}}</text>
|
||||
<text class="order-time">{{item.create_time}}</text>
|
||||
</view>
|
||||
<text class="order-send-hint">点击发送订单卡片</text>
|
||||
</view>
|
||||
</block>
|
||||
<view class="empty-tip" wx:if="{{!loading && !filteredOrders.length}}">暂无相关订单</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
@@ -1,45 +1,140 @@
|
||||
.order-sender-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 2000;
|
||||
}
|
||||
.order-sender {
|
||||
position: fixed; bottom: 0; left: 0; right: 0;
|
||||
height: 360rpx;
|
||||
}
|
||||
|
||||
.order-sender {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 72vh;
|
||||
max-height: 900rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
z-index: 2001;
|
||||
transform: translateY(100%);
|
||||
transition: 0.25s;
|
||||
transition: transform 0.25s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.order-sender.show { transform: translateY(0); }
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex; justify-content: space-between;
|
||||
.order-sender.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.title { font-size: 32rpx; font-weight: 600; }
|
||||
.close { font-size: 44rpx; color: #999; padding: 0 12rpx; }
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.close {
|
||||
font-size: 44rpx;
|
||||
color: #999;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
padding: 16rpx 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background: #f5f5f5;
|
||||
border-radius: 32rpx;
|
||||
padding: 16rpx 28rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.order-list {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
padding: 0 24rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading-tip,
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
.order-item {
|
||||
background: #fafafa;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.order-item:active {
|
||||
background: #f0f7ff;
|
||||
}
|
||||
|
||||
.order-item-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
.icon { font-size: 80rpx; margin-bottom: 20rpx; }
|
||||
.notice {
|
||||
font-size: 34rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.order-id {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.order-status {
|
||||
font-size: 22rpx;
|
||||
color: #07c160;
|
||||
background: #e8f8ee;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.order-desc {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.order-item-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.order-price {
|
||||
font-size: 28rpx;
|
||||
color: #ff6b00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 22rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.order-send-hint {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #007aff;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
15
components/pindao-modal/pindao-modal.js
Normal file
15
components/pindao-modal/pindao-modal.js
Normal file
@@ -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');
|
||||
},
|
||||
},
|
||||
});
|
||||
4
components/pindao-modal/pindao-modal.json
Normal file
4
components/pindao-modal/pindao-modal.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
24
components/pindao-modal/pindao-modal.wxml
Normal file
24
components/pindao-modal/pindao-modal.wxml
Normal file
@@ -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 || 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>
|
||||
88
components/pindao-modal/pindao-modal.wxss
Normal file
88
components/pindao-modal/pindao-modal.wxss
Normal file
@@ -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;
|
||||
}
|
||||
@@ -138,7 +138,7 @@
|
||||
.countdown-tip {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #9333ea;
|
||||
color: #ff6600;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.3);
|
||||
border: 2rpx solid rgba(196, 181, 253, 0.6);
|
||||
border: 2rpx solid rgba(255, 215, 0, 0.6);
|
||||
background: #f0f0f0;
|
||||
display: block;
|
||||
transform: translateZ(0);
|
||||
|
||||
11
config/club-config.js
Normal file
11
config/club-config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 小程序所属俱乐部配置 — 每个小程序工程编译前改这里即可。
|
||||
* 星阙 = xq;星之界 = xzj;与 club 表 club_id 一致。
|
||||
* 不要依赖后端猜测:前端必须明确所属 club。
|
||||
*/
|
||||
export const CLUB_ID = 'xzj';
|
||||
|
||||
/** 与 club 表 wx_appid 一致,登录时可传给后端反查 club */
|
||||
export const WX_APP_ID = 'wxdefa454152e78a03';
|
||||
|
||||
export const CLUB_NAME = '星之界电竞';
|
||||
@@ -1,9 +1,16 @@
|
||||
// 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';
|
||||
import { checkPenaltyForGrab } from '../../utils/grab-order-gate.js';
|
||||
import { fetchMyZhidingOrders, submitZhidingResponse, processZhidingList, filterZhidingBannerList, ZHIDING_HF_MAP } from '../../utils/zhiding-order.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -30,7 +37,7 @@ Page({
|
||||
// 4. 刷新控制字段
|
||||
scrollViewRefreshing: false,
|
||||
lastRefreshTime: 0,
|
||||
refreshCooldown: 2000,
|
||||
refreshCooldown: 0,
|
||||
isLoadingMore: false,
|
||||
|
||||
// 5. 切换类型冷却
|
||||
@@ -47,13 +54,93 @@ Page({
|
||||
|
||||
// 商品轮播展示(只读 globalData,无额外接口)
|
||||
lunboList: [],
|
||||
gonggao: '',
|
||||
|
||||
examRequired: false,
|
||||
examPassed: false,
|
||||
_examChecked: false,
|
||||
|
||||
myZhidingList: [],
|
||||
highlightOrderId: '',
|
||||
myZhidingCount: 0,
|
||||
defaultAvatarUrl: '',
|
||||
showZhidingDetail: false,
|
||||
zhidingDetailOrder: null,
|
||||
showLaterModal: false,
|
||||
laterOrderId: '',
|
||||
laterNote: '',
|
||||
},
|
||||
|
||||
async onLoad(options) {
|
||||
this.syncShopBannerFromGlobal();
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const highlight = (options && options.highlight) ? String(options.highlight) : '';
|
||||
this.setData({ defaultAvatarUrl: defAvatar, highlightOrderId: highlight });
|
||||
if (options && options.highlight) {
|
||||
this._pendingHighlight = String(options.highlight);
|
||||
}
|
||||
if (app.globalData._acceptOrderSessionReady && app.globalData._acceptOrderPageCache) {
|
||||
const cache = { ...app.globalData._acceptOrderPageCache };
|
||||
this.data.shangpinleixing = cache.shangpinleixing || [];
|
||||
const dingdanList = Array.isArray(cache.dingdanList)
|
||||
? cache.dingdanList.map((item) => this.processDingdanItem(item))
|
||||
: [];
|
||||
this.setData({
|
||||
...cache,
|
||||
shangpinleixing: cache.shangpinleixing || [],
|
||||
dingdanList,
|
||||
bankuaiBiaoqian: cache.bankuaiBiaoqian || [],
|
||||
lunboList: cache.lunboList || [],
|
||||
});
|
||||
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();
|
||||
await this.loadShangpinLeixing();
|
||||
this.registerNotificationComponent();
|
||||
await this.syncDashouProfileFromServer();
|
||||
if (!this.data.shangpinleixing || this.data.shangpinleixing.length === 0) {
|
||||
await this.loadShangpinLeixing(true, false, false);
|
||||
} else if (this.data.xuanzhongLeixingId) {
|
||||
await this.loadDingdanList(true, true);
|
||||
}
|
||||
this.persistPageCache();
|
||||
this._skipShowRefresh = true;
|
||||
await this.loadMyZhidingOrders(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadGlobalStatus();
|
||||
const banner = await this.loadGonggaoAndLunbo(false);
|
||||
await this.syncDashouProfileFromServer();
|
||||
await this.loadShangpinLeixing(true, false, false);
|
||||
await this.loadMyZhidingOrders(true);
|
||||
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 () {
|
||||
@@ -64,65 +151,94 @@ Page({
|
||||
},
|
||||
|
||||
async onShow() {
|
||||
if (!app.globalData._dashouPhoneChecked) {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
|
||||
if (!phoneOk) return;
|
||||
app.globalData._dashouPhoneChecked = true;
|
||||
}
|
||||
|
||||
this.syncShopBannerFromGlobal();
|
||||
this.registerNotificationComponent();
|
||||
this.loadGlobalStatus();
|
||||
await this.syncDashouProfileFromServer();
|
||||
warmupPindaoConfig(app);
|
||||
|
||||
if (wx.getStorageSync('uid')) {
|
||||
// IM 延后连接,避免切 Tab 时卡顿;订单列表不自动刷新,由用户下拉刷新
|
||||
setTimeout(() => {
|
||||
reconnectForRole('dashou');
|
||||
if (app.startImWhenReady) app.startImWhenReady();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
await this.checkExamStatus(false);
|
||||
|
||||
this.setData({ defaultAvatarUrl: getDefaultAvatarUrl(app) });
|
||||
await this.loadMyZhidingOrders(true);
|
||||
|
||||
const hl = app.globalData._acceptOrderHighlight;
|
||||
if (hl) {
|
||||
app.globalData._acceptOrderHighlight = '';
|
||||
this.setData({ highlightOrderId: hl });
|
||||
await this.loadMyZhidingOrders(true);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
await this.loadMyZhidingOrders(true);
|
||||
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() {
|
||||
async loadGonggaoAndLunbo(forceFetch = false) {
|
||||
let lunboRaw = app.globalData.shangpinlunbo || [];
|
||||
let gonggaoText = app.globalData.shangpingonggao || '';
|
||||
|
||||
if (forceFetch || !isGonggaoCacheValid(app)) {
|
||||
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;
|
||||
const data = await fetchGonggaoLunbo(app, 'accept_order');
|
||||
if (data) {
|
||||
lunboRaw = data.shangpinlunbo || app.globalData.shangpinlunbo || [];
|
||||
gonggaoText = data.shangpingonggao || app.globalData.shangpingonggao || '';
|
||||
}
|
||||
this.setData({
|
||||
zhuanghaoStatus: patch.zhanghaoStatus,
|
||||
dashouzhuangtai: patch.dashouzhuangtai,
|
||||
huiyuanList: patch.clumber,
|
||||
yajin: patch.yajin,
|
||||
jifen: patch.jifen,
|
||||
});
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
lunboRaw = app.globalData.shangpinlunbo || [];
|
||||
gonggaoText = app.globalData.shangpingonggao || '';
|
||||
}
|
||||
}
|
||||
|
||||
const lunboList = this.processLunboUrls(lunboRaw);
|
||||
const gonggao = gonggaoText;
|
||||
this.setData({ lunboList, gonggao });
|
||||
return { lunboList, gonggao };
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
@@ -148,9 +264,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',
|
||||
@@ -167,14 +296,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' });
|
||||
@@ -182,7 +313,7 @@ Page({
|
||||
} catch (error) {
|
||||
wx.showToast({ title: '网络错误,加载失败', icon: 'none' });
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
if (showLoading) wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -249,8 +380,8 @@ Page({
|
||||
xuanzhongBiaoqianId: 0 // 重置标签筛选
|
||||
});
|
||||
await this.loadDingdanList(true);
|
||||
// 🆕 切换类型后加载新标签
|
||||
await this.loadBankuaiBiaoqian();
|
||||
this.persistPageCache();
|
||||
} catch (error) {
|
||||
console.error('切换类型失败:', error);
|
||||
} finally {
|
||||
@@ -270,19 +401,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;
|
||||
|
||||
if (!silent) {
|
||||
this.setData({
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh
|
||||
isLoadingMore: !isRefresh,
|
||||
});
|
||||
}
|
||||
this._listRequesting = true;
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
@@ -302,6 +438,7 @@ Page({
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
});
|
||||
this._listRequesting = false;
|
||||
|
||||
if (res.data.code === 200 || res.data.code === 0) {
|
||||
const newList = res.data.data.list || [];
|
||||
@@ -322,6 +459,7 @@ Page({
|
||||
page: loadPage,
|
||||
hasMore: hasMore
|
||||
});
|
||||
this.persistPageCache();
|
||||
|
||||
if (isRefresh) {
|
||||
this.setData({ lastRefreshTime: Date.now() });
|
||||
@@ -336,25 +474,36 @@ Page({
|
||||
isLoadingMore: false,
|
||||
scrollViewRefreshing: false
|
||||
});
|
||||
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;
|
||||
const uid = this.data.uid;
|
||||
const isMyZhiding = isZhiding && uid && String(item.zhiding_uid) === String(uid);
|
||||
|
||||
let zhidingAvatar = '';
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
let zhidingAvatar = defAvatar;
|
||||
if (item.zhiding_avatar) {
|
||||
zhidingAvatar = !item.zhiding_avatar.startsWith('http')
|
||||
? ossUrl + item.zhiding_avatar
|
||||
@@ -362,31 +511,41 @@ Page({
|
||||
}
|
||||
const zhidingNicheng = item.zhiding_nicheng || '';
|
||||
|
||||
// 🆕 判断用户是否有订单所需的会员(优先后端 hasRequiredMember)
|
||||
// 订单要求指定会员时,核对用户是否持有该会员类型(优先后端字段)
|
||||
let hasRequiredMember = true;
|
||||
if (item.hasRequiredMember !== undefined) {
|
||||
hasRequiredMember = !!item.hasRequiredMember;
|
||||
} else if (item.has_required_member !== undefined) {
|
||||
hasRequiredMember = !!item.has_required_member;
|
||||
} else if (item.yaoqiuleixing == 1 && item.huiyuan_id) {
|
||||
const userHasIt = this.data.huiyuanList.some(h =>
|
||||
(h.huiyuanid == item.huiyuan_id) || (h.huiyuan_id == 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,
|
||||
isMyZhiding,
|
||||
isHighlighted: this.data.highlightOrderId && item.dingdan_id === this.data.highlightOrderId,
|
||||
zhiding_hf: item.zhiding_hf,
|
||||
zhiding_hf_text: item.zhiding_hf_text || ZHIDING_HF_MAP[item.zhiding_hf] || '',
|
||||
zhiding_dhj_sm: item.zhiding_dhj_sm || '',
|
||||
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,
|
||||
};
|
||||
@@ -399,13 +558,16 @@ Page({
|
||||
|
||||
switch (failureType) {
|
||||
case 'huiyuan':
|
||||
params = { needScroll: '0', scrollTo: 'member' };
|
||||
url = '/pages/fighter-recharge/fighter-recharge';
|
||||
params = { scrollTo: 'member' };
|
||||
break;
|
||||
case 'yajin':
|
||||
params = { needScroll: '1', scrollTo: 'bottom', requiredYajin: requiredYajin };
|
||||
url = '/pages/fighter-recharge/fighter-deposit';
|
||||
params = { requiredYajin: requiredYajin };
|
||||
break;
|
||||
case 'jifen':
|
||||
params = { needScroll: '1', scrollTo: 'bottom' };
|
||||
url = '/pages/fighter-recharge/fighter-recharge';
|
||||
params = { scrollTo: 'jifen' };
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -424,11 +586,63 @@ 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;
|
||||
|
||||
const penalty = await checkPenaltyForGrab();
|
||||
if (penalty.blocked) {
|
||||
wx.showToast({
|
||||
title: penalty.msg,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
wx.navigateTo({ url: penalty.url });
|
||||
}, 500);
|
||||
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' });
|
||||
@@ -453,9 +667,7 @@ Page({
|
||||
}
|
||||
// 校验5: 会员要求
|
||||
if (dingdanItem.yaoqiuleixing == 1) {
|
||||
const hasHuiyuan = this.data.huiyuanList.some(h =>
|
||||
(h.huiyuanid == dingdanItem.huiyuan_id) || (h.huiyuan_id == dingdanItem.huiyuan_id)
|
||||
);
|
||||
const hasHuiyuan = userHasHuiyuan(this.data.huiyuanList, dingdanItem.huiyuan_id);
|
||||
if (!hasHuiyuan) {
|
||||
wx.showToast({
|
||||
title: '未开通对应会员,无法抢单',
|
||||
@@ -521,6 +733,8 @@ Page({
|
||||
item => item.dingdan_id !== dingdanItem.dingdan_id
|
||||
);
|
||||
that.setData({ dingdanList: newList });
|
||||
that.persistPageCache();
|
||||
that.loadMyZhidingOrders(true);
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: qiangdanRes.data.msg || '抢单失败',
|
||||
@@ -551,43 +765,134 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
const now = Date.now();
|
||||
const lastTime = this.data.lastRefreshTime;
|
||||
const cooldown = this.data.refreshCooldown;
|
||||
async loadMyZhidingOrders(silent = false) {
|
||||
if (!this.data.uid && !wx.getStorageSync('uid')) return;
|
||||
try {
|
||||
const raw = await fetchMyZhidingOrders();
|
||||
const uid = this.data.uid || wx.getStorageSync('uid');
|
||||
const highlight = this.data.highlightOrderId || this._pendingHighlight || '';
|
||||
const list = filterZhidingBannerList(processZhidingList(raw, app, uid, highlight));
|
||||
this.setData({ myZhidingList: list, myZhidingCount: list.length });
|
||||
if (highlight && list.some((o) => o.dingdan_id === highlight)) {
|
||||
this.setData({ highlightOrderId: highlight });
|
||||
this._pendingHighlight = '';
|
||||
}
|
||||
} catch (e) {
|
||||
if (!silent) console.warn('loadMyZhidingOrders', e);
|
||||
}
|
||||
},
|
||||
|
||||
if (now - lastTime < cooldown) {
|
||||
wx.showToast({
|
||||
title: `操作太快,请${Math.ceil((cooldown - (now - lastTime)) / 1000)}秒后再试`,
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
openZhidingDetail(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item) return;
|
||||
this.setData({ showZhidingDetail: true, zhidingDetailOrder: item });
|
||||
},
|
||||
|
||||
closeZhidingDetail() {
|
||||
this.setData({ showZhidingDetail: false, zhidingDetailOrder: null });
|
||||
},
|
||||
|
||||
async onZhidingReject(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item) return;
|
||||
wx.showModal({
|
||||
title: '确认婉拒',
|
||||
content: '婉拒后订单将退回抢单大厅,确定不想接此单吗?',
|
||||
confirmText: '不想接',
|
||||
confirmColor: '#e64340',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return;
|
||||
wx.showLoading({ title: '提交中...', mask: true });
|
||||
try {
|
||||
const body = await submitZhidingResponse(item.dingdan_id, 2);
|
||||
wx.hideLoading();
|
||||
if (body && (body.code === 200 || body.code === 0)) {
|
||||
wx.showToast({ title: body.msg || '已婉拒', icon: 'none' });
|
||||
this.closeZhidingDetail();
|
||||
await this.loadMyZhidingOrders(true);
|
||||
await this.loadDingdanList(true, true);
|
||||
} else {
|
||||
wx.showToast({ title: body?.msg || '操作失败', icon: 'none' });
|
||||
}
|
||||
} catch (err) {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
onZhidingLater(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item) return;
|
||||
this.setData({
|
||||
showZhidingDetail: false,
|
||||
zhidingDetailOrder: null,
|
||||
showLaterModal: true,
|
||||
laterOrderId: item.dingdan_id,
|
||||
laterNote: item.zhiding_dhj_sm || '',
|
||||
});
|
||||
},
|
||||
|
||||
closeLaterModal() {
|
||||
this.setData({ showLaterModal: false, laterOrderId: '', laterNote: '' });
|
||||
},
|
||||
|
||||
onLaterNoteInput(e) {
|
||||
this.setData({ laterNote: e.detail.value });
|
||||
},
|
||||
|
||||
async confirmZhidingLater() {
|
||||
const { laterOrderId, laterNote } = this.data;
|
||||
if (!laterNote.trim()) {
|
||||
wx.showToast({ title: '请填写预计接待时间', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '提交中...', mask: true });
|
||||
try {
|
||||
const body = await submitZhidingResponse(laterOrderId, 3, laterNote.trim());
|
||||
wx.hideLoading();
|
||||
if (body && (body.code === 200 || body.code === 0)) {
|
||||
const note = laterNote.trim();
|
||||
wx.showToast({ title: `已记录:${note}`, icon: 'none', duration: 3500 });
|
||||
this.closeLaterModal();
|
||||
this.closeZhidingDetail();
|
||||
await this.loadMyZhidingOrders(true);
|
||||
await this.loadDingdanList(true, true);
|
||||
} else {
|
||||
wx.showToast({ title: body?.msg || '操作失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
async onZhidingAccept(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item) return;
|
||||
wx.showLoading({ title: '处理中...', mask: true });
|
||||
try {
|
||||
await submitZhidingResponse(item.dingdan_id, 1);
|
||||
wx.hideLoading();
|
||||
} catch (e) {
|
||||
wx.hideLoading();
|
||||
}
|
||||
const processed = this.processDingdanItem(item);
|
||||
this.onQiangdanTap({ currentTarget: { dataset: { item: processed } } });
|
||||
},
|
||||
|
||||
noop() {},
|
||||
|
||||
onReachBottom() {
|
||||
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;
|
||||
@@ -595,15 +900,26 @@ Page({
|
||||
|
||||
this.setData({
|
||||
scrollViewRefreshing: true,
|
||||
lastRefreshTime: now,
|
||||
lastRefreshTime: Date.now(),
|
||||
page: 1,
|
||||
hasMore: true
|
||||
hasMore: true,
|
||||
});
|
||||
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
this.refreshDashouProfileSilent();
|
||||
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);
|
||||
}
|
||||
await this.loadMyZhidingOrders(true);
|
||||
this.loadGlobalStatus();
|
||||
this.persistPageCache(banner);
|
||||
} catch (e) {
|
||||
console.error('下拉刷新失败:', e);
|
||||
} finally {
|
||||
this.setData({ scrollViewRefreshing: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"navigationBarTitleText": "抢单大厅",
|
||||
"navigationBarBackgroundColor": "#c4b5fd",
|
||||
"navigationBarTitleText": "抢单列表",
|
||||
"navigationBarBackgroundColor": "#f7dc51",
|
||||
"backgroundColorTop": "#f7dc51",
|
||||
"navigationBarTextStyle": "black",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "dark",
|
||||
"backgroundColor": "#f5f3ff",
|
||||
"backgroundColor": "#fff8e1",
|
||||
"usingComponents": {
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag",
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"kefu-float": "/components/kefu-float/kefu-float"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
refresher-default-style="black"
|
||||
|
||||
refresher-background="#f5f3ff"
|
||||
refresher-background="#fff8e1"
|
||||
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
|
||||
@@ -38,6 +38,38 @@
|
||||
|
||||
|
||||
|
||||
<!-- 公告置顶 -->
|
||||
<view class="xym-gonggao-bar" wx:if="{{gonggao}}">
|
||||
<image class="xym-gonggao-ico" src="https://bintao.xmxym88.com/xcx/bintao/38.png" mode="aspectFit"/>
|
||||
<text class="xym-gonggao-txt">{{gonggao}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 被指定订单提醒 -->
|
||||
<view class="my-zhiding-banner" wx:if="{{myZhidingList.length > 0}}">
|
||||
<view class="my-zhiding-banner-head">
|
||||
<view class="my-zhiding-head-left">
|
||||
<text class="my-zhiding-badge">指定给您</text>
|
||||
<text class="my-zhiding-count-num">{{myZhidingCount || myZhidingList.length}}单</text>
|
||||
</view>
|
||||
<text class="my-zhiding-sub">请确认是否接待</text>
|
||||
</view>
|
||||
<view wx:for="{{myZhidingList}}" wx:key="dingdan_id" class="my-zhiding-card {{item.isHighlighted ? 'my-zhiding-card--highlight' : ''}}">
|
||||
<view class="my-zhiding-card-main" data-item="{{item}}" bindtap="openZhidingDetail">
|
||||
<view class="my-zhiding-card-top">
|
||||
<text class="my-zhiding-order-id">单号 {{item.dingdan_id}}</text>
|
||||
<text class="my-zhiding-reward">¥{{item.dashou_fencheng || 0}}</text>
|
||||
</view>
|
||||
<text class="my-zhiding-desc line2">{{item.jieshao || '暂无介绍'}}</text>
|
||||
<text class="my-zhiding-status" wx:if="{{item.zhiding_hf_text}}">您的回复:{{item.zhiding_hf_text}}{{item.zhiding_dhj_sm ? ' · ' + item.zhiding_dhj_sm : ''}}</text>
|
||||
</view>
|
||||
<view class="my-zhiding-actions">
|
||||
<view class="zhiding-act zhiding-act--ghost" data-item="{{item}}" catchtap="onZhidingReject">不想接</view>
|
||||
<view class="zhiding-act zhiding-act--ghost" data-item="{{item}}" catchtap="onZhidingLater">等会接</view>
|
||||
<view class="zhiding-act zhiding-act--primary" data-item="{{item}}" catchtap="onZhidingAccept">立即接待</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品轮播(只读 globalData,与点单端同源) -->
|
||||
|
||||
<view class="xym-lunbo-container" wx:if="{{lunboList.length > 0}}">
|
||||
@@ -50,7 +82,7 @@
|
||||
|
||||
indicator-color="rgba(255,255,255,0.6)"
|
||||
|
||||
indicator-active-color="#9333ea"
|
||||
indicator-active-color="#ffd061"
|
||||
|
||||
autoplay="{{lunboList.length > 1}}"
|
||||
|
||||
@@ -74,11 +106,11 @@
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
<view class="xym-section-gap" wx:if="{{lunboList.length > 0}}"></view>
|
||||
|
||||
<!-- 商品类型选择 -->
|
||||
|
||||
<view class="leixing-quyu">
|
||||
<view class="leixing-quyu dingdanxiang1 filter-card mx20">
|
||||
|
||||
<scroll-view class="leixing-scroll" scroll-x enhanced show-scrollbar="{{false}}">
|
||||
|
||||
@@ -176,7 +208,7 @@
|
||||
|
||||
<!-- 平台订单(逍遥梦卡片布局) -->
|
||||
|
||||
<view wx:if="{{item.isPingtai}}" class="xym-order-item xym-pingtai-order" data-item="{{item}}">
|
||||
<view wx:if="{{item.isPingtai}}" class="xym-order-item xym-pingtai-order {{item.isMyZhiding ? 'order-my-zhiding' : ''}} {{item.isHighlighted ? 'order-zhiding-highlight' : ''}}" data-item="{{item}}">
|
||||
|
||||
<view class="xym-pingtai-head flexb">
|
||||
|
||||
@@ -190,16 +222,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 +234,20 @@
|
||||
|
||||
</view>
|
||||
|
||||
<block wx:if="{{item.isZhiding}}">
|
||||
<view class="zhiding-strip light">
|
||||
<view class="zhiding-strip-top myflex">
|
||||
<text class="zhiding-label">指定</text>
|
||||
<image class="zhiding-avatar" src="{{item.zhiding_avatar_full || defaultAvatarUrl}}" 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 +262,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 +272,10 @@
|
||||
|
||||
<text class="xuqiu-label">需求标签</text>
|
||||
|
||||
<scroll-view class="biaoqian-inline-scroll" scroll-x>
|
||||
|
||||
<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>
|
||||
@@ -248,7 +284,7 @@
|
||||
|
||||
<text class="o-amt light">平台派单</text>
|
||||
|
||||
<view class="qiangdan flexa btn-bg btn-qiang btn-qiang-brown" data-item="{{item}}" bindtap="onQiangdanTap">抢单</view>
|
||||
<view class="qiangdan flexa btn-bg btn-qiang btn-qiang-brown" data-item="{{item}}" bindtap="onQiangdanTap">{{item.isMyZhiding ? '立即接待' : '抢单'}}</view>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -260,9 +296,84 @@
|
||||
|
||||
|
||||
|
||||
<!-- 优质商家订单(金牌卡片) -->
|
||||
<view wx:elif="{{item.isShangjia && item.shangjia_youzhi}}" class="xym-order-item gold-card {{item.isMyZhiding ? 'order-my-zhiding' : ''}} {{item.isHighlighted ? 'order-zhiding-highlight' : ''}}" 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 class="zhiding-avatar" src="{{item.zhiding_avatar_full || defaultAvatarUrl}}" 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">{{item.isMyZhiding ? '立即接待' : '抢单'}}</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}}">
|
||||
<view wx:elif="{{item.isShangjia}}" class="xym-order-item xym-shangjia-order {{item.isMyZhiding ? 'order-my-zhiding' : ''}} {{item.isHighlighted ? 'order-zhiding-highlight' : ''}}" data-item="{{item}}">
|
||||
|
||||
<view class="xym-shangjia-head flexb">
|
||||
|
||||
@@ -276,19 +387,23 @@
|
||||
|
||||
<view class="order-con">
|
||||
|
||||
<view wx:if="{{item.isZhiding}}" class="zhiding-strip myflex">
|
||||
|
||||
<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"/>
|
||||
|
||||
<image class="zhiding-avatar" src="{{item.zhiding_avatar_full || defaultAvatarUrl}}" 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 +433,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>
|
||||
<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">
|
||||
|
||||
<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>
|
||||
@@ -344,7 +468,7 @@
|
||||
|
||||
<text class="o-amt">商家派单</text>
|
||||
|
||||
<view class="qiangdan flexa btn-bg btn-qiang" data-item="{{item}}" bindtap="onQiangdanTap">抢单</view>
|
||||
<view class="qiangdan flexa btn-bg btn-qiang" data-item="{{item}}" bindtap="onQiangdanTap">{{item.isMyZhiding ? '立即接待' : '抢单'}}</view>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -384,8 +508,58 @@
|
||||
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
<kefu-float bottom="200rpx" />
|
||||
|
||||
<popup-notice id="popupNotice" />
|
||||
|
||||
<!-- 指定单详情 -->
|
||||
<view class="zhiding-modal-mask" wx:if="{{showZhidingDetail}}" catchtap="closeZhidingDetail">
|
||||
<view class="zhiding-modal" catchtap="noop">
|
||||
<view class="zhiding-modal-head">
|
||||
<text class="zhiding-modal-title">指定订单详情</text>
|
||||
<text class="zhiding-modal-close" catchtap="closeZhidingDetail">×</text>
|
||||
</view>
|
||||
<view class="zhiding-modal-body" wx:if="{{zhidingDetailOrder}}">
|
||||
<view class="zhiding-modal-row"><text class="lbl">单号</text><text>{{zhidingDetailOrder.dingdan_id}}</text></view>
|
||||
<view class="zhiding-modal-row"><text class="lbl">分成</text><text class="reward">¥{{zhidingDetailOrder.dashou_fencheng || 0}}</text></view>
|
||||
<view class="zhiding-modal-row col"><text class="lbl">介绍</text><text>{{zhidingDetailOrder.jieshao || '暂无'}}</text></view>
|
||||
<view class="zhiding-modal-row col" wx:if="{{zhidingDetailOrder.beizhu}}"><text class="lbl">备注</text><text>{{zhidingDetailOrder.beizhu}}</text></view>
|
||||
<view class="zhiding-modal-row" wx:if="{{zhidingDetailOrder.zhiding_hf_text}}"><text class="lbl">您的回复</text><text>{{zhidingDetailOrder.zhiding_hf_text}}</text></view>
|
||||
</view>
|
||||
<view class="my-zhiding-actions zhiding-modal-actions" wx:if="{{zhidingDetailOrder}}">
|
||||
<view class="zhiding-act zhiding-act--ghost" data-item="{{zhidingDetailOrder}}" catchtap="onZhidingReject">不想接</view>
|
||||
<view class="zhiding-act zhiding-act--ghost" data-item="{{zhidingDetailOrder}}" catchtap="onZhidingLater">等会接</view>
|
||||
<view class="zhiding-act zhiding-act--primary" data-item="{{zhidingDetailOrder}}" catchtap="onZhidingAccept">立即接待</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 等会接时间 -->
|
||||
<view class="zhiding-modal-mask zhiding-modal-mask--top" wx:if="{{showLaterModal}}" catchtap="closeLaterModal">
|
||||
<view class="zhiding-modal zhiding-modal--sm" catchtap="noop">
|
||||
<view class="zhiding-modal-head">
|
||||
<text class="zhiding-modal-title">等会接</text>
|
||||
<text class="zhiding-modal-close" catchtap="closeLaterModal">×</text>
|
||||
</view>
|
||||
<view class="zhiding-modal-body">
|
||||
<text class="later-hint">请填写预计接待时间,例如「30分钟后」「今晚8点」</text>
|
||||
<input
|
||||
class="later-input"
|
||||
placeholder="预计接待时间"
|
||||
placeholder-class="later-input-ph"
|
||||
value="{{laterNote}}"
|
||||
bindinput="onLaterNoteInput"
|
||||
maxlength="50"
|
||||
focus="{{showLaterModal}}"
|
||||
adjust-position="{{true}}"
|
||||
confirm-type="done"
|
||||
bindconfirm="confirmZhidingLater"
|
||||
/>
|
||||
</view>
|
||||
<view class="later-confirm" catchtap="confirmZhidingLater">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<tab-bar />
|
||||
|
||||
</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 {
|
||||
@@ -486,7 +493,7 @@
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 1rpx solid rgba(196, 181, 253, 0.3);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.3);
|
||||
}
|
||||
|
||||
/* 分佣图标占位符,用户可替换为自己图标 */
|
||||
@@ -504,10 +511,10 @@
|
||||
|
||||
.fenyong-price {
|
||||
font-size: 44rpx;
|
||||
color: #c4b5fd;
|
||||
color: #ffd700;
|
||||
font-weight: 800;
|
||||
margin-right: 4rpx;
|
||||
text-shadow: 0 0 20px rgba(196, 181, 253, 0.8);
|
||||
text-shadow: 0 0 20px rgba(255, 215, 0, 0.8);
|
||||
}
|
||||
|
||||
.fenyong-unit {
|
||||
@@ -764,4 +771,179 @@
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
@import '../../styles/dashou-xym-order-card.wxss';
|
||||
/* 被指定订单(星阙功能保留,不影响星之界卡片 UI) */
|
||||
.my-zhiding-banner {
|
||||
margin: 16rpx 24rpx 0;
|
||||
padding: 22rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(135deg, #ff9500 0%, #ff6b00 100%);
|
||||
border: 3rpx solid #ff4500;
|
||||
box-shadow: 0 12rpx 32rpx rgba(255, 69, 0, 0.45);
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
}
|
||||
.my-zhiding-banner-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.my-zhiding-head-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
.my-zhiding-badge {
|
||||
font-size: 26rpx;
|
||||
font-weight: 800;
|
||||
color: #ff4500;
|
||||
background: #fff;
|
||||
padding: 8rpx 18rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
.my-zhiding-count-num {
|
||||
font-size: 32rpx;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
}
|
||||
.my-zhiding-sub {
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
.my-zhiding-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 18rpx;
|
||||
margin-bottom: 12rpx;
|
||||
border: 2rpx solid #ffe0b2;
|
||||
}
|
||||
.my-zhiding-card--highlight {
|
||||
border-color: #ff8c00;
|
||||
box-shadow: 0 0 0 4rpx rgba(255, 140, 0, 0.15);
|
||||
}
|
||||
.my-zhiding-card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.my-zhiding-order-id {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
.my-zhiding-reward {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #e64340;
|
||||
}
|
||||
.my-zhiding-desc {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.my-zhiding-status {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #059669;
|
||||
}
|
||||
.my-zhiding-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
.zhiding-act {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 14rpx 0;
|
||||
border-radius: 999rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.zhiding-act--ghost {
|
||||
background: #f3f4f6;
|
||||
color: #666;
|
||||
}
|
||||
.zhiding-act--primary {
|
||||
background: linear-gradient(90deg, #ff8c00, #ff6b00);
|
||||
color: #fff;
|
||||
}
|
||||
.order-my-zhiding {
|
||||
border: 3rpx solid #ff4500 !important;
|
||||
box-shadow: 0 0 0 6rpx rgba(255, 69, 0, 0.25), 0 12rpx 36rpx rgba(255, 69, 0, 0.35) !important;
|
||||
}
|
||||
.order-zhiding-highlight {
|
||||
animation: zhidingPulse 1.5s ease-in-out 2;
|
||||
}
|
||||
@keyframes zhidingPulse {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 140, 0, 0.3); }
|
||||
50% { box-shadow: 0 0 0 12rpx rgba(255, 140, 0, 0); }
|
||||
}
|
||||
.zhiding-modal-mask {
|
||||
position: fixed;
|
||||
left: 0; right: 0; top: 0; bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.62);
|
||||
z-index: 10050;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
}
|
||||
.zhiding-modal-mask--top {
|
||||
z-index: 10060;
|
||||
}
|
||||
.zhiding-modal {
|
||||
width: 100%;
|
||||
max-width: 640rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 24rpx 64rpx rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
.zhiding-modal--sm { max-width: 580rpx; }
|
||||
.zhiding-modal-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 28rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
background: #fff;
|
||||
}
|
||||
.zhiding-modal-title { font-size: 32rpx; font-weight: 700; color: #222; }
|
||||
.zhiding-modal-close { font-size: 44rpx; color: #666; line-height: 1; padding: 0 8rpx; }
|
||||
.zhiding-modal-body { padding: 24rpx 28rpx; background: #fff; }
|
||||
.zhiding-modal-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.zhiding-modal-row.col { flex-direction: column; gap: 8rpx; }
|
||||
.zhiding-modal-row .lbl { color: #999; font-size: 24rpx; }
|
||||
.zhiding-modal-row .reward { color: #e64340; font-weight: 700; }
|
||||
.zhiding-modal-actions { padding: 0 28rpx 28rpx; margin-top: 0; background: #fff; }
|
||||
.later-hint { font-size: 26rpx; color: #444; display: block; margin-bottom: 16rpx; }
|
||||
.later-input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 12rpx;
|
||||
padding: 22rpx 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
background: #f9fafb;
|
||||
min-height: 88rpx;
|
||||
}
|
||||
.later-input-ph { color: #aaa; }
|
||||
.later-confirm {
|
||||
margin: 0 28rpx 28rpx;
|
||||
text-align: center;
|
||||
padding: 24rpx;
|
||||
background: linear-gradient(90deg, #ff8c00, #ff4500);
|
||||
color: #fff;
|
||||
border-radius: 999rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ page {
|
||||
border-radius: 20rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.status-0 { background: #F3E8FF; color: #9333ea; }
|
||||
.status-0 { background: #FFF3E0; color: #EF6C00; }
|
||||
.status-1 { background: #E8F5E9; color: #2E7D32; }
|
||||
.status-2 { background: #FFEBEE; color: #C62828; }
|
||||
.status-3 { background: #E3F2FD; color: #1565C0; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,13 +60,13 @@
|
||||
|
||||
.search-btn {
|
||||
margin-left: 20rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(147, 51, 234, 0.3);
|
||||
box-shadow: 0 6rpx 20rpx rgba(255, 170, 0, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.search-btn:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 4rpx 15rpx rgba(147, 51, 234, 0.4);
|
||||
box-shadow: 0 4rpx 15rpx rgba(255, 170, 0, 0.4);
|
||||
}
|
||||
|
||||
.search-btn-text {
|
||||
@@ -127,7 +127,7 @@
|
||||
transform: translate(-50%, -50%);
|
||||
width: 300rpx;
|
||||
height: 300rpx;
|
||||
background: radial-gradient(circle, rgba(168, 85, 247, 0.1) 0%, transparent 70%);
|
||||
background: radial-gradient(circle, rgba(255, 204, 0, 0.1) 0%, transparent 70%);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@@ -167,8 +167,8 @@
|
||||
.leixing-active .leixing-image-box {
|
||||
transform: scale(1.12);
|
||||
box-shadow:
|
||||
0 18rpx 40rpx rgba(168, 85, 247, 0.3),
|
||||
0 8rpx 16rpx rgba(147, 51, 234, 0.2);
|
||||
0 18rpx 40rpx rgba(255, 204, 0, 0.3),
|
||||
0 8rpx 16rpx rgba(255, 170, 0, 0.2);
|
||||
}
|
||||
|
||||
.leixing-image {
|
||||
@@ -185,7 +185,7 @@
|
||||
right: -3rpx;
|
||||
bottom: -3rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #c4b5fd, #9333ea, #7c3aed);
|
||||
background: linear-gradient(135deg, #ffcc00, #ffaa00, #ff8800);
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
z-index: -1;
|
||||
@@ -223,10 +223,10 @@
|
||||
}
|
||||
|
||||
.leixing-active .leixing-text {
|
||||
color: #8b5cf6;
|
||||
color: #ff9900;
|
||||
font-weight: 600;
|
||||
transform: translateY(-2rpx);
|
||||
text-shadow: 0 2rpx 4rpx rgba(168, 85, 247, 0.2);
|
||||
text-shadow: 0 2rpx 4rpx rgba(255, 153, 0, 0.2);
|
||||
}
|
||||
|
||||
.leixing-active-bar {
|
||||
@@ -234,12 +234,12 @@
|
||||
bottom: -4rpx;
|
||||
width: 44rpx;
|
||||
height: 4rpx;
|
||||
background: linear-gradient(90deg, #c4b5fd, #9333ea);
|
||||
background: linear-gradient(90deg, #ffcc00, #ffaa00);
|
||||
border-radius: 2rpx;
|
||||
opacity: 0;
|
||||
transform: scaleX(0);
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
box-shadow: 0 2rpx 8rpx rgba(147, 51, 234, 0.4);
|
||||
box-shadow: 0 2rpx 8rpx rgba(255, 170, 0, 0.4);
|
||||
}
|
||||
|
||||
.leixing-active-bar-show {
|
||||
@@ -261,7 +261,7 @@
|
||||
|
||||
.border-effect-active {
|
||||
opacity: 1;
|
||||
border-color: rgba(168, 85, 247, 0.5);
|
||||
border-color: rgba(255, 204, 0, 0.5);
|
||||
animation: borderGlow 2s infinite;
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@
|
||||
}
|
||||
|
||||
.zhuanqu-item:active {
|
||||
background: rgba(168, 85, 247, 0.05);
|
||||
background: rgba(255, 204, 0, 0.05);
|
||||
}
|
||||
|
||||
.zhuanqu-item-content {
|
||||
@@ -320,9 +320,9 @@
|
||||
}
|
||||
|
||||
.zhuanqu-active .zhuanqu-name {
|
||||
color: #8b5cf6;
|
||||
color: #ff9900;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 2rpx 4rpx rgba(168, 85, 247, 0.2);
|
||||
text-shadow: 0 2rpx 4rpx rgba(255, 153, 0, 0.2);
|
||||
}
|
||||
|
||||
.zhuanqu-count {
|
||||
@@ -332,7 +332,7 @@
|
||||
}
|
||||
|
||||
.zhuanqu-active .zhuanqu-count {
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@
|
||||
transform: translateY(-50%);
|
||||
width: 6rpx;
|
||||
height: 44rpx;
|
||||
background: linear-gradient(180deg, #a855f7, #9333ea);
|
||||
background: linear-gradient(180deg, #ffcc00, #ffaa00);
|
||||
border-radius: 3rpx 0 0 3rpx;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
@@ -353,7 +353,7 @@
|
||||
.zhuanqu-indicator-glow {
|
||||
opacity: 1;
|
||||
animation: indicatorGlow 2s infinite;
|
||||
box-shadow: 0 0 20rpx rgba(168, 85, 247, 0.5);
|
||||
box-shadow: 0 0 20rpx rgba(255, 204, 0, 0.5);
|
||||
}
|
||||
|
||||
.indicator-sparkle {
|
||||
@@ -372,7 +372,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(168, 85, 247, 0.05), transparent);
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 204, 0, 0.05), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
@@ -389,7 +389,7 @@
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
background: rgba(255, 204, 0, 0.1);
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
@@ -463,7 +463,7 @@
|
||||
right: -4rpx;
|
||||
bottom: -4rpx;
|
||||
border-radius: 18rpx;
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.2), transparent);
|
||||
background: linear-gradient(135deg, rgba(255, 204, 0, 0.2), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
@@ -479,7 +479,7 @@
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: linear-gradient(135deg, #a855f7, transparent 70%);
|
||||
background: linear-gradient(135deg, #ffcc00, transparent 70%);
|
||||
border-radius: 0 14rpx 0 0;
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2rpx;
|
||||
background: linear-gradient(90deg, #c4b5fd, #9333ea);
|
||||
background: linear-gradient(90deg, #ffcc00, #ffaa00);
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
@@ -537,20 +537,20 @@
|
||||
|
||||
.price-icon {
|
||||
font-size: 24rpx;
|
||||
color: #9333ea;
|
||||
color: #ff6600;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.price-integer {
|
||||
font-size: 36rpx; /* 适当调整大小 */
|
||||
color: #9333ea;
|
||||
color: #ff6600;
|
||||
font-weight: 600;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
|
||||
.price-decimal {
|
||||
font-size: 24rpx;
|
||||
color: #9333ea;
|
||||
color: #ff6600;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -573,11 +573,11 @@
|
||||
.detail-btn {
|
||||
position: relative;
|
||||
padding: 12rpx 28rpx;
|
||||
background: linear-gradient(135deg, #c4b5fd, #9333ea);
|
||||
background: linear-gradient(135deg, #ffcc00, #ffaa00);
|
||||
border-radius: 40rpx;
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 6rpx 20rpx rgba(147, 51, 234, 0.2);
|
||||
box-shadow: 0 6rpx 20rpx rgba(255, 170, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -586,7 +586,7 @@
|
||||
|
||||
.detail-btn:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 4rpx 15rpx rgba(147, 51, 234, 0.4);
|
||||
box-shadow: 0 4rpx 15rpx rgba(255, 170, 0, 0.4);
|
||||
}
|
||||
|
||||
.detail-btn-text {
|
||||
@@ -659,7 +659,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, transparent, rgba(168, 85, 247, 0.03), transparent);
|
||||
background: linear-gradient(135deg, transparent, rgba(255, 204, 0, 0.03), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
@@ -676,7 +676,7 @@
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
background: rgba(255, 204, 0, 0.1);
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
@@ -707,19 +707,19 @@
|
||||
}
|
||||
|
||||
.shangpin-empty-btn {
|
||||
background: linear-gradient(135deg, #c4b5fd, #9333ea);
|
||||
background: linear-gradient(135deg, #ffcc00, #ffaa00);
|
||||
color: white;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
padding: 16rpx 44rpx;
|
||||
border-radius: 40rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(147, 51, 234, 0.3);
|
||||
box-shadow: 0 8rpx 24rpx rgba(255, 170, 0, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.shangpin-empty-btn:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 4rpx 16rpx rgba(147, 51, 234, 0.4);
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 170, 0, 0.4);
|
||||
}
|
||||
|
||||
.load-more-line {
|
||||
@@ -859,7 +859,7 @@
|
||||
right: -4rpx;
|
||||
bottom: -4rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.2), transparent);
|
||||
background: linear-gradient(135deg, rgba(255, 204, 0, 0.2), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
@@ -896,7 +896,7 @@
|
||||
|
||||
.info-tag {
|
||||
padding: 6rpx 12rpx;
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
background: rgba(255, 204, 0, 0.1);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
@@ -921,7 +921,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, transparent, rgba(168, 85, 247, 0.05), transparent);
|
||||
background: linear-gradient(135deg, transparent, rgba(255, 204, 0, 0.05), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
@@ -977,7 +977,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 4rpx solid transparent;
|
||||
border-top-color: #a855f7;
|
||||
border-top-color: #ffcc00;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@@ -1028,7 +1028,7 @@
|
||||
left: 50%;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: #a855f7;
|
||||
background: #ffcc00;
|
||||
border-radius: 50%;
|
||||
transform: translateX(-50%);
|
||||
animation: spin 1s linear infinite;
|
||||
@@ -1045,7 +1045,7 @@
|
||||
left: 50%;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
background: radial-gradient(circle, rgba(168, 85, 247, 0.2) 0%, transparent 70%);
|
||||
background: radial-gradient(circle, rgba(255, 204, 0, 0.2) 0%, transparent 70%);
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: -1;
|
||||
}
|
||||
@@ -1076,19 +1076,19 @@
|
||||
|
||||
@keyframes borderGlow {
|
||||
0%, 100% {
|
||||
border-color: rgba(168, 85, 247, 0.3);
|
||||
border-color: rgba(255, 204, 0, 0.3);
|
||||
}
|
||||
50% {
|
||||
border-color: rgba(168, 85, 247, 0.8);
|
||||
border-color: rgba(255, 204, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes indicatorGlow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 20rpx rgba(168, 85, 247, 0.3);
|
||||
box-shadow: 0 0 20rpx rgba(255, 204, 0, 0.3);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 30rpx rgba(168, 85, 247, 0.8);
|
||||
box-shadow: 0 0 30rpx rgba(255, 204, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
const app = getApp();
|
||||
import { formatDate } from '../../static/lib/utils';
|
||||
import { resolveAvatarUrl, getDefaultAvatarUrl } from '../../utils/avatar.js';
|
||||
import { resolveAvatarUrl, getDefaultAvatarUrl, resolveAvatarFromStorage, subscribeConfigAvatarRefresh } from '../../utils/avatar.js';
|
||||
import { getLocalImUserId } from '../../utils/im-user.js';
|
||||
import { recordConversationOpen } from '../../utils/group-chat.js';
|
||||
import {
|
||||
fetchHistoryMessages,
|
||||
getOldestHistoryTimestamp,
|
||||
mergeHistoryMessages,
|
||||
waitChatImReady,
|
||||
} from '../../utils/chat-history.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -25,29 +32,58 @@ Page({
|
||||
pendingImage: '',
|
||||
pendingImageFile: null,
|
||||
keyboardHeight: 0,
|
||||
bottomSafeHeight: 10
|
||||
bottomSafeHeight: 140,
|
||||
defaultAvatarUrl: '',
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
if (options.data) {
|
||||
try {
|
||||
const p = JSON.parse(decodeURIComponent(options.data));
|
||||
this.setData({
|
||||
toUserId: p.toUserId || '',
|
||||
toName: p.toName || '聊天',
|
||||
toAvatar: resolveAvatarUrl(p.toAvatar, app),
|
||||
toAvatar: resolveAvatarUrl(p.toAvatar, app) || defAvatar,
|
||||
defaultAvatarUrl: defAvatar,
|
||||
});
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
this.setData({ defaultAvatarUrl: defAvatar });
|
||||
}
|
||||
} else {
|
||||
this.setData({ defaultAvatarUrl: defAvatar });
|
||||
}
|
||||
wx.setNavigationBarTitle({ title: this.data.toName });
|
||||
this.initCurrentUser();
|
||||
this._unsubConfigAvatar = subscribeConfigAvatarRefresh(this, () => {
|
||||
const def = getDefaultAvatarUrl(app);
|
||||
this.setData({
|
||||
defaultAvatarUrl: def,
|
||||
toAvatar: resolveAvatarUrl(this.data.toAvatar, app) || def,
|
||||
});
|
||||
this.refreshCurrentUserAvatar();
|
||||
});
|
||||
},
|
||||
|
||||
onShow() {
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
this.setData({
|
||||
defaultAvatarUrl: defAvatar,
|
||||
toAvatar: resolveAvatarUrl(this.data.toAvatar, app) || defAvatar,
|
||||
});
|
||||
this.refreshCurrentUserAvatar();
|
||||
if (this.data.toUserId) {
|
||||
if (!app.globalData.pageState) app.globalData.pageState = {};
|
||||
app.globalData.pageState.lastOpenedConvId = this.data.toUserId;
|
||||
recordConversationOpen({ userId: this.data.toUserId });
|
||||
}
|
||||
this.ensureChatReady();
|
||||
},
|
||||
|
||||
onHide() {
|
||||
if (this.data.toUserId) {
|
||||
recordConversationOpen({ userId: this.data.toUserId });
|
||||
}
|
||||
this.clearPageListeners();
|
||||
this.markPrivateMessageAsRead();
|
||||
setTimeout(() => {
|
||||
@@ -55,27 +91,34 @@ Page({
|
||||
}, 250);
|
||||
},
|
||||
|
||||
ensureChatReady() {
|
||||
onUnload() {
|
||||
if (this._unsubConfigAvatar) {
|
||||
this._unsubConfigAvatar();
|
||||
this._unsubConfigAvatar = null;
|
||||
}
|
||||
},
|
||||
|
||||
async ensureChatReady() {
|
||||
const targetUserId = getLocalImUserId(app);
|
||||
if (!targetUserId) return;
|
||||
|
||||
const ready = () => {
|
||||
const ready = async () => {
|
||||
this.setupAllListeners();
|
||||
this.loadHistory(true);
|
||||
await this.loadHistory(true);
|
||||
};
|
||||
|
||||
if (app.ensureConnection) {
|
||||
const ret = app.ensureConnection();
|
||||
if (ret && typeof ret.then === 'function') {
|
||||
ret.then(ready).catch(ready);
|
||||
} else {
|
||||
setTimeout(ready, 300);
|
||||
}
|
||||
try {
|
||||
const ok = await waitChatImReady(app, 12000);
|
||||
if (ok) {
|
||||
await ready();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[私聊] IM 连接失败', e);
|
||||
}
|
||||
|
||||
if (this.isConnected() && wx.goEasy.im?.userId === targetUserId) {
|
||||
ready();
|
||||
await ready();
|
||||
return;
|
||||
}
|
||||
this.connectGoEasy(targetUserId);
|
||||
@@ -133,14 +176,26 @@ Page({
|
||||
const imId = getLocalImUserId(app);
|
||||
const uid = wx.getStorageSync('uid');
|
||||
const nick = app.globalData.nicheng || app.globalData.dashouNicheng || wx.getStorageSync('nicheng') || '';
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const avatar = resolveAvatarFromStorage(app) || defAvatar;
|
||||
this.setData({
|
||||
currentUser: {
|
||||
id: imId || ('Ds' + uid),
|
||||
name: nick || `用户${uid}`,
|
||||
avatar: resolveAvatarUrl(wx.getStorageSync('touxiang'), app),
|
||||
avatar,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
refreshCurrentUserAvatar() {
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const cu = this.data.currentUser;
|
||||
if (!cu) return;
|
||||
const avatar = resolveAvatarUrl(cu.avatar || wx.getStorageSync('touxiang'), app) || defAvatar;
|
||||
if (avatar !== cu.avatar) {
|
||||
this.setData({ currentUser: { ...cu, avatar } });
|
||||
}
|
||||
},
|
||||
fixAvatar(url) {
|
||||
return resolveAvatarUrl(url, app);
|
||||
},
|
||||
@@ -156,6 +211,19 @@ Page({
|
||||
normalizeMessage(msg) {
|
||||
if (!msg) return msg;
|
||||
if (!msg.payload) msg.payload = {};
|
||||
const defAvatar = this.data.defaultAvatarUrl || getDefaultAvatarUrl(app);
|
||||
const myId = this.data.currentUser?.id;
|
||||
if (myId && msg.senderId === myId) {
|
||||
msg.senderData = {
|
||||
name: this.data.currentUser.name,
|
||||
avatar: resolveAvatarUrl(this.data.currentUser.avatar, app) || defAvatar,
|
||||
};
|
||||
} else {
|
||||
msg.senderData = {
|
||||
name: this.data.toName,
|
||||
avatar: resolveAvatarUrl(this.data.toAvatar, app) || defAvatar,
|
||||
};
|
||||
}
|
||||
if (msg.type === 'image') {
|
||||
const p = msg.payload;
|
||||
if (!p.url) p.url = p.thumbnail || p.thumb || p.imageUrl || '';
|
||||
@@ -188,28 +256,25 @@ Page({
|
||||
|
||||
onPullDownRefresh() {
|
||||
if (this.data.loadingHistory) return;
|
||||
if (!this.data.hasMore) {
|
||||
wx.showToast({ title: '没有更早的消息了', icon: 'none' });
|
||||
this.setData({ isRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
return;
|
||||
}
|
||||
this.setData({ isRefreshing: true });
|
||||
this.loadHistory(false).finally(() => { this.setData({ isRefreshing: false }); wx.stopPullDownRefresh(); });
|
||||
this.loadHistory(false).finally(() => {
|
||||
this.setData({ isRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
loadHistory(refresh) {
|
||||
if (!refresh && !this.data.hasMore) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
this.setData({ loadingHistory: true });
|
||||
const { toUserId, lastTimestamp } = this.data;
|
||||
const ts = refresh ? null : lastTimestamp;
|
||||
wx.goEasy.im.history({
|
||||
type: wx.GoEasy.IM_SCENE.PRIVATE, id: toUserId, lastTimestamp: ts, limit: 20,
|
||||
onSuccess: (res) => {
|
||||
let list = res.content || [];
|
||||
list.sort((a, b) => a.timestamp - b.timestamp);
|
||||
list.forEach((m, idx) => {
|
||||
mapHistoryMessage(m, idx, list) {
|
||||
m = this.normalizeMessage(m);
|
||||
list[idx] = m;
|
||||
m.formattedTime = formatDate(m.timestamp);
|
||||
if (idx === 0) m.showTime = true;
|
||||
else m.showTime = (m.timestamp - list[idx-1].timestamp) / 60000 > 5;
|
||||
if (m.senderId === this.data.currentUser.id) {
|
||||
else m.showTime = (m.timestamp - list[idx - 1].timestamp) / 60000 > 5;
|
||||
if (m.senderId === this.data.currentUser?.id) {
|
||||
m.senderData = {
|
||||
name: this.data.currentUser.name,
|
||||
avatar: resolveAvatarUrl(this.data.currentUser.avatar, app),
|
||||
@@ -220,31 +285,62 @@ Page({
|
||||
avatar: resolveAvatarUrl(this.data.toAvatar, app),
|
||||
};
|
||||
}
|
||||
return m;
|
||||
},
|
||||
|
||||
async loadHistory(refresh) {
|
||||
if (!refresh && !this.data.hasMore) return;
|
||||
if (this.data.loadingHistory) return;
|
||||
if (!this.data.toUserId) return;
|
||||
|
||||
this.setData({ loadingHistory: true });
|
||||
try {
|
||||
const ready = await waitChatImReady(app, 12000);
|
||||
if (!ready) {
|
||||
wx.showToast({ title: '消息服务未连接,请稍后重试', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const ts = refresh
|
||||
? null
|
||||
: getOldestHistoryTimestamp(this.data.messages, this.data.lastTimestamp);
|
||||
if (!refresh && ts == null) {
|
||||
this.setData({ hasMore: false });
|
||||
wx.showToast({ title: '没有更早的消息了', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const raw = await fetchHistoryMessages({
|
||||
scene: wx.GoEasy.IM_SCENE.PRIVATE,
|
||||
id: this.data.toUserId,
|
||||
lastTimestamp: ts,
|
||||
limit: 20,
|
||||
});
|
||||
let final = refresh ? list : [...list, ...this.data.messages];
|
||||
if (!refresh) {
|
||||
for (let i=0; i<final.length; i++) {
|
||||
if (i===0) final[i].showTime = true;
|
||||
else final[i].showTime = (final[i].timestamp - final[i-1].timestamp) / 60000 > 5;
|
||||
}
|
||||
}
|
||||
const ids = new Set();
|
||||
const unique = [];
|
||||
for (const m of final) { if (!ids.has(m.messageId)) { ids.add(m.messageId); unique.push(m); } }
|
||||
|
||||
const merged = mergeHistoryMessages({
|
||||
incoming: raw,
|
||||
existing: this.data.messages,
|
||||
refresh,
|
||||
limit: 20,
|
||||
mapMessage: (m, idx, list) => this.mapHistoryMessage(m, idx, list),
|
||||
});
|
||||
|
||||
const update = {
|
||||
messages: unique,
|
||||
hasMore: list.length >= 20,
|
||||
lastTimestamp: unique.length ? unique[0].timestamp : lastTimestamp,
|
||||
loadingHistory: false
|
||||
messages: merged.messages,
|
||||
hasMore: merged.hasMore,
|
||||
lastTimestamp: merged.lastTimestamp,
|
||||
};
|
||||
if (refresh) update.scrollToView = 'msg-bottom';
|
||||
this.setData(update);
|
||||
if (refresh) this.markPrivateMessageAsRead();
|
||||
resolve();
|
||||
},
|
||||
onFailed: () => { this.setData({ loadingHistory: false }); resolve(); }
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('[私聊] 历史消息加载失败', e);
|
||||
if (!refresh) {
|
||||
wx.showToast({ title: '加载历史消息失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this.setData({ loadingHistory: false });
|
||||
}
|
||||
},
|
||||
|
||||
listenNewMsg() {
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
refresher-enabled="{{true}}"
|
||||
refresher-triggered="{{isRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
style="top: 20rpx; bottom: calc({{bottomSafeHeight}}rpx + {{keyboardHeight}}px + env(safe-area-inset-bottom) + 20rpx);">
|
||||
style="top: 20rpx; bottom: calc({{bottomSafeHeight}}rpx + {{keyboardHeight}}px + env(safe-area-inset-bottom) + 24rpx);">
|
||||
<view class="chat-loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<block wx:for="{{messages}}" wx:key="messageId">
|
||||
<view class="chat-msg-wrapper" data-messageid="{{item.messageId}}" bindlongpress="showAction">
|
||||
<view class="chat-time-tag" wx:if="{{item.showTime}}">{{item.formattedTime}}</view>
|
||||
<view class="chat-msg-row {{item.senderId === currentUser.id ? 'chat-msg-row--right' : 'chat-msg-row--left'}}">
|
||||
<image wx:if="{{item.senderId !== currentUser.id}}" class="chat-avatar" src="{{item.senderData.avatar || toAvatar}}" mode="aspectFill" binderror="onPeerAvatarError" />
|
||||
<image wx:if="{{item.senderId !== currentUser.id}}" class="chat-avatar" src="{{toAvatar || defaultAvatarUrl}}" mode="aspectFill" binderror="onPeerAvatarError" />
|
||||
<view class="chat-bubble {{item.senderId === currentUser.id ? 'chat-bubble--right' : 'chat-bubble--left'}}"
|
||||
data-messageid="{{item.messageId}}"
|
||||
data-text="{{item.type==='text' ? item.payload.text : ''}}"
|
||||
@@ -23,14 +23,14 @@
|
||||
<text class="chat-order-info">ID: {{item.payload.orderId}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{item.senderData.avatar || currentUser.avatar}}" mode="aspectFill" binderror="onSelfAvatarError" data-index="{{index}}" />
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar || defaultAvatarUrl}}" mode="aspectFill" binderror="onSelfAvatarError" data-index="{{index}}" />
|
||||
</view>
|
||||
<view wx:if="{{item.senderId === currentUser.id && item.status === 'success'}}" class="chat-read-tag">
|
||||
{{item.read ? '已读' : '未读'}}
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view id="msg-bottom" style="height:20rpx;"></view>
|
||||
<view id="msg-bottom" style="height:56rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="chat-input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
const app = getApp();
|
||||
import { formatDate } from '../../static/lib/utils';
|
||||
import { getLocalImUserId } from '../../utils/im-user.js';
|
||||
import { resolveAvatarUrl } from '../../utils/avatar.js';
|
||||
import { resolveAvatarUrl, getDefaultAvatarUrl, resolveAvatarFromStorage, subscribeConfigAvatarRefresh, ensureAvatarUrl } from '../../utils/avatar.js';
|
||||
import {
|
||||
fetchHistoryMessages,
|
||||
getOldestHistoryTimestamp,
|
||||
mergeHistoryMessages,
|
||||
waitChatImReady,
|
||||
} from '../../utils/chat-history.js';
|
||||
import { matchAutoReply, getWelcomeText } from '../../utils/scriptService.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -29,11 +36,14 @@ Page({
|
||||
pendingImage: '',
|
||||
pendingImageFile: null,
|
||||
keyboardHeight: 0,
|
||||
bottomSafeHeight: 10
|
||||
bottomSafeHeight: 140,
|
||||
defaultAvatarUrl: '',
|
||||
welcomeText: '你好,请问有什么可以帮到您的?',
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
let teamId = '', teamName = '客服', teamAvatar = '';
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
if (options.data) {
|
||||
try {
|
||||
const p = JSON.parse(decodeURIComponent(options.data));
|
||||
@@ -43,38 +53,102 @@ Page({
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (!teamAvatar) {
|
||||
teamAvatar = app.globalData.ossImageUrl + app.globalData.morentouxiang;
|
||||
let teamAvatarResolved = teamAvatar ? ensureAvatarUrl(teamAvatar, app) : defAvatar;
|
||||
if (!teamAvatarResolved || !teamAvatarResolved.startsWith('http')) {
|
||||
teamAvatarResolved = defAvatar;
|
||||
}
|
||||
this.setData({ teamId, teamName, teamAvatar });
|
||||
this.setData({ teamId, teamName, teamAvatar: teamAvatarResolved, defaultAvatarUrl: defAvatar });
|
||||
wx.setNavigationBarTitle({ title: teamName });
|
||||
this.initCurrentUser();
|
||||
this.loadWelcomeScript();
|
||||
this._unsubConfigAvatar = subscribeConfigAvatarRefresh(this, () => {
|
||||
const def = getDefaultAvatarUrl(app);
|
||||
this.setData({
|
||||
defaultAvatarUrl: def,
|
||||
teamAvatar: def,
|
||||
});
|
||||
this.refreshCurrentUserAvatar();
|
||||
});
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
if (this._unsubConfigAvatar) {
|
||||
this._unsubConfigAvatar();
|
||||
this._unsubConfigAvatar = null;
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const patch = {
|
||||
defaultAvatarUrl: defAvatar,
|
||||
teamAvatar: defAvatar,
|
||||
};
|
||||
this.setData(patch);
|
||||
this.refreshCurrentUserAvatar();
|
||||
this.autoConnect();
|
||||
},
|
||||
|
||||
refreshCurrentUserAvatar() {
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const cu = this.data.currentUser;
|
||||
if (!cu) return;
|
||||
const avatar = resolveAvatarUrl(cu.avatar || wx.getStorageSync('touxiang'), app) || defAvatar;
|
||||
if (avatar !== cu.avatar) {
|
||||
this.setData({ currentUser: { ...cu, avatar } });
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.clearAllListeners();
|
||||
},
|
||||
|
||||
autoConnect() {
|
||||
async loadWelcomeScript() {
|
||||
try {
|
||||
const text = await getWelcomeText();
|
||||
if (!text) return;
|
||||
this.setData({ welcomeText: text });
|
||||
const msgs = this.data.messages;
|
||||
const idx = msgs.findIndex((m) => m.messageId === 'welcome-cs');
|
||||
if (idx >= 0) {
|
||||
const updated = [...msgs];
|
||||
updated[idx] = { ...updated[idx], payload: { ...updated[idx].payload, text } };
|
||||
this.setData({ messages: updated });
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('loadWelcomeScript failed', e);
|
||||
}
|
||||
},
|
||||
|
||||
async autoConnect() {
|
||||
const targetUserId = getLocalImUserId(app);
|
||||
if (!targetUserId) return;
|
||||
|
||||
const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
|
||||
if (status === 'connected' || status === 'reconnected') {
|
||||
const currentUserId = wx.goEasy.im ? wx.goEasy.im.userId : null;
|
||||
if (currentUserId === targetUserId) {
|
||||
const ready = async () => {
|
||||
this.setupAllListeners();
|
||||
this.loadHistory(true);
|
||||
await this.loadHistory(true);
|
||||
};
|
||||
|
||||
try {
|
||||
const ok = await waitChatImReady(app, 12000);
|
||||
if (ok) {
|
||||
await ready();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[客服] IM 连接失败', e);
|
||||
}
|
||||
|
||||
if (this.isConnected()) {
|
||||
await ready();
|
||||
} else {
|
||||
wx.goEasy.disconnect();
|
||||
}
|
||||
}
|
||||
this.connectGoEasy(targetUserId);
|
||||
}
|
||||
},
|
||||
|
||||
isConnected() {
|
||||
const s = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
|
||||
return s === 'connected' || s === 'reconnected';
|
||||
},
|
||||
|
||||
connectGoEasy(userId) {
|
||||
@@ -84,7 +158,7 @@ Page({
|
||||
id: userId,
|
||||
data: {
|
||||
name: currentUser.name,
|
||||
avatar: resolveAvatarUrl(currentUser.avatar, app)
|
||||
avatar: resolveAvatarUrl(currentUser.avatar, app) || getDefaultAvatarUrl(app)
|
||||
},
|
||||
onSuccess: () => {
|
||||
this.setupAllListeners();
|
||||
@@ -119,11 +193,13 @@ Page({
|
||||
initCurrentUser() {
|
||||
const imId = getLocalImUserId(app);
|
||||
const uid = wx.getStorageSync('uid');
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const avatar = resolveAvatarFromStorage(app) || defAvatar;
|
||||
this.setData({
|
||||
currentUser: {
|
||||
id: imId || ('Boss' + uid),
|
||||
name: app.globalData.currentUser?.name || `用户${uid}`,
|
||||
avatar: resolveAvatarUrl(wx.getStorageSync('touxiang'), app),
|
||||
name: app.globalData.currentUser?.name || wx.getStorageSync('nicheng') || `用户${uid}`,
|
||||
avatar,
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -169,66 +245,89 @@ Page({
|
||||
this.setData({ messages: msgs });
|
||||
},
|
||||
|
||||
// 历史消息
|
||||
loadHistory(refresh) {
|
||||
if (!refresh && !this.data.hasMore) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
this.setData({ loadingHistory: true });
|
||||
const { teamId, lastTimestamp } = this.data;
|
||||
const ts = refresh ? null : lastTimestamp;
|
||||
wx.goEasy.im.history({
|
||||
type: wx.GoEasy.IM_SCENE.CS,
|
||||
id: teamId,
|
||||
lastTimestamp: ts,
|
||||
limit: 20,
|
||||
onSuccess: (res) => {
|
||||
let list = res.content || [];
|
||||
list.sort((a, b) => a.timestamp - b.timestamp);
|
||||
list.forEach((m, idx) => {
|
||||
mapHistoryMessage(m, idx, list) {
|
||||
m = this.normalizeMessage(m);
|
||||
list[idx] = m;
|
||||
m.formattedTime = formatDate(m.timestamp);
|
||||
if (idx === 0) m.showTime = true;
|
||||
else m.showTime = (m.timestamp - list[idx-1].timestamp) / 60000 > 5;
|
||||
if (m.senderId === this.data.currentUser.id) {
|
||||
m.senderData = { name: this.data.currentUser.name, avatar: this.data.currentUser.avatar };
|
||||
else m.showTime = (m.timestamp - list[idx - 1].timestamp) / 60000 > 5;
|
||||
if (m.senderId === this.data.currentUser?.id) {
|
||||
const av = resolveAvatarUrl(this.data.currentUser.avatar, app) || this.data.defaultAvatarUrl;
|
||||
m.senderData = { name: this.data.currentUser.name, avatar: av };
|
||||
} else {
|
||||
m.senderData = { name: this.data.teamName, avatar: this.data.teamAvatar };
|
||||
const av = resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl;
|
||||
m.senderData = { name: this.data.teamName, avatar: av };
|
||||
}
|
||||
});
|
||||
let final = refresh ? list : [...list, ...this.data.messages];
|
||||
if (!refresh) {
|
||||
for (let i=0; i<final.length; i++) {
|
||||
if (i===0) final[i].showTime = true;
|
||||
else final[i].showTime = (final[i].timestamp - final[i-1].timestamp) / 60000 > 5;
|
||||
}
|
||||
}
|
||||
const ids = new Set();
|
||||
const unique = [];
|
||||
for (const m of final) { if (!ids.has(m.messageId)) { ids.add(m.messageId); unique.push(m); } }
|
||||
return m;
|
||||
},
|
||||
|
||||
// 插入欢迎语
|
||||
injectWelcomeMessage(messages) {
|
||||
const list = Array.isArray(messages) ? [...messages] : [];
|
||||
const welcomeId = 'welcome-cs';
|
||||
if (!unique.some(m => m.messageId === welcomeId)) {
|
||||
if (!list.some((m) => m.messageId === welcomeId)) {
|
||||
const welcome = this.createWelcomeMessage();
|
||||
welcome.messageId = welcomeId;
|
||||
welcome.showTime = true;
|
||||
unique.unshift(welcome);
|
||||
list.unshift(welcome);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
|
||||
// 历史消息
|
||||
async loadHistory(refresh) {
|
||||
if (!refresh && !this.data.hasMore) return;
|
||||
if (this.data.loadingHistory) return;
|
||||
if (!this.data.teamId) return;
|
||||
|
||||
this.setData({ loadingHistory: true });
|
||||
try {
|
||||
const ready = await waitChatImReady(app, 12000);
|
||||
if (!ready) {
|
||||
wx.showToast({ title: '消息服务未连接,请稍后重试', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const ts = refresh
|
||||
? null
|
||||
: getOldestHistoryTimestamp(this.data.messages, this.data.lastTimestamp);
|
||||
if (!refresh && ts == null) {
|
||||
this.setData({ hasMore: false });
|
||||
wx.showToast({ title: '没有更早的消息了', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const raw = await fetchHistoryMessages({
|
||||
scene: wx.GoEasy.IM_SCENE.CS,
|
||||
id: this.data.teamId,
|
||||
lastTimestamp: ts,
|
||||
limit: 20,
|
||||
});
|
||||
|
||||
let merged = mergeHistoryMessages({
|
||||
incoming: raw,
|
||||
existing: this.data.messages,
|
||||
refresh,
|
||||
limit: 20,
|
||||
mapMessage: (m, idx, list) => this.mapHistoryMessage(m, idx, list),
|
||||
});
|
||||
|
||||
merged.messages = this.injectWelcomeMessage(merged.messages);
|
||||
merged.lastTimestamp = getOldestHistoryTimestamp(merged.messages, merged.lastTimestamp);
|
||||
|
||||
const update = {
|
||||
messages: unique,
|
||||
hasMore: list.length >= 20,
|
||||
lastTimestamp: unique.length ? unique[0].timestamp : lastTimestamp,
|
||||
loadingHistory: false
|
||||
messages: merged.messages,
|
||||
hasMore: merged.hasMore,
|
||||
lastTimestamp: merged.lastTimestamp,
|
||||
};
|
||||
if (refresh) update.scrollToView = 'msg-bottom';
|
||||
this.setData(update);
|
||||
resolve();
|
||||
},
|
||||
onFailed: () => { this.setData({ loadingHistory: false }); resolve(); }
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('[客服] 历史消息加载失败', e);
|
||||
if (!refresh) {
|
||||
wx.showToast({ title: '加载历史消息失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this.setData({ loadingHistory: false });
|
||||
}
|
||||
},
|
||||
|
||||
createWelcomeMessage() {
|
||||
@@ -238,8 +337,11 @@ Page({
|
||||
timestamp: Date.now(),
|
||||
senderId: 'system',
|
||||
receiverId: this.data.currentUser?.id,
|
||||
senderData: { name: '系统消息', avatar: '' },
|
||||
payload: { text: '你好,请问有什么可以帮到您的?' },
|
||||
senderData: {
|
||||
name: this.data.teamName || '客服',
|
||||
avatar: resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl,
|
||||
},
|
||||
payload: { text: this.data.welcomeText || '你好,请问有什么可以帮到您的?' },
|
||||
status: 'success',
|
||||
read: false,
|
||||
formattedTime: formatDate(Date.now()),
|
||||
@@ -255,7 +357,10 @@ Page({
|
||||
if (msg.senderId === this.data.currentUser.id) return;
|
||||
msg = this.normalizeMessage(msg);
|
||||
msg.formattedTime = formatDate(msg.timestamp);
|
||||
msg.senderData = { name: this.data.teamName, avatar: this.data.teamAvatar };
|
||||
msg.senderData = {
|
||||
name: this.data.teamName,
|
||||
avatar: resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl,
|
||||
};
|
||||
const msgs = this.data.messages;
|
||||
const last = msgs.length ? msgs[msgs.length-1] : null;
|
||||
msg.showTime = last ? (msg.timestamp - last.timestamp) / 60000 > 5 : true;
|
||||
@@ -370,11 +475,53 @@ Page({
|
||||
});
|
||||
wx.goEasy.im.sendMessage({
|
||||
message:msg,
|
||||
onSuccess:() => this.updateMsg(id, 'success'),
|
||||
onSuccess:() => {
|
||||
this.updateMsg(id, 'success');
|
||||
this.tryAutoReply(text);
|
||||
},
|
||||
onFailed:() => this.updateMsg(id, 'failed')
|
||||
});
|
||||
},
|
||||
|
||||
async tryAutoReply(userText) {
|
||||
try {
|
||||
const result = await matchAutoReply(userText);
|
||||
if (result && result.matched && result.content) {
|
||||
this.insertCsAutoReply(result);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('tryAutoReply failed', e);
|
||||
}
|
||||
},
|
||||
|
||||
insertCsAutoReply(result) {
|
||||
const replyType = (result && result.reply_type) || 'text';
|
||||
const content = (result && result.content) || '';
|
||||
if (!content) return;
|
||||
const isImage = replyType === 'image';
|
||||
const msg = {
|
||||
messageId: 'auto-reply-' + Date.now(),
|
||||
type: isImage ? 'image' : 'text',
|
||||
timestamp: Date.now(),
|
||||
senderId: this.data.teamId,
|
||||
receiverId: this.data.currentUser?.id,
|
||||
senderData: {
|
||||
name: this.data.teamName,
|
||||
avatar: resolveAvatarUrl(this.data.teamAvatar, app) || this.data.defaultAvatarUrl,
|
||||
},
|
||||
payload: isImage ? { url: content } : { text: content },
|
||||
status: 'success',
|
||||
read: false,
|
||||
formattedTime: formatDate(Date.now()),
|
||||
showTime: this.needShow(Date.now()),
|
||||
};
|
||||
if (isImage) msg.imageUrl = content;
|
||||
this.setData({
|
||||
messages: [...this.data.messages, msg],
|
||||
scrollToView: 'msg-bottom',
|
||||
});
|
||||
},
|
||||
|
||||
sendImageMsg(filePath, fileObj) {
|
||||
const { currentUser, teamId, teamName, teamAvatar } = this.data;
|
||||
if (!teamId || !currentUser || !filePath) return;
|
||||
@@ -521,9 +668,30 @@ Page({
|
||||
|
||||
onKeyboardHeightChange(e) { this.setData({ keyboardHeight: e.detail.height }); },
|
||||
|
||||
onTeamAvatarError() {
|
||||
this.setData({ teamAvatar: getDefaultAvatarUrl(app) });
|
||||
},
|
||||
|
||||
onSelfAvatarError() {
|
||||
const def = getDefaultAvatarUrl(app);
|
||||
const cu = this.data.currentUser;
|
||||
if (cu) {
|
||||
this.setData({ currentUser: { ...cu, avatar: def } });
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
if (this.data.loadingHistory) return;
|
||||
if (!this.data.hasMore) {
|
||||
wx.showToast({ title: '没有更早的消息了', icon: 'none' });
|
||||
this.setData({ isRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
return;
|
||||
}
|
||||
this.setData({ isRefreshing: true });
|
||||
this.loadHistory(false).finally(() => { this.setData({ isRefreshing: false }); wx.stopPullDownRefresh(); });
|
||||
this.loadHistory(false).finally(() => {
|
||||
this.setData({ isRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -11,7 +11,7 @@
|
||||
<view class="chat-time-tag" wx:if="{{item.showTime}}">{{item.formattedTime}}</view>
|
||||
<view class="chat-msg-row {{item.senderId === currentUser.id ? 'chat-msg-row--right' : 'chat-msg-row--left'}}">
|
||||
<!-- 对方头像(客服) -->
|
||||
<image wx:if="{{item.senderId !== currentUser.id}}" class="chat-avatar" src="{{teamAvatar}}" mode="aspectFill" />
|
||||
<image wx:if="{{item.senderId !== currentUser.id}}" class="chat-avatar" src="{{defaultAvatarUrl}}" mode="aspectFill" binderror="onTeamAvatarError" />
|
||||
<view class="chat-bubble {{item.senderId === currentUser.id ? 'chat-bubble--right' : 'chat-bubble--left'}}"
|
||||
data-messageid="{{item.messageId}}"
|
||||
data-text="{{item.type==='text' ? item.payload.text : ''}}"
|
||||
@@ -24,19 +24,19 @@
|
||||
<view class="chat-order-info">
|
||||
<text>订单号:{{item.payload.dingdan_id || '无'}}</text>
|
||||
<text>服务:{{item.payload.jieshao || ''}}</text>
|
||||
<text style="color:#a855f7;font-weight:bold">¥{{item.payload.jine}}</text>
|
||||
<text style="color:#e6a23c;font-weight:bold">¥{{item.payload.jine}}</text>
|
||||
</view>
|
||||
<button class="chat-copy-btn" catchtap="showOrderDetailPopup" data-order="{{item.payload}}" size="mini">查看详情</button>
|
||||
</view>
|
||||
<text wx:else class="chat-msg-text">{{item.payload.text || '[未知消息]'}}</text>
|
||||
</view>
|
||||
<!-- 自己头像 -->
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar}}" mode="aspectFill" />
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar || defaultAvatarUrl}}" mode="aspectFill" binderror="onSelfAvatarError" />
|
||||
</view>
|
||||
<!-- 🔥 已读/未读标签已彻底移除 -->
|
||||
</view>
|
||||
</block>
|
||||
<view id="msg-bottom" style="height:20rpx;"></view>
|
||||
<view id="msg-bottom" style="height:56rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部输入区域(保持不变) -->
|
||||
|
||||
283
pages/dashou-exam/dashou-exam.js
Normal file
283
pages/dashou-exam/dashou-exam.js
Normal file
@@ -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' });
|
||||
}
|
||||
},
|
||||
}));
|
||||
6
pages/dashou-exam/dashou-exam.json
Normal file
6
pages/dashou-exam/dashou-exam.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "接单考试",
|
||||
"navigationBarBackgroundColor": "#c4b5fd",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
66
pages/dashou-exam/dashou-exam.wxml
Normal file
66
pages/dashou-exam/dashou-exam.wxml
Normal file
@@ -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>
|
||||
228
pages/dashou-exam/dashou-exam.wxss
Normal file
228
pages/dashou-exam/dashou-exam.wxss
Normal file
@@ -0,0 +1,228 @@
|
||||
.exam-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #f5f3ff 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, #c4b5fd, #a78bfa);
|
||||
padding: 22rpx 48rpx;
|
||||
border-radius: 48rpx;
|
||||
font-weight: 600;
|
||||
font-size: 30rpx;
|
||||
color: #6d28d9;
|
||||
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: #6d28d9;
|
||||
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: #f5f3ff;
|
||||
border: 1rpx solid #c4b5fd;
|
||||
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: #c4b5fd;
|
||||
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: #c4b5fd;
|
||||
background: #f5f3ff;
|
||||
}
|
||||
|
||||
.opt-radio-dot {
|
||||
width: 18rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 50%;
|
||||
background: #c4b5fd;
|
||||
}
|
||||
|
||||
.opt-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.opt-label {
|
||||
font-weight: 700;
|
||||
color: #6d28d9;
|
||||
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;
|
||||
}
|
||||
14
pages/dashouduan/dashouduan.js
Normal file
14
pages/dashouduan/dashouduan.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/** 旧二维码兼容:dashouduan → 打手中心,inviteCode 自动注册逻辑不变 */
|
||||
import { parseSceneOptions } from '../../utils/base-page.js';
|
||||
|
||||
Page({
|
||||
onLoad(options) {
|
||||
const parsed = parseSceneOptions(options || {});
|
||||
const inviteCode = parsed.inviteCode || options.inviteCode || '';
|
||||
let url = '/pages/fighter/fighter';
|
||||
if (inviteCode) {
|
||||
url += `?inviteCode=${encodeURIComponent(inviteCode)}`;
|
||||
}
|
||||
wx.reLaunch({ url });
|
||||
},
|
||||
});
|
||||
1
pages/dashouduan/dashouduan.json
Normal file
1
pages/dashouduan/dashouduan.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "navigationBarTitleText": "加载中", "usingComponents": {} }
|
||||
1
pages/dashouduan/dashouduan.wxml
Normal file
1
pages/dashouduan/dashouduan.wxml
Normal file
@@ -0,0 +1 @@
|
||||
<view class="tip">正在进入打手端…</view>
|
||||
1
pages/dashouduan/dashouduan.wxss
Normal file
1
pages/dashouduan/dashouduan.wxss
Normal file
@@ -0,0 +1 @@
|
||||
.tip { display:flex; align-items:center; justify-content:center; min-height:100vh; color:#999; font-size:28rpx; }
|
||||
@@ -1,6 +1,6 @@
|
||||
/* pages/edit/edit.wxss */
|
||||
page {
|
||||
background: linear-gradient(135deg, #faf5ff 0%, #f0f8ff 100%);
|
||||
background: linear-gradient(135deg, #fffaf0 0%, #f0f8ff 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag"
|
||||
},
|
||||
"backgroundTextStyle": "dark",
|
||||
"navigationBarBackgroundColor": "#fff8e1",
|
||||
"navigationBarBackgroundColor": "#f7dc51",
|
||||
"navigationBarTitleText": "常规发单",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f5f5f5",
|
||||
|
||||
@@ -289,7 +289,7 @@ page {
|
||||
}
|
||||
|
||||
.filter-switch.active {
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ page {
|
||||
|
||||
.copy-link-btn {
|
||||
padding: 8rpx 20rpx;
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
border-radius: 15rpx;
|
||||
font-size: 22rpx;
|
||||
@@ -337,7 +337,7 @@ page {
|
||||
.load-more-btn {
|
||||
display: inline-block;
|
||||
padding: 15rpx 40rpx;
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
border-radius: 30rpx;
|
||||
color: #492f00;
|
||||
font-size: 26rpx;
|
||||
@@ -373,7 +373,7 @@ page {
|
||||
.generate-modal-grid-btn,
|
||||
.copy-modal-grid-btn,
|
||||
.edit-modal-grid-btn {
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ page {
|
||||
}
|
||||
|
||||
.confirm-add-grid-btn {
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ page {
|
||||
}
|
||||
|
||||
.confirm-new-link-grid-btn {
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ page {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
border: 8rpx solid #eee;
|
||||
border-top: 8rpx solid #9333ea;
|
||||
border-top: 8rpx solid #ffd061;
|
||||
border-radius: 50%;
|
||||
animation: spin 1.2s linear infinite;
|
||||
margin-bottom: 35rpx;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* 页面容器 */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #f5f3ff 0%, #e6f7ff 100%);
|
||||
background: linear-gradient(135deg, #fffacd 0%, #e6f7ff 100%);
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
height: 180rpx;
|
||||
border-radius: 50%;
|
||||
border: 6rpx solid #ffffff;
|
||||
box-shadow: 0 0 30rpx rgba(196, 181, 253, 0.4);
|
||||
box-shadow: 0 0 30rpx rgba(255, 215, 0, 0.4);
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
background-color: #f5f5f5;
|
||||
@@ -38,7 +38,7 @@
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(196, 181, 253, 0.2) 0%, transparent 70%);
|
||||
background: radial-gradient(circle, rgba(255, 215, 0, 0.2) 0%, transparent 70%);
|
||||
z-index: 1;
|
||||
animation: halo-pulse 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
139
pages/fighter-ledger/fighter-ledger.js
Normal file
139
pages/fighter-ledger/fighter-ledger.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import request from '../../utils/request.js';
|
||||
import { getOrderStatusText } from '../../utils/api-helper.js';
|
||||
|
||||
const PAGE_SIZE = 15;
|
||||
|
||||
const TYPE_CONFIG = {
|
||||
commission: { title: '佣金记录', statuses: [3, 8] },
|
||||
settle: { title: '结算记录', statuses: [3] },
|
||||
withdraw: { title: '提现记录', api: 'withdraw' },
|
||||
deduct: { title: '扣款记录', api: 'deduct' },
|
||||
};
|
||||
|
||||
Page({
|
||||
data: {
|
||||
pageTitle: '交易明细',
|
||||
type: 'commission',
|
||||
statusBar: 20,
|
||||
list: [],
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
loading: false,
|
||||
refreshing: false,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
const type = (options.type || 'commission').trim();
|
||||
const cfg = TYPE_CONFIG[type] || TYPE_CONFIG.commission;
|
||||
this.setData({
|
||||
type,
|
||||
pageTitle: cfg.title,
|
||||
statusBar: sys.statusBarHeight || 20,
|
||||
});
|
||||
this.loadList(true);
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/fighter/fighter' }) });
|
||||
},
|
||||
|
||||
onRefresh() {
|
||||
this.setData({ refreshing: true });
|
||||
this.loadList(true).finally(() => this.setData({ refreshing: false }));
|
||||
},
|
||||
|
||||
onLoadMore() {
|
||||
if (this.data.hasMore && !this.data.loading) {
|
||||
this.loadList(false);
|
||||
}
|
||||
},
|
||||
|
||||
async loadList(refresh) {
|
||||
const { type, loading } = this.data;
|
||||
if (loading) return;
|
||||
const page = refresh ? 1 : this.data.page;
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
let rows = [];
|
||||
let hasMore = false;
|
||||
if (type === 'withdraw') {
|
||||
const res = await request({
|
||||
url: '/yonghu/tixianjiluhq',
|
||||
method: 'POST',
|
||||
data: { page, limit: PAGE_SIZE },
|
||||
});
|
||||
const body = res?.data;
|
||||
if (body && (body.code === 200 || body.code === 0)) {
|
||||
const raw = body.data?.list || [];
|
||||
rows = raw.map((item, idx) => ({
|
||||
id: item.id || item.jilu_id || `${page}-${idx}`,
|
||||
title: item.beizhu || item.status_text || item.zhuangtai_text || '提现',
|
||||
time: item.creat_time || item.create_time || item.tijiao_shijian || '',
|
||||
amount: `-¥${item.jine || item.amount || item.tixian_jine || '0.00'}`,
|
||||
minus: true,
|
||||
}));
|
||||
hasMore = raw.length >= PAGE_SIZE;
|
||||
}
|
||||
} else if (type === 'deduct') {
|
||||
const res = await request({
|
||||
url: '/yonghu/dsfklbhq',
|
||||
method: 'POST',
|
||||
data: { page, page_size: PAGE_SIZE, zhuangtai: 0 },
|
||||
});
|
||||
const body = res?.data;
|
||||
if (body && (body.code === 200 || body.code === 0)) {
|
||||
const raw = body.data?.list || [];
|
||||
rows = raw.map((item, idx) => ({
|
||||
id: item.fadan_id || item.id || `${page}-${idx}`,
|
||||
title: item.yuanyin || item.beizhu || item.fakuan_yuanyin || '扣款',
|
||||
time: item.creat_time || item.create_time || '',
|
||||
amount: `-¥${item.jine || item.fakuan_jine || item.fakuanjine || '0.00'}`,
|
||||
minus: true,
|
||||
}));
|
||||
hasMore = raw.length >= PAGE_SIZE;
|
||||
}
|
||||
} else {
|
||||
const cfg = TYPE_CONFIG[type] || TYPE_CONFIG.commission;
|
||||
const res = await request({
|
||||
url: '/dingdan/dshqdingdan',
|
||||
method: 'POST',
|
||||
data: {
|
||||
zhuangtai_list: cfg.statuses,
|
||||
page,
|
||||
page_size: PAGE_SIZE,
|
||||
},
|
||||
});
|
||||
const body = res?.data;
|
||||
if (body && (body.code === 200 || body.code === 0)) {
|
||||
const raw = body.data?.list || [];
|
||||
rows = raw.map((item) => {
|
||||
const amt = item.jine || item.dashou_fencheng || '0.00';
|
||||
const brief = (item.jieshao || '').trim();
|
||||
const title = brief
|
||||
? (brief.length > 18 ? `${brief.slice(0, 18)}…` : brief)
|
||||
: `订单 ${item.dingdan_id || ''}`;
|
||||
return {
|
||||
id: item.dingdan_id,
|
||||
title,
|
||||
time: item.wancheng_shijian || item.jiesuan_shijian || item.creat_time || getOrderStatusText(item.zhuangtai),
|
||||
amount: `+¥${amt}`,
|
||||
minus: false,
|
||||
};
|
||||
});
|
||||
hasMore = body.data?.has_more ?? raw.length >= PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
const list = refresh ? rows : [...this.data.list, ...rows];
|
||||
this.setData({
|
||||
list,
|
||||
page: refresh ? 2 : page + 1,
|
||||
hasMore,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('ledger load fail', e);
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
});
|
||||
4
pages/fighter-ledger/fighter-ledger.json
Normal file
4
pages/fighter-ledger/fighter-ledger.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {}
|
||||
}
|
||||
20
pages/fighter-ledger/fighter-ledger.wxml
Normal file
20
pages/fighter-ledger/fighter-ledger.wxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<view class="ledger-page safe-area">
|
||||
<view class="status-bar" style="height:{{statusBar}}px"></view>
|
||||
<view class="nav-bar flexb">
|
||||
<text class="back" bindtap="goBack">‹</text>
|
||||
<text class="nav-title">{{pageTitle}}</text>
|
||||
<text class="back placeholder"></text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="ledger-scroll" refresher-enabled refresher-triggered="{{refreshing}}" bindrefresherrefresh="onRefresh" bindscrolltolower="onLoadMore">
|
||||
<view wx:if="{{loading && list.length === 0}}" class="empty-box">加载中...</view>
|
||||
<view wx:elif="{{list.length === 0}}" class="empty-box">暂无记录</view>
|
||||
<view wx:for="{{list}}" wx:key="id" class="list-item flexb">
|
||||
<view class="item-main">
|
||||
<view class="row-t">{{item.title}}</view>
|
||||
<view class="row-d">{{item.time}}</view>
|
||||
</view>
|
||||
<text class="row-r {{item.minus ? 'minus' : ''}}">{{item.amount}}</text>
|
||||
</view>
|
||||
<view wx:if="{{list.length > 0 && !hasMore}}" class="load-tip">— 已加载全部 —</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
75
pages/fighter-ledger/fighter-ledger.wxss
Normal file
75
pages/fighter-ledger/fighter-ledger.wxss
Normal file
@@ -0,0 +1,75 @@
|
||||
@import '../../styles/xym-layout.wxss';
|
||||
|
||||
.ledger-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
padding: 0 24rpx 16rpx;
|
||||
}
|
||||
|
||||
.back {
|
||||
width: 60rpx;
|
||||
font-size: 48rpx;
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.back.placeholder {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.ledger-scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.row-t {
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.row-d {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.row-r {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #492f00;
|
||||
flex-shrink: 0;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.row-r.minus {
|
||||
color: #ff4c45;
|
||||
}
|
||||
|
||||
.empty-box,
|
||||
.load-tip {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
padding: 80rpx 0;
|
||||
}
|
||||
@@ -387,7 +387,7 @@ Page({
|
||||
},
|
||||
|
||||
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'
|
||||
|
||||
@@ -296,9 +296,9 @@
|
||||
}
|
||||
|
||||
.zhuangtai-shensuzhong {
|
||||
background: rgba(147, 51, 234, 0.2);
|
||||
border-color: #9333ea;
|
||||
box-shadow: 0 0 8rpx rgba(147, 51, 234, 0.2);
|
||||
background: rgba(255, 165, 0, 0.2);
|
||||
border-color: #FFA500;
|
||||
box-shadow: 0 0 8rpx rgba(255, 165, 0, 0.2);
|
||||
}
|
||||
|
||||
.zhuangtai-weizhi {
|
||||
@@ -657,9 +657,9 @@
|
||||
}
|
||||
|
||||
.cbtn-shensu {
|
||||
background: rgba(147, 51, 234, 0.1);
|
||||
border-color: #9333ea;
|
||||
box-shadow: 0 0 12rpx rgba(147, 51, 234, 0.2);
|
||||
background: rgba(255, 165, 0, 0.1);
|
||||
border-color: #FFA500;
|
||||
box-shadow: 0 0 12rpx rgba(255, 165, 0, 0.2);
|
||||
}
|
||||
|
||||
.cbtn-shensu-disabled {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// pages/fighter-order-detail/fighter-order-detail.js - 【最终版:提交前公告弹窗 + 修改图片至少保留一张】
|
||||
const app = getApp()
|
||||
import request from '../../utils/request.js'
|
||||
const COS = require('../../utils/cos-wx-sdk-v5.js')
|
||||
import connectionManager from '../../utils/xiaoxilj.js'
|
||||
const COS = require('../../utils/cos-wx-sdk-v5.min.js')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -30,8 +31,8 @@ Page({
|
||||
laoban_id: ''
|
||||
},
|
||||
zhuangtaiYanseMap: {
|
||||
1: '#E8F5E9', 2: '#E3F2FD', 3: '#F3E5F5', 4: '#F3E8FF',
|
||||
5: '#FFEBEE', 6: '#EFEBE9', 7: '#E0F7FA', 8: '#F5F3FF'
|
||||
1: '#E8F5E9', 2: '#E3F2FD', 3: '#F3E5F5', 4: '#FFF3E0',
|
||||
5: '#FFEBEE', 6: '#EFEBE9', 7: '#E0F7FA', 8: '#FFF8E1'
|
||||
},
|
||||
zhuangtaiWenziMap: {
|
||||
1: '已下单', 2: '进行中', 3: '已完成', 4: '退款中',
|
||||
@@ -284,7 +285,7 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
// ===== 联系老板(订单群 groupId 即订单号,与商家端 sjddxq 一致) =====
|
||||
// ===== 联系老板/商家(配对群 group_Ds{打手}_Sj/Boss{对方}) =====
|
||||
goToChatWithBoss() {
|
||||
const uid = wx.getStorageSync('uid')
|
||||
if (!uid) {
|
||||
@@ -298,37 +299,22 @@ Page({
|
||||
return
|
||||
}
|
||||
|
||||
const targetUserId = 'Ds' + uid
|
||||
const connected = wx.goEasy && wx.goEasy.getConnectionStatus && wx.goEasy.getConnectionStatus() === 'connected'
|
||||
const currentUserId = wx.goEasy?.im?.userId
|
||||
if (!connected || currentUserId !== targetUserId) {
|
||||
if (app.ensureConnection) app.ensureConnection()
|
||||
wx.showToast({ title: '连接中,请稍后再试', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const fadanPingtai = this.data.jibenShuju.fadanpingtai || 0
|
||||
const partnerUid = fadanPingtai === 2
|
||||
? (this.data.xiangxiShuju.shangjia_id || '')
|
||||
: (this.data.xiangxiShuju.laoban_id || '')
|
||||
|
||||
const orderIdStr = String(orderId)
|
||||
const groupName = (this.data.jibenShuju.nicheng || '订单') + '的订单群'
|
||||
const isCross = this.data.jibenShuju.fadanpingtai || 0
|
||||
|
||||
wx.goEasy.im.subscribeGroup({
|
||||
groupIds: [orderIdStr],
|
||||
onSuccess: () => {
|
||||
const param = {
|
||||
groupId: orderIdStr,
|
||||
orderId: orderIdStr,
|
||||
groupName,
|
||||
groupAvatar: '',
|
||||
isCross,
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param)),
|
||||
})
|
||||
},
|
||||
onFailed: (err) => {
|
||||
console.error('订阅群组失败', err)
|
||||
wx.showToast({ title: '连接群聊失败', icon: 'none' })
|
||||
},
|
||||
connectionManager.connectToGroupChat({
|
||||
identityType: 'dashou',
|
||||
userId: 'Ds' + uid,
|
||||
orderId: orderId,
|
||||
partnerUid,
|
||||
fadanPingtai,
|
||||
groupName: (this.data.jibenShuju.nicheng || '订单') + '的订单群',
|
||||
isCross: this.data.jibenShuju.fadanpingtai || 0,
|
||||
}).catch((err) => {
|
||||
console.error('跳转群聊失败', err)
|
||||
wx.showToast({ title: '跳转聊天失败', icon: 'none' })
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
<text class="item-label">申请状态:</text>
|
||||
<text
|
||||
class="item-value"
|
||||
style="color: {{xiangxiShuju.chufa_zhuangtai == 0 ? '#9333ea' : (xiangxiShuju.chufa_zhuangtai == 1 ? '#4CAF50' : '#FF4444')}}"
|
||||
style="color: {{xiangxiShuju.chufa_zhuangtai == 0 ? '#FF9800' : (xiangxiShuju.chufa_zhuangtai == 1 ? '#4CAF50' : '#FF4444')}}"
|
||||
>
|
||||
{{xiangxiShuju.chufa_zhuangtai == 0 ? '审核中' : (xiangxiShuju.chufa_zhuangtai == 1 ? '处罚成功' : '处罚驳回')}}
|
||||
</text>
|
||||
|
||||
@@ -432,9 +432,9 @@
|
||||
|
||||
/* 🆕 新增:联系老板按钮 */
|
||||
.boss-btn {
|
||||
background-color: #9333ea;
|
||||
background-color: #ff9800;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4rpx 15rpx rgba(147, 51, 234, 0.3);
|
||||
box-shadow: 0 4rpx 15rpx rgba(255, 152, 0, 0.3);
|
||||
}
|
||||
|
||||
/* 🆕 新增:修改按钮(黑色) */
|
||||
|
||||
@@ -19,7 +19,7 @@ Page(createPage({
|
||||
statusList: [
|
||||
{ name: '全部', key: 'all', zhuangtaiList: [], color: '#333333' },
|
||||
{ name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' },
|
||||
{ name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#9333ea' },
|
||||
{ name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#FF9800' },
|
||||
{ name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' },
|
||||
{ name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' },
|
||||
{ name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' },
|
||||
@@ -49,18 +49,43 @@ 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();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.registerNotificationComponent();
|
||||
const app = getApp();
|
||||
const filterKey = app.globalData._fighterOrdersStatusKey;
|
||||
if (filterKey) {
|
||||
app.globalData._fighterOrdersStatusKey = null;
|
||||
if (this.data.statusList.some((s) => s.key === filterKey)) {
|
||||
this.setData({ currentStatusKey: filterKey });
|
||||
}
|
||||
if (this.data.xuanzhongLeixingId) {
|
||||
this.loadCurrentStatusOrders(true);
|
||||
}
|
||||
}
|
||||
if (wx.getStorageSync('uid')) {
|
||||
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 +166,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;
|
||||
|
||||
if (!silent) {
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${key}.isLoading`]: true,
|
||||
isLoading: true,
|
||||
isLoadingMore: !isRefresh
|
||||
isLoadingMore: !isRefresh,
|
||||
});
|
||||
}
|
||||
this._listRequesting = true;
|
||||
|
||||
try {
|
||||
const apiUrl = this.data.orderType === 'peihu'
|
||||
@@ -208,7 +237,10 @@ Page(createPage({
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载订单失败', err);
|
||||
if (!silent) {
|
||||
wx.showToast({ title: err.message || '加载失败', icon: 'none' });
|
||||
}
|
||||
if (!silent) {
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${key}.isLoading`]: false,
|
||||
isLoading: false,
|
||||
@@ -217,6 +249,9 @@ Page(createPage({
|
||||
});
|
||||
this.refreshCurrentListView();
|
||||
}
|
||||
} finally {
|
||||
this._listRequesting = false;
|
||||
}
|
||||
},
|
||||
|
||||
refreshCurrentListView() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -347,3 +347,4 @@ page {
|
||||
}
|
||||
|
||||
@import '../../styles/dashou-xym-orders-theme.wxss';
|
||||
@import '../../styles/dashou-xym-orders-page.wxss';
|
||||
@@ -1,119 +1,121 @@
|
||||
/**
|
||||
* 鎺掕姒? * 榛樿 POST /yonghu/phbhqsj
|
||||
* 鏄熶箣鐣?xzj) POST /yonghu/xzjphbhqsj锛堥個璇蜂汉鏁版帓搴忕瓑涓撶敤瑙勫垯锛? */
|
||||
/**
|
||||
* 排行榜
|
||||
* 默认 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 CLOSED_DATES = ['鏄ㄦ棩', '涓婂懆', '涓婃湀']
|
||||
const CLOSED_DATES = ['昨日', '上周', '上月']
|
||||
|
||||
const ROLE_LIST = [
|
||||
{ key: 'dashou', label: '鎺ュ崟鍛? },
|
||||
{ key: 'guanshi', label: '绠′簨' },
|
||||
{ key: 'zuzhang', label: '缁勯暱' },
|
||||
{ key: 'shangjia', label: '鍟嗗' },
|
||||
{ key: 'dashou', label: '接单员' },
|
||||
{ key: 'guanshi', label: '管事' },
|
||||
{ key: 'zuzhang', label: '组长' },
|
||||
{ key: 'shangjia', label: '商家' },
|
||||
]
|
||||
|
||||
const DATE_LIST = [
|
||||
{ key: '浠婃棩', label: '浠婃棩' },
|
||||
{ key: '鏄ㄦ棩', label: '鏄ㄦ棩' },
|
||||
{ key: '鏈懆', label: '鏈懆' },
|
||||
{ key: '涓婂懆', label: '涓婂懆' },
|
||||
{ key: '鏈湀', label: '鏈湀' },
|
||||
{ key: '涓婃湀', label: '涓婃湀' },
|
||||
{ key: '鎬绘', label: '鎬绘' },
|
||||
{ key: '今日', label: '今日' },
|
||||
{ key: '昨日', label: '昨日' },
|
||||
{ key: '本周', label: '本周' },
|
||||
{ key: '上周', label: '上周' },
|
||||
{ key: '本月', label: '本月' },
|
||||
{ key: '上月', label: '上月' },
|
||||
{ key: '总榜', label: '总榜' },
|
||||
]
|
||||
|
||||
const ROLE_META = {
|
||||
dashou: {
|
||||
title: '鎺ュ崟鍛樻帓琛屾',
|
||||
title: '接单员排行榜',
|
||||
sortField: 'chengjiao_zonge',
|
||||
sortLabel: '鍒嗙孩鎬婚',
|
||||
sortLabel: '分红总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiedan_zongliang', label: '鎺ュ崟閲?, type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '鎺ュ崟棰?, type: 'money' },
|
||||
{ field: 'chengjiao_zongliang', label: '鎴愪氦閲?, type: 'int' },
|
||||
{ field: 'jiedan_zongliang', label: '接单量', type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '接单额', type: 'money' },
|
||||
{ field: 'chengjiao_zongliang', label: '成交量', type: 'int' },
|
||||
],
|
||||
},
|
||||
guanshi: {
|
||||
title: '绠′簨鎺掕姒?,
|
||||
title: '管事排行榜',
|
||||
sortField: 'shouru_zonge',
|
||||
sortLabel: '鏀跺叆鎬婚',
|
||||
sortLabel: '收入总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'yaoqing_dashou_shu', label: '閭€璇?, type: 'int' },
|
||||
{ field: 'chongzhi_dashou_shu', label: '鍏呭€?, type: 'int' },
|
||||
{ field: 'yaoqing_dashou_shu', label: '邀请', type: 'int' },
|
||||
{ field: 'chongzhi_dashou_shu', label: '充值', type: 'int' },
|
||||
],
|
||||
},
|
||||
zuzhang: {
|
||||
title: '缁勯暱鎺掕姒?,
|
||||
title: '组长排行榜',
|
||||
sortField: 'shouru_zonge',
|
||||
sortLabel: '鏀跺叆鎬婚',
|
||||
sortLabel: '收入总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'yaoqing_guanshi_shu', label: '閭€璇?, type: 'int' },
|
||||
{ field: 'fenyong_jine', label: '鍒嗕剑', type: 'money' },
|
||||
{ field: 'yaoqing_guanshi_shu', label: '邀请', type: 'int' },
|
||||
{ field: 'fenyong_jine', label: '分佣', type: 'money' },
|
||||
],
|
||||
},
|
||||
shangjia: {
|
||||
title: '鍟嗗鎺掕姒?,
|
||||
title: '商家排行榜',
|
||||
sortField: 'jiesuan_jine',
|
||||
sortLabel: '鎴愪氦鎬婚',
|
||||
sortLabel: '成交总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiesuan_dingdan_shu', label: '鎴愪氦閲?, type: 'int' },
|
||||
{ field: 'paifa_dingdan_shu', label: '娲惧崟閲?, type: 'int' },
|
||||
{ field: 'paifa_jine', label: '娲惧崟棰?, type: 'money' },
|
||||
{ field: 'jiesuan_dingdan_shu', label: '成交量', type: 'int' },
|
||||
{ field: 'paifa_dingdan_shu', label: '派单量', type: 'int' },
|
||||
{ field: 'paifa_jine', label: '派单额', type: 'money' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
/** 鏄熶箣鐣屼笓鐢ㄥ睍绀轰笌鎺掑簭瀛楁 */
|
||||
/** 星之界专用展示与排序字段 */
|
||||
const XZJ_ROLE_META = {
|
||||
dashou: {
|
||||
title: '鎺ュ崟鍛樻帓琛屾',
|
||||
title: '接单员排行榜',
|
||||
sortField: 'chengjiao_zonge',
|
||||
sortLabel: '鎴愪氦閲戦',
|
||||
sortLabel: '成交金额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiedan_zongliang', label: '鎺ュ崟閲?, type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '鎺ュ崟棰?, type: 'money' },
|
||||
{ field: 'chengjiao_zongliang', label: '鎴愪氦閲?, type: 'int' },
|
||||
{ field: 'jiedan_zongliang', label: '接单量', type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '接单额', type: 'money' },
|
||||
{ field: 'chengjiao_zongliang', label: '成交量', type: 'int' },
|
||||
],
|
||||
},
|
||||
guanshi: {
|
||||
title: '绠′簨鎺掕姒?,
|
||||
title: '管事排行榜',
|
||||
sortField: 'chongzhi_dashou_shu',
|
||||
sortLabel: '鏈夋晥浜烘暟',
|
||||
sortLabel: '有效人数',
|
||||
mainType: 'int',
|
||||
metrics: [
|
||||
{ field: 'wuxiao_yaoqing_dashou_shu', label: '鏃犳晥閭€璇?, type: 'int' },
|
||||
{ field: 'yaoqing_dashou_shu', label: '閭€璇蜂汉鏁?, type: 'int' },
|
||||
{ field: 'shouru_zonge', label: '鏀剁泭鎬婚', type: 'money' },
|
||||
{ field: 'wuxiao_yaoqing_dashou_shu', label: '无效邀请', type: 'int' },
|
||||
{ field: 'yaoqing_dashou_shu', label: '邀请人数', type: 'int' },
|
||||
{ field: 'shouru_zonge', label: '收益总额', type: 'money' },
|
||||
],
|
||||
},
|
||||
zuzhang: {
|
||||
title: '缁勯暱鎺掕姒?,
|
||||
title: '组长排行榜',
|
||||
sortField: 'shouru_zonge',
|
||||
sortLabel: '鏀剁泭鎬婚',
|
||||
sortLabel: '收益总额',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'wuxiao_yaoqing_guanshi_shu', label: '鏃犳晥閭€璇?, type: 'int' },
|
||||
{ field: 'youxiao_guanshi_shu', label: '鏈夋晥浜烘暟', type: 'int' },
|
||||
{ field: 'yaoqing_guanshi_shu', label: '閭€璇蜂汉鏁?, type: 'int' },
|
||||
{ field: 'wuxiao_yaoqing_guanshi_shu', label: '无效邀请', type: 'int' },
|
||||
{ field: 'youxiao_guanshi_shu', label: '有效人数', type: 'int' },
|
||||
{ field: 'yaoqing_guanshi_shu', label: '邀请人数', type: 'int' },
|
||||
],
|
||||
},
|
||||
shangjia: {
|
||||
title: '鍟嗗鎺掕姒?,
|
||||
title: '商家排行榜',
|
||||
sortField: 'paifa_jine',
|
||||
sortLabel: '娲惧崟娴佹按',
|
||||
sortLabel: '派单流水',
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'paifa_dingdan_shu', label: '娲惧崟閲?, type: 'int' },
|
||||
{ field: 'paifa_jine', label: '娲惧崟娴佹按', type: 'money' },
|
||||
{ field: 'paifa_dingdan_shu', label: '派单量', type: 'int' },
|
||||
{ field: 'paifa_jine', label: '派单流水', type: 'money' },
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -143,11 +145,12 @@ Page({
|
||||
roleList: ROLE_LIST,
|
||||
dateList: DATE_LIST,
|
||||
currentRole: 'dashou',
|
||||
currentDate: '浠婃棩',
|
||||
currentDate: '今日',
|
||||
fanwei: 'club',
|
||||
showJituanScope: false, // 涓存椂闅愯棌闆嗗洟鎬绘锛屾仮澶嶆椂鏀?true 骞惰皟鏁?fanwei 榛樿鍊? myClubId: CLUB_ID || 'xq',
|
||||
showJituanScope: false, // 临时隐藏集团总榜,恢复时改 true 并调整 fanwei 默认值
|
||||
myClubId: CLUB_ID || 'xq',
|
||||
roleMeta: ROLE_META.dashou,
|
||||
sortLabel: '鍒嗙孩鎬婚',
|
||||
sortLabel: '分红总额',
|
||||
isLoading: true,
|
||||
rankList: [],
|
||||
topThree: [],
|
||||
@@ -183,7 +186,7 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/** 涓?app.js 涓€鑷达細ossImageUrl + morentouxiang锛岄厤缃紓姝ュ姞杞藉悗 onShow 鍐嶅悓姝?*/
|
||||
/** 与 app.js 一致:ossImageUrl + morentouxiang,配置异步加载后 onShow 再同步 */
|
||||
syncDefaultAvatar() {
|
||||
const def = getDefaultAvatar()
|
||||
if (def && def !== this.data.defaultAvatar) {
|
||||
@@ -193,7 +196,7 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
/** 閰嶇疆鏅氫簬椤甸潰鍔犺浇鏃讹紝琛ュ叏绌哄ご鍍?*/
|
||||
/** 配置晚于页面加载时,补全空头像 */
|
||||
ensureAvatars() {
|
||||
const def = getDefaultAvatar()
|
||||
if (!def) return
|
||||
@@ -223,7 +226,7 @@ Page({
|
||||
|
||||
applyRole(role, reload = true) {
|
||||
const meta = ACTIVE_ROLE_META[role]
|
||||
wx.setNavigationBarTitle({ title: '鎬绘帓琛屾' })
|
||||
wx.setNavigationBarTitle({ title: '总排行榜' })
|
||||
this.setData({ currentRole: role, roleMeta: meta, sortLabel: meta.sortLabel })
|
||||
if (reload) this.fetchRankList()
|
||||
},
|
||||
@@ -252,19 +255,19 @@ Page({
|
||||
onShowRules() {
|
||||
const r = this.data.rewardInfo
|
||||
if (!r || !r.tiers || !r.tiers.length) {
|
||||
wx.showToast({ title: '鏆傛棤濂栧姳瑙勫垯', icon: 'none' })
|
||||
wx.showToast({ title: '暂无奖励规则', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const lines = r.tiers.map((t) => {
|
||||
const label = t.label || `绗?{t.rank_from}${t.rank_to > t.rank_from ? '-' + t.rank_to : ''}鍚峘
|
||||
return `${label}锛毬?{t.amount}`
|
||||
const label = t.label || `第${t.rank_from}${t.rank_to > t.rank_from ? '-' + t.rank_to : ''}名`
|
||||
return `${label}:¥${t.amount}`
|
||||
})
|
||||
const tip = r.period_status === 'open' ? '锛堟湰鏈熻繘琛屼腑锛岀粨鏉熷悗鍙鍙栵級' : ''
|
||||
const tip = r.period_status === 'open' ? '(本期进行中,结束后可领取)' : ''
|
||||
wx.showModal({
|
||||
title: `${r.title || '鎺掕姒滃鍔?}${tip}`,
|
||||
title: `${r.title || '排行榜奖励'}${tip}`,
|
||||
content: [
|
||||
`淇变箰閮細${r.club_id || this.data.myClubId}`,
|
||||
`鎺掑簭锛?{r.sort_label || ''}`,
|
||||
`俱乐部:${r.club_id || this.data.myClubId}`,
|
||||
`排序:${r.sort_label || ''}`,
|
||||
...lines,
|
||||
r.description || '',
|
||||
].filter(Boolean).join('\n'),
|
||||
@@ -284,13 +287,13 @@ Page({
|
||||
})
|
||||
const body = res?.data || {}
|
||||
if (body.code === 200) {
|
||||
wx.showToast({ title: `宸查鍙?楼${claim.amount}`, icon: 'success' })
|
||||
wx.showToast({ title: `已领取 ¥${claim.amount}`, icon: 'success' })
|
||||
await this.fetchRewardInfo()
|
||||
} else {
|
||||
wx.showToast({ title: body.msg || '棰嗗彇澶辫触', icon: 'none' })
|
||||
wx.showToast({ title: body.msg || '领取失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '棰嗗彇澶辫触', icon: 'none' })
|
||||
wx.showToast({ title: '领取失败', icon: 'none' })
|
||||
} finally {
|
||||
this.setData({ claimLoading: false })
|
||||
}
|
||||
@@ -314,7 +317,7 @@ Page({
|
||||
const meta = ACTIVE_ROLE_META[role]
|
||||
const app = getApp()
|
||||
const uid = raw.yonghuid || raw.uid || ''
|
||||
const nicheng = raw.nicheng || raw.nick || (role === 'dashou' ? '鎵撴墜' : role === 'guanshi' ? '绠′簨' : role === 'zuzhang' ? '缁勯暱' : role === 'shangjia' ? '鍟嗗' : '鐢ㄦ埛')
|
||||
const nicheng = raw.nicheng || raw.nick || (role === 'dashou' ? '打手' : role === 'guanshi' ? '管事' : role === 'zuzhang' ? '组长' : role === 'shangjia' ? '商家' : '用户')
|
||||
const def = getDefaultAvatar(app)
|
||||
const avatar = buildAvatar(raw.touxiang || raw.avatar, app) || def
|
||||
const metrics = meta.metrics.map((m) => {
|
||||
@@ -345,7 +348,7 @@ Page({
|
||||
clubName: raw.club_name || raw.club_id || '',
|
||||
rewardAmount: raw.reward_amount || 0,
|
||||
mainType,
|
||||
mainPrefix: mainType === 'money' ? '楼' : '',
|
||||
mainPrefix: mainType === 'money' ? '¥' : '',
|
||||
mainValue,
|
||||
mainLabel: (IS_XZJ ? meta.sortLabel : (raw.metric_label || meta.sortLabel)),
|
||||
metrics,
|
||||
@@ -387,7 +390,7 @@ Page({
|
||||
restList: merged.slice(3),
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn('濂栧姳淇℃伅鍔犺浇澶辫触', e)
|
||||
console.warn('奖励信息加载失败', e)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -432,7 +435,7 @@ Page({
|
||||
|
||||
if (!Array.isArray(list)) list = []
|
||||
const role = currentRole
|
||||
// 鍚嶆椤哄簭瀹屽叏娌跨敤鍚庣杩斿洖锛屽墠绔笉鍋?sort / 閲嶆帓
|
||||
// 名次顺序完全沿用后端返回,前端不做 sort / 重排
|
||||
let normalized = list.slice(0, 50).map((item, i) => this.normalizeItem(item, i, role))
|
||||
if (rewardFromRank) {
|
||||
normalized = this.applyRewardToList(normalized, rewardFromRank, 'club')
|
||||
@@ -450,8 +453,8 @@ Page({
|
||||
await this.fetchRewardInfo()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('鎺掕姒滃姞杞藉け璐?, e)
|
||||
wx.showToast({ title: '鍔犺浇澶辫触', icon: 'none' })
|
||||
console.error('排行榜加载失败', e)
|
||||
wx.showToast({ title: '加载失败', icon: 'none' })
|
||||
this.setData({ rankList: [], topThree: [], restList: [], isEmpty: true, isLoading: false })
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
"navigationBarTitleText": "排行榜",
|
||||
"navigationBarBackgroundColor": "#0d1117",
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "总排行榜",
|
||||
"navigationBarBackgroundColor": "#FFE566",
|
||||
"navigationBarTextStyle": "black",
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundColor": "#0d1117",
|
||||
"backgroundTextStyle": "light"
|
||||
"backgroundColor": "#FFE566",
|
||||
"backgroundTextStyle": "dark"
|
||||
}
|
||||
|
||||
@@ -1,30 +1,47 @@
|
||||
<view class="rank-page">
|
||||
<image class="page-bg" src="{{imgUrls.pageBg}}" mode="aspectFill" />
|
||||
|
||||
<view class="top-bar">
|
||||
<view class="top-row">
|
||||
<text class="page-title">{{roleMeta.title}}</text>
|
||||
<view class="refresh-wrap" bindtap="onRefreshTap">
|
||||
<image class="refresh-ico" src="{{imgUrls.refreshIcon}}" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-x class="role-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view class="role-line">
|
||||
<!-- 顶区:黄底 + 白 Tab + 时间筛选 -->
|
||||
<view class="header-zone">
|
||||
<view class="role-tabs">
|
||||
<view
|
||||
wx:for="{{roleList}}" wx:key="key"
|
||||
class="role-chip {{currentRole === item.key ? 'on' : ''}}"
|
||||
class="role-tab {{currentRole === item.key ? 'active' : ''}}"
|
||||
data-role="{{item.key}}" bindtap="onRoleTap"
|
||||
>{{item.label}}</view>
|
||||
>
|
||||
<text class="role-tab-text">{{item.label}}</text>
|
||||
<view class="role-tab-line" wx:if="{{currentRole === item.key}}"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="date-grid">
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-x class="date-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view class="date-line">
|
||||
<view
|
||||
wx:for="{{dateList}}" wx:key="key"
|
||||
class="date-chip {{currentDate === item.key ? 'on' : ''}}"
|
||||
class="date-tab {{currentDate === item.key ? 'active' : ''}}"
|
||||
data-date="{{item.key}}" bindtap="onDateTap"
|
||||
>{{item.label}}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="scope-row">
|
||||
<!-- 临时隐藏集团总榜,恢复时取消 wx:if 注释 -->
|
||||
<view wx:if="{{showJituanScope}}" class="scope-tab {{fanwei === 'jituan' ? 'active' : ''}}" data-fanwei="jituan" bindtap="onScopeTap">集团总榜</view>
|
||||
<view class="scope-tab {{fanwei === 'club' ? 'active' : ''}}" data-fanwei="club" bindtap="onScopeTap">本俱乐部榜</view>
|
||||
<view class="refresh-btn" bindtap="onRefreshTap">
|
||||
<image class="refresh-ico" src="{{imgUrls.refreshIcon}}" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
<text class="sort-tip">{{currentDate}} · 按{{sortLabel}} · TOP50</text>
|
||||
|
||||
<view class="reward-banner" wx:if="{{rewardInfo && rewardInfo.scheme_enabled}}" bindtap="onShowRules">
|
||||
<view class="reward-banner-main">
|
||||
<text class="reward-tag">🏆 {{rewardInfo.title || '排行榜奖励'}}</text>
|
||||
<text class="reward-club">俱乐部 {{rewardInfo.club_id || myClubId}}</text>
|
||||
<text class="reward-status" wx:if="{{rewardInfo.period_status === 'open'}}">进行中 · 结束后可领</text>
|
||||
<text class="reward-status pending" wx:elif="{{rewardInfo.my_claim}}">待领取 ¥{{rewardInfo.my_claim.amount}}</text>
|
||||
<text class="reward-status claimed" wx:elif="{{rewardInfo.my_claimed}}">已领取 ¥{{rewardInfo.my_claimed.amount}}</text>
|
||||
</view>
|
||||
<text class="reward-link">规则 ›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="state-wrap" wx:if="{{isLoading}}">
|
||||
@@ -38,97 +55,85 @@
|
||||
</view>
|
||||
|
||||
<scroll-view wx:else scroll-y class="body-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<!-- 前三名领奖台 -->
|
||||
<view class="podium-zone" wx:if="{{topThree.length > 0}}">
|
||||
<view class="podium-row">
|
||||
|
||||
<!-- 前三名 · 纯 CSS 领奖台 -->
|
||||
<view class="podium-panel" wx:if="{{topThree.length > 0}}">
|
||||
<view class="podium-cols">
|
||||
|
||||
<!-- 榜二 -->
|
||||
<view class="p-col p-second" wx:if="{{topThree[1]}}">
|
||||
<view class="p-badge badge-silver"><text>2</text></view>
|
||||
<view class="p-avatar-wrap wrap-silver">
|
||||
<view class="podium-col col-2" wx:if="{{topThree[1]}}">
|
||||
<view class="crown crown-silver">👑</view>
|
||||
<view class="avatar-ring ring-silver">
|
||||
<image class="p-avatar" src="{{topThree[1].avatar || defaultAvatar}}" mode="aspectFill" data-zone="top" data-index="1" binderror="onAvatarError" />
|
||||
</view>
|
||||
<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-label">{{topThree[1].mainLabel}}</text>
|
||||
</view>
|
||||
<text class="p-amount">{{topThree[1].mainPrefix}}{{topThree[1].mainValue}}</text>
|
||||
<view class="p-metrics">
|
||||
<text class="p-metric" wx:for="{{topThree[1].metrics}}" wx:key="label" wx:for-item="m">{{m.label}} {{m.value}}</text>
|
||||
</view>
|
||||
<view class="p-pedestal pedestal-silver"></view>
|
||||
<text class="p-reward" wx:if="{{topThree[1].rewardAmount > 0}}">奖 ¥{{topThree[1].rewardAmount}}</text>
|
||||
<view class="pedestal pedestal-2"></view>
|
||||
</view>
|
||||
|
||||
<!-- 榜一 -->
|
||||
<view class="p-col p-first" wx:if="{{topThree[0]}}">
|
||||
<view class="p-badge badge-first"><text>1</text></view>
|
||||
<view class="p-avatar-wrap wrap-first">
|
||||
<view class="podium-col col-1" wx:if="{{topThree[0]}}">
|
||||
<view class="crown crown-gold">👑</view>
|
||||
<view class="avatar-ring ring-gold">
|
||||
<image class="p-avatar p-avatar-lg" src="{{topThree[0].avatar || defaultAvatar}}" mode="aspectFill" data-zone="top" data-index="0" binderror="onAvatarError" />
|
||||
</view>
|
||||
<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-name p-name-lg">{{topThree[0].nicheng}}</text>
|
||||
<text class="p-label">{{topThree[0].mainLabel}}</text>
|
||||
</view>
|
||||
<text class="p-amount p-amount-lg">{{topThree[0].mainPrefix}}{{topThree[0].mainValue}}</text>
|
||||
<view class="p-metrics">
|
||||
<text class="p-metric" wx:for="{{topThree[0].metrics}}" wx:key="label" wx:for-item="m">{{m.label}} {{m.value}}</text>
|
||||
</view>
|
||||
<view class="p-pedestal pedestal-first"></view>
|
||||
<text class="p-reward" wx:if="{{topThree[0].rewardAmount > 0}}">奖 ¥{{topThree[0].rewardAmount}}</text>
|
||||
<view class="pedestal pedestal-1"></view>
|
||||
</view>
|
||||
|
||||
<!-- 榜三 -->
|
||||
<view class="p-col p-third" wx:if="{{topThree[2]}}">
|
||||
<view class="p-badge badge-bronze"><text>3</text></view>
|
||||
<view class="p-avatar-wrap wrap-bronze">
|
||||
<view class="podium-col col-3" wx:if="{{topThree[2]}}">
|
||||
<view class="crown crown-bronze">👑</view>
|
||||
<view class="avatar-ring ring-bronze">
|
||||
<image class="p-avatar" src="{{topThree[2].avatar || defaultAvatar}}" mode="aspectFill" data-zone="top" data-index="2" binderror="onAvatarError" />
|
||||
</view>
|
||||
<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-label">{{topThree[2].mainLabel}}</text>
|
||||
</view>
|
||||
<text class="p-amount">{{topThree[2].mainPrefix}}{{topThree[2].mainValue}}</text>
|
||||
<view class="p-metrics">
|
||||
<text class="p-metric" wx:for="{{topThree[2].metrics}}" wx:key="label" wx:for-item="m">{{m.label}} {{m.value}}</text>
|
||||
</view>
|
||||
<view class="p-pedestal pedestal-bronze"></view>
|
||||
<text class="p-reward" wx:if="{{topThree[2].rewardAmount > 0}}">奖 ¥{{topThree[2].rewardAmount}}</text>
|
||||
<view class="pedestal pedestal-3"></view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4-50 名 -->
|
||||
<view class="list-wrap" wx:if="{{restList.length > 0}}">
|
||||
<view class="list-title-row">
|
||||
<text class="list-title">完整榜单</text>
|
||||
<text class="list-range">第 4 - {{rankList.length}} 名</text>
|
||||
</view>
|
||||
|
||||
<view class="list-card" wx:for="{{restList}}" wx:key="mingci">
|
||||
<image class="list-card-bg" src="{{imgUrls.cardBg}}" mode="aspectFill" />
|
||||
<view class="list-card-body">
|
||||
<view class="lc-rank"><text>{{item.mingci}}</text></view>
|
||||
<image class="lc-avatar" src="{{item.avatar || defaultAvatar}}" mode="aspectFill" data-zone="rest" data-index="{{index}}" binderror="onAvatarError" />
|
||||
<view class="lc-info">
|
||||
<text class="lc-name">{{item.nicheng}}</text>
|
||||
<text class="lc-uid">ID {{item.uid}}</text>
|
||||
<view class="lc-tags">
|
||||
<text class="lc-tag" wx:for="{{item.metrics}}" wx:key="label" wx:for-item="m">{{m.label}} {{m.value}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="lc-score">
|
||||
<text class="lc-money">¥{{item.mainValue}}</text>
|
||||
<text class="lc-label">{{item.mainLabel}}</text>
|
||||
<!-- 4 名以后列表 -->
|
||||
<view class="list-panel" wx:if="{{restList.length > 0}}">
|
||||
<view class="list-row" wx:for="{{restList}}" wx:key="mingci">
|
||||
<text class="li-rank">{{item.mingci}}</text>
|
||||
<image class="li-avatar" src="{{item.avatar || defaultAvatar}}" mode="aspectFill" data-zone="rest" data-index="{{index}}" binderror="onAvatarError" />
|
||||
<text class="li-name">{{item.nicheng}}</text>
|
||||
<view class="li-right">
|
||||
<text class="li-amount">{{item.mainPrefix}}{{item.mainValue}}</text>
|
||||
<text class="li-label">{{item.mainLabel}}</text>
|
||||
<view class="li-tags">
|
||||
<text class="li-tag" wx:for="{{item.metrics}}" wx:key="label" wx:for-item="m">{{m.label}} {{m.value}}</text>
|
||||
</view>
|
||||
<text class="li-reward" wx:if="{{item.rewardAmount > 0}}">奖¥{{item.rewardAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-gap"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="claim-bar" wx:if="{{showClaimBar && rewardInfo.my_claim}}">
|
||||
<view class="claim-text">
|
||||
<text class="claim-title">🎉 第{{rewardInfo.my_claim.mingci}}名 · 待领取</text>
|
||||
<text class="claim-amount">¥{{rewardInfo.my_claim.amount}}</text>
|
||||
</view>
|
||||
<button class="claim-btn" loading="{{claimLoading}}" bindtap="onClaimTap">立即领取</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
@@ -1,290 +1,434 @@
|
||||
page {
|
||||
background: #0d1117;
|
||||
color: #fff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif;
|
||||
background: #ffe566;
|
||||
color: #333;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
/* 主题色 */
|
||||
/* 选中/金额: #6BA3F7 浅蓝: #8EB8FF 银: #A8B4C4 铜: #C4A882 */
|
||||
|
||||
.rank-page {
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
background: linear-gradient(180deg, #ffe566 0%, #ffe566 420rpx, #fff8dc 520rpx, #ffffff 620rpx);
|
||||
}
|
||||
|
||||
.page-bg {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* ========== 顶栏 ========== */
|
||||
.top-bar {
|
||||
/* ========== 顶区 ========== */
|
||||
.header-zone {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
padding: 16rpx 28rpx 20rpx;
|
||||
padding-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
.role-tabs {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
background: #ffffff;
|
||||
margin: 0 0 0;
|
||||
padding: 0 8rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.role-tab {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0 18rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.role-tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.role-tab.active .role-tab-text {
|
||||
color: #1a1a1a;
|
||||
font-weight: 700;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.role-tab-line {
|
||||
position: absolute;
|
||||
bottom: 8rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 48rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 6rpx;
|
||||
background: #3d2914;
|
||||
}
|
||||
|
||||
.date-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
padding: 20rpx 0 8rpx;
|
||||
}
|
||||
|
||||
.date-line {
|
||||
display: inline-flex;
|
||||
padding: 0 20rpx;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.date-tab {
|
||||
display: inline-block;
|
||||
padding: 10rpx 22rpx;
|
||||
font-size: 26rpx;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.date-tab.active {
|
||||
color: #1a1a1a;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.date-tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
transform: translateX(-50%);
|
||||
width: 36rpx;
|
||||
height: 4rpx;
|
||||
border-radius: 4rpx;
|
||||
background: #3d2914;
|
||||
}
|
||||
|
||||
.scope-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
gap: 12rpx;
|
||||
padding: 8rpx 24rpx 12rpx;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
.scope-tab {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 22rpx;
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
border: 1rpx solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.refresh-wrap {
|
||||
width: 60rpx; height: 60rpx;
|
||||
.scope-tab.active {
|
||||
color: #1a1a1a;
|
||||
font-weight: 600;
|
||||
background: #ffffff;
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
margin-left: auto;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.14);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.refresh-ico {
|
||||
width: 32rpx; height: 32rpx;
|
||||
opacity: 0.8;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.role-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 16rpx;
|
||||
.reward-banner {
|
||||
margin: 0 24rpx 12rpx;
|
||||
padding: 18rpx 22rpx;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 1rpx solid rgba(255, 193, 7, 0.35);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.role-line {
|
||||
display: inline-flex;
|
||||
gap: 12rpx;
|
||||
.reward-banner-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
.role-chip {
|
||||
display: inline-block;
|
||||
padding: 12rpx 28rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 26rpx;
|
||||
color: rgba(255,255,255,0.65);
|
||||
background: rgba(20, 26, 40, 0.75);
|
||||
border: 1rpx solid rgba(255,255,255,0.12);
|
||||
}
|
||||
|
||||
.role-chip.on {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
background: rgba(107, 163, 247, 0.45);
|
||||
border-color: rgba(107, 163, 247, 0.65);
|
||||
box-shadow: 0 0 20rpx rgba(107, 163, 247, 0.2);
|
||||
}
|
||||
|
||||
.date-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 10rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.date-chip {
|
||||
text-align: center;
|
||||
padding: 14rpx 0;
|
||||
border-radius: 14rpx;
|
||||
font-size: 24rpx;
|
||||
color: rgba(255,255,255,0.65);
|
||||
background: rgba(20, 26, 40, 0.72);
|
||||
border: 1rpx solid rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
.date-chip.on {
|
||||
color: #A8CCFF;
|
||||
font-weight: 600;
|
||||
background: rgba(107, 163, 247, 0.35);
|
||||
border-color: rgba(107, 163, 247, 0.55);
|
||||
}
|
||||
|
||||
.sort-tip {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255,255,255,0.3);
|
||||
}
|
||||
.reward-tag { font-size: 26rpx; font-weight: 600; color: #d48806; }
|
||||
.reward-club { font-size: 22rpx; color: #666; }
|
||||
.reward-status { font-size: 22rpx; color: #999; }
|
||||
.reward-status.pending { color: #52c41a; font-weight: 600; }
|
||||
.reward-status.claimed { color: #8c8c8c; }
|
||||
.reward-link { font-size: 24rpx; color: #d48806; }
|
||||
|
||||
/* ========== 状态 ========== */
|
||||
.state-wrap {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 140rpx 0;
|
||||
color: rgba(255,255,255,0.45);
|
||||
padding: 120rpx 0;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 48rpx; height: 48rpx;
|
||||
border: 3rpx solid rgba(255,255,255,0.12);
|
||||
border-top-color: #6BA3F7;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border: 3rpx solid rgba(0, 0, 0, 0.08);
|
||||
border-top-color: #ffb800;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.7s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.state-img {
|
||||
width: 160rpx; height: 160rpx;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
margin-bottom: 20rpx;
|
||||
opacity: 0.65;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* ========== 滚动 ========== */
|
||||
/* ========== 滚动区 ========== */
|
||||
.body-scroll {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
height: calc(100vh - 300rpx);
|
||||
height: calc(100vh - 280rpx);
|
||||
}
|
||||
|
||||
/* ========== 领奖台(纯 CSS) ========== */
|
||||
.podium-panel {
|
||||
margin: 8rpx 24rpx 24rpx;
|
||||
padding: 28rpx 12rpx 0;
|
||||
border-radius: 28rpx;
|
||||
background: rgba(14, 18, 30, 0.88);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
/* ========== 领奖台 ========== */
|
||||
.podium-zone {
|
||||
padding: 16rpx 16rpx 0;
|
||||
}
|
||||
|
||||
.podium-cols {
|
||||
.podium-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.p-col {
|
||||
.podium-col {
|
||||
flex: 1;
|
||||
max-width: 220rpx;
|
||||
max-width: 230rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-first {
|
||||
max-width: 250rpx;
|
||||
margin-bottom: 16rpx;
|
||||
.col-1 {
|
||||
max-width: 260rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.p-badge {
|
||||
width: 44rpx; height: 44rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 14rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.p-first .p-badge {
|
||||
width: 52rpx; height: 52rpx;
|
||||
.crown {
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
margin-bottom: 6rpx;
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.15));
|
||||
}
|
||||
|
||||
.badge-first {
|
||||
color: #fff;
|
||||
background: linear-gradient(145deg, #6BA3F7, #4A7FD4);
|
||||
box-shadow: 0 4rpx 16rpx rgba(107, 163, 247, 0.4);
|
||||
.col-1 .crown {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.badge-silver {
|
||||
color: #fff;
|
||||
background: linear-gradient(145deg, #A8B4C4, #7A8899);
|
||||
box-shadow: 0 4rpx 12rpx rgba(168, 180, 196, 0.25);
|
||||
}
|
||||
|
||||
.badge-bronze {
|
||||
color: #fff;
|
||||
background: linear-gradient(145deg, #C4A882, #9A7858);
|
||||
box-shadow: 0 4rpx 12rpx rgba(196, 168, 130, 0.25);
|
||||
}
|
||||
|
||||
.p-avatar-wrap {
|
||||
.avatar-ring {
|
||||
border-radius: 50%;
|
||||
padding: 4rpx;
|
||||
margin-bottom: 12rpx;
|
||||
padding: 6rpx;
|
||||
margin-bottom: 10rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.wrap-first {
|
||||
padding: 5rpx;
|
||||
background: linear-gradient(145deg, #8EB8FF, #5B8FD8);
|
||||
box-shadow: 0 6rpx 24rpx rgba(107, 163, 247, 0.3);
|
||||
.ring-gold {
|
||||
padding: 8rpx;
|
||||
box-shadow: 0 0 0 4rpx #ffd700, 0 8rpx 20rpx rgba(255, 183, 0, 0.35);
|
||||
}
|
||||
|
||||
.wrap-silver {
|
||||
background: linear-gradient(145deg, #C8D0DA, #909AA8);
|
||||
.ring-silver {
|
||||
box-shadow: 0 0 0 4rpx #b8c4d4, 0 6rpx 16rpx rgba(120, 140, 180, 0.25);
|
||||
}
|
||||
|
||||
.wrap-bronze {
|
||||
background: linear-gradient(145deg, #D4B896, #A88860);
|
||||
.ring-bronze {
|
||||
box-shadow: 0 0 0 4rpx #cd9a5a, 0 6rpx 16rpx rgba(205, 127, 50, 0.25);
|
||||
}
|
||||
|
||||
.p-avatar {
|
||||
width: 100rpx; height: 100rpx;
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
background: #1a2030;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.p-avatar-lg {
|
||||
width: 120rpx; height: 120rpx;
|
||||
width: 116rpx;
|
||||
height: 116rpx;
|
||||
}
|
||||
|
||||
.p-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: rgba(255,255,255,0.92);
|
||||
color: #1a1a1a;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.p-name-first {
|
||||
.p-name-lg {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.p-uid {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
color: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
.p-amount {
|
||||
margin: 12rpx 0 10rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.p-money {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #8EB8FF;
|
||||
letter-spacing: -0.5rpx;
|
||||
}
|
||||
|
||||
.p-money-first {
|
||||
font-size: 34rpx;
|
||||
color: #A8CCFF;
|
||||
}
|
||||
|
||||
.p-label {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.p-amount {
|
||||
font-size: 34rpx;
|
||||
font-weight: 800;
|
||||
color: #ffb800;
|
||||
line-height: 1.3;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
|
||||
.p-amount-lg {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.p-sub-label {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.p-sub-value {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #ffb800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.p-sub-value-lg {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.p-reward {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
color: rgba(255,255,255,0.35);
|
||||
color: #d48806;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.pedestal {
|
||||
width: 92%;
|
||||
margin-top: 14rpx;
|
||||
border-radius: 16rpx 16rpx 0 0;
|
||||
background: linear-gradient(180deg, #ffe08a 0%, #ffd04d 100%);
|
||||
box-shadow: inset 0 2rpx 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.pedestal-1 {
|
||||
height: 88rpx;
|
||||
background: linear-gradient(180deg, #ffe08a 0%, #ffc933 100%);
|
||||
}
|
||||
|
||||
.pedestal-2 {
|
||||
height: 64rpx;
|
||||
background: linear-gradient(180deg, #ffe08a 0%, #ffd04d 100%);
|
||||
}
|
||||
|
||||
.pedestal-3 {
|
||||
height: 48rpx;
|
||||
background: linear-gradient(180deg, #ffe08a 0%, #ffd04d 100%);
|
||||
}
|
||||
|
||||
/* ========== 列表区(白底) ========== */
|
||||
.list-panel {
|
||||
margin-top: 8rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
padding: 24rpx 28rpx 0;
|
||||
min-height: 400rpx;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.list-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.li-rank {
|
||||
width: 44rpx;
|
||||
flex-shrink: 0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.li-avatar {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
margin: 0 16rpx 0 8rpx;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.li-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #1a1a1a;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.li-right {
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.li-amount {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.li-orders {
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.li-reward {
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
color: #d48806;
|
||||
}
|
||||
|
||||
.sort-tip {
|
||||
display: block;
|
||||
padding: 0 24rpx 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.p-metrics {
|
||||
@@ -292,176 +436,86 @@ page {
|
||||
flex-wrap: wrap;
|
||||
gap: 6rpx;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
min-height: 36rpx;
|
||||
margin: 8rpx 0 10rpx;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.p-metric {
|
||||
font-size: 18rpx;
|
||||
color: rgba(255,255,255,0.55);
|
||||
background: rgba(255,255,255,0.1);
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
/* 底座 · 圆角柱体,非方块 */
|
||||
.p-pedestal {
|
||||
width: 88%;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-pedestal::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 10%; right: 10%;
|
||||
height: 2rpx;
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
.pedestal-first {
|
||||
height: 100rpx;
|
||||
background: linear-gradient(180deg, rgba(107,163,247,0.55) 0%, rgba(107,163,247,0.22) 100%);
|
||||
border: 1rpx solid rgba(107,163,247,0.35);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.pedestal-silver {
|
||||
height: 72rpx;
|
||||
background: linear-gradient(180deg, rgba(168,180,196,0.45) 0%, rgba(168,180,196,0.18) 100%);
|
||||
border: 1rpx solid rgba(168,180,196,0.28);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.pedestal-bronze {
|
||||
height: 56rpx;
|
||||
background: linear-gradient(180deg, rgba(196,168,130,0.45) 0%, rgba(196,168,130,0.18) 100%);
|
||||
border: 1rpx solid rgba(196,168,130,0.28);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* ========== 列表 ========== */
|
||||
.list-wrap {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.list-title-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
padding: 8rpx 8rpx 20rpx;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: rgba(255,255,255,0.85);
|
||||
}
|
||||
|
||||
.list-range {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.list-card {
|
||||
position: relative;
|
||||
margin-bottom: 14rpx;
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
.list-card-bg {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.list-card-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 22rpx;
|
||||
background: rgba(12, 16, 28, 0.92);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.lc-rank {
|
||||
width: 48rpx;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lc-rank text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: rgba(255,255,255,0.18);
|
||||
}
|
||||
|
||||
.lc-avatar {
|
||||
width: 80rpx; height: 80rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
margin: 0 16rpx 0 6rpx;
|
||||
border: 2rpx solid rgba(107, 163, 247, 0.2);
|
||||
background: #1a2030;
|
||||
}
|
||||
|
||||
.lc-info { flex: 1; min-width: 0; }
|
||||
|
||||
.lc-name {
|
||||
.li-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: rgba(255,255,255,0.92);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.lc-uid {
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
margin-top: 2rpx;
|
||||
font-size: 22rpx;
|
||||
color: rgba(255,255,255,0.32);
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.lc-tags {
|
||||
.li-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
margin-top: 8rpx;
|
||||
gap: 6rpx;
|
||||
justify-content: flex-end;
|
||||
margin-top: 6rpx;
|
||||
max-width: 280rpx;
|
||||
}
|
||||
|
||||
.lc-tag {
|
||||
.li-tag {
|
||||
font-size: 20rpx;
|
||||
color: rgba(255,255,255,0.5);
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 2rpx 10rpx;
|
||||
color: #888;
|
||||
background: #f5f5f5;
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.lc-score {
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
margin-left: 10rpx;
|
||||
.bottom-gap {
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.lc-money {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
/* ========== 领取栏 ========== */
|
||||
.claim-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
padding: 20rpx 28rpx calc(20rpx + env(safe-area-inset-bottom));
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.claim-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.claim-title {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.claim-amount {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #8EB8FF;
|
||||
color: #ffb800;
|
||||
}
|
||||
|
||||
.lc-label {
|
||||
display: block;
|
||||
margin-top: 2rpx;
|
||||
font-size: 20rpx;
|
||||
color: rgba(255,255,255,0.32);
|
||||
.claim-btn {
|
||||
margin: 0;
|
||||
padding: 0 40rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
font-size: 28rpx;
|
||||
background: linear-gradient(135deg, #ffc107, #ffb300);
|
||||
color: #1a1a1a;
|
||||
border-radius: 999rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.bottom-gap { height: 48rpx; }
|
||||
|
||||
368
pages/fighter-recharge/fighter-deposit.js
Normal file
368
pages/fighter-recharge/fighter-deposit.js
Normal file
@@ -0,0 +1,368 @@
|
||||
// pages/fighter-recharge/fighter-deposit.js — 平台履约金/保证金独立页
|
||||
import request from '../../utils/request';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
import { ICON_KEYS, resolveConfiguredIcon, resolveMiniappIcon, ICON_FOLDER, refreshPindaoConfig } from '../../utils/miniapp-icons.js';
|
||||
import { resolveAvatarFromStorage, onAvatarImageError } from '../../utils/avatar.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
const DEFAULT_RULES = [
|
||||
'履约金有效期为永久有效',
|
||||
'超过15天未接单,可联系客服申请退还',
|
||||
'缴纳后可优先接锁单/平台单等待遇',
|
||||
];
|
||||
|
||||
const DEFAULT_BENEFITS = [
|
||||
{ label: '平台订单优先', key: ICON_KEYS.FIGHTER_DEPOSIT_BENEFIT_1 },
|
||||
{ label: '快人一步', key: ICON_KEYS.FIGHTER_DEPOSIT_BENEFIT_2 },
|
||||
{ label: '优质接单', key: ICON_KEYS.FIGHTER_DEPOSIT_BENEFIT_3 },
|
||||
{ label: '解锁平台单', key: ICON_KEYS.FIGHTER_DEPOSIT_BENEFIT_4 },
|
||||
];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
imgUrls: { pageBg: '', hero: '' },
|
||||
avatarUrl: '',
|
||||
nickname: '',
|
||||
uid: '',
|
||||
yajin: 0,
|
||||
yajinDisplay: '0.00',
|
||||
yajinActive: false,
|
||||
depositRules: DEFAULT_RULES,
|
||||
depositBenefits: DEFAULT_BENEFITS,
|
||||
agreed: false,
|
||||
payAmountLabel: '',
|
||||
yajinAmount: '100',
|
||||
quickAmounts: ['100', '500', '1000', '5000'],
|
||||
showAmountModal: false,
|
||||
showPayMethodModal: false,
|
||||
showBalanceModal: false,
|
||||
showConfirmModal: false,
|
||||
balanceOptions: [],
|
||||
selectedBalanceId: null,
|
||||
selectedBalanceInfo: null,
|
||||
currentYajinAmount: null,
|
||||
currentDingdanid: '',
|
||||
payLoading: false,
|
||||
loadingText: '处理中...',
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this._jumpParams = options || {};
|
||||
this.initImages();
|
||||
this.loadUserInfo();
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.selectComponent('#popupNotice')?.cleanup?.();
|
||||
},
|
||||
|
||||
async onShow() {
|
||||
const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
|
||||
if (!phoneOk) return;
|
||||
this.registerNotification();
|
||||
this.loadYajin();
|
||||
this.refreshConfiguredIcons();
|
||||
PopupService.checkAndShow(this, 'dashouchongzhi');
|
||||
const preset = this._jumpParams?.presetAmount || this._jumpParams?.requiredYajin;
|
||||
if (preset && parseFloat(preset) > 0) {
|
||||
this.setData({ yajinAmount: String(parseInt(preset, 10)) });
|
||||
}
|
||||
},
|
||||
|
||||
registerNotification() {
|
||||
const comp = this.selectComponent('#global-notification');
|
||||
if (comp?.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => comp.showNotification(data),
|
||||
hide: () => comp.hideNotification(),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
initImages() {
|
||||
const fb = (key) => `${ICON_FOLDER}/${key}.png`;
|
||||
const depositBenefits = DEFAULT_BENEFITS.map((item) => ({
|
||||
label: item.label,
|
||||
iconUrl: resolveMiniappIcon(app, item.key, fb(item.key)),
|
||||
}));
|
||||
this.setData({
|
||||
imgUrls: {
|
||||
pageBg: resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_DEPOSIT_BG),
|
||||
hero: resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_DEPOSIT_HERO),
|
||||
},
|
||||
depositBenefits,
|
||||
});
|
||||
},
|
||||
|
||||
async refreshConfiguredIcons() {
|
||||
try {
|
||||
await refreshPindaoConfig(app);
|
||||
} catch (e) {
|
||||
console.warn('refreshConfiguredIcons failed', e);
|
||||
}
|
||||
this.initImages();
|
||||
this.loadUserInfo();
|
||||
},
|
||||
|
||||
loadUserInfo() {
|
||||
const gd = app.globalData || {};
|
||||
this.setData({
|
||||
avatarUrl: resolveAvatarFromStorage(app),
|
||||
nickname: gd.dashouNicheng || gd.nicheng || '打手',
|
||||
uid: wx.getStorageSync('uid') || gd.uid || '',
|
||||
});
|
||||
},
|
||||
|
||||
onAvatarError() {
|
||||
onAvatarImageError(this, 'avatarUrl', app);
|
||||
},
|
||||
|
||||
loadYajin() {
|
||||
const yajin = parseFloat(app.globalData.yajin || 0) || 0;
|
||||
this.setData({
|
||||
yajin,
|
||||
yajinDisplay: yajin.toFixed(2),
|
||||
yajinActive: yajin > 0,
|
||||
});
|
||||
},
|
||||
|
||||
toggleAgree() {
|
||||
this.setData({ agreed: !this.data.agreed });
|
||||
},
|
||||
|
||||
onTapPay() {
|
||||
if (!this.data.agreed) {
|
||||
wx.showToast({ title: '请先阅读并同意缴纳规则', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ showAmountModal: true });
|
||||
},
|
||||
|
||||
hideAmountModal() {
|
||||
this.setData({ showAmountModal: false });
|
||||
},
|
||||
|
||||
onYajinInput(e) {
|
||||
let v = (e.detail.value || '').replace(/[^\d]/g, '');
|
||||
if (v) {
|
||||
const n = parseInt(v, 10);
|
||||
if (n > 10000) v = '10000';
|
||||
if (n < 1) v = '1';
|
||||
}
|
||||
this.setData({ yajinAmount: v });
|
||||
},
|
||||
|
||||
setQuickAmount(e) {
|
||||
this.setData({ yajinAmount: e.currentTarget.dataset.amount });
|
||||
},
|
||||
|
||||
onConfirmAmount() {
|
||||
const amount = this.data.yajinAmount;
|
||||
if (!amount || amount < 1 || amount > 10000) {
|
||||
wx.showToast({ title: '请输入1-10000元', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.hideAmountModal();
|
||||
this.setData({
|
||||
currentYajinAmount: amount,
|
||||
payAmountLabel: `(¥ ${amount})`,
|
||||
showPayMethodModal: true,
|
||||
});
|
||||
},
|
||||
|
||||
hidePayMethodModal() {
|
||||
this.setData({ showPayMethodModal: false });
|
||||
},
|
||||
|
||||
onPayMethodSelect(e) {
|
||||
const method = e.currentTarget.dataset.method;
|
||||
this.hidePayMethodModal();
|
||||
if (method === 'wx') {
|
||||
this.setData({ yajinAmount: this.data.currentYajinAmount }, () => this.handleYajinPay());
|
||||
} else {
|
||||
this.fetchBalanceOptions();
|
||||
}
|
||||
},
|
||||
|
||||
async fetchBalanceOptions() {
|
||||
this.showLoading('获取抵扣信息...');
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/czhqdy',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: 2,
|
||||
yajin_jine: parseInt(this.data.currentYajinAmount, 10),
|
||||
},
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
this.setData({ balanceOptions: res.data.data || [], showBalanceModal: true });
|
||||
} else {
|
||||
wx.showToast({ title: res.data.message || '获取失败', icon: 'none' });
|
||||
}
|
||||
} catch (err) {
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
hideBalanceModal() {
|
||||
this.setData({ showBalanceModal: false });
|
||||
},
|
||||
|
||||
onBalanceSelect(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const selected = this.data.balanceOptions.find((o) => o.id === id);
|
||||
if (!selected) return;
|
||||
this.setData({
|
||||
selectedBalanceId: id,
|
||||
selectedBalanceInfo: selected,
|
||||
showConfirmModal: true,
|
||||
showBalanceModal: false,
|
||||
});
|
||||
},
|
||||
|
||||
hideConfirmModal() {
|
||||
this.setData({ showConfirmModal: false });
|
||||
},
|
||||
|
||||
async confirmBalancePay() {
|
||||
const { selectedBalanceId, currentYajinAmount } = this.data;
|
||||
if (!selectedBalanceId) return;
|
||||
this.hideConfirmModal();
|
||||
this.showLoading('抵扣支付中...');
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/dsqrgmdh',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: 2,
|
||||
shenfen_id: selectedBalanceId,
|
||||
yajin_jine: parseInt(currentYajinAmount, 10),
|
||||
},
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
const data = res.data.data || {};
|
||||
if (data.yajin !== undefined) app.globalData.yajin = data.yajin;
|
||||
this.loadYajin();
|
||||
wx.showToast({ title: '缴纳成功', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: res.data.message || '购买失败', icon: 'none' });
|
||||
}
|
||||
} catch (err) {
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
confirmWechatPurchase() {
|
||||
return new Promise((resolve) => {
|
||||
wx.showModal({
|
||||
title: '确认微信支付',
|
||||
content: '您即将通过微信支付缴纳履约金。购买成功后不可随意退款,请确认无误后再继续。',
|
||||
confirmText: '确认购买',
|
||||
cancelText: '再想想',
|
||||
success: (res) => resolve(!!res.confirm),
|
||||
fail: () => resolve(false),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async handleYajinPay() {
|
||||
const amount = this.data.yajinAmount;
|
||||
if (!amount || amount < 1 || amount > 10000) {
|
||||
wx.showToast({ title: '请输入1-10000元', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (!(await this.confirmWechatPurchase())) return;
|
||||
this.showLoading('发起支付中...');
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/yajingoumai',
|
||||
method: 'POST',
|
||||
data: { jine: parseInt(amount, 10) },
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
this.setData({ currentDingdanid: res.data.dingdanid });
|
||||
await this.wechatPay(res.data.payParams);
|
||||
} else {
|
||||
wx.showToast({ title: res.data.message || '支付发起失败', icon: 'none' });
|
||||
}
|
||||
} catch (err) {
|
||||
wx.showToast({ title: '支付失败', icon: 'none' });
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
wechatPay(payParams) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.requestPayment({
|
||||
timeStamp: payParams.timeStamp,
|
||||
nonceStr: payParams.nonceStr,
|
||||
package: payParams.package,
|
||||
signType: payParams.signType || 'MD5',
|
||||
paySign: payParams.paySign,
|
||||
success: async () => {
|
||||
wx.showToast({ title: '支付成功!确认中...', icon: 'none' });
|
||||
try {
|
||||
await this.startPolling();
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
fail: async (res) => {
|
||||
wx.showToast({ title: res.errMsg?.includes('cancel') ? '已取消支付' : '支付失败', icon: 'none' });
|
||||
try {
|
||||
await request({
|
||||
url: '/shangpin/yajinshibai',
|
||||
method: 'POST',
|
||||
data: { dingdanid: this.data.currentDingdanid },
|
||||
});
|
||||
} catch (e) { /* ignore */ }
|
||||
reject(new Error(res.errMsg));
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async startPolling() {
|
||||
const dingdanid = this.data.currentDingdanid;
|
||||
if (!dingdanid) return;
|
||||
this.showLoading('确认支付结果...');
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/yajinlunxun',
|
||||
method: 'POST',
|
||||
data: { dingdanid },
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
if (res.data.yajin !== undefined) app.globalData.yajin = res.data.yajin;
|
||||
this.loadYajin();
|
||||
this.hideLoading();
|
||||
wx.showToast({ title: '履约金缴纳成功', icon: 'success' });
|
||||
return;
|
||||
}
|
||||
} catch (e) { /* retry */ }
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
}
|
||||
this.hideLoading();
|
||||
wx.showToast({ title: '确认超时,请稍后查看余额', icon: 'none' });
|
||||
},
|
||||
|
||||
showLoading(text) {
|
||||
this.setData({ payLoading: true, loadingText: text || '处理中...' });
|
||||
},
|
||||
|
||||
hideLoading() {
|
||||
this.setData({ payLoading: false });
|
||||
},
|
||||
|
||||
stopPropagation() {},
|
||||
});
|
||||
10
pages/fighter-recharge/fighter-deposit.json
Normal file
10
pages/fighter-recharge/fighter-deposit.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"popup-notice": "/components/popup-notice/popup-notice"
|
||||
},
|
||||
"navigationBarTitleText": "平台履约金",
|
||||
"navigationBarBackgroundColor": "#FFF9F0",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#FFF9F0"
|
||||
}
|
||||
133
pages/fighter-recharge/fighter-deposit.wxml
Normal file
133
pages/fighter-recharge/fighter-deposit.wxml
Normal file
@@ -0,0 +1,133 @@
|
||||
<!-- 平台履约金 / 保证金充值页 -->
|
||||
<view class="deposit-page">
|
||||
<image wx:if="{{imgUrls.pageBg}}" class="page-bg" src="{{imgUrls.pageBg}}" mode="widthFix" />
|
||||
<view class="page-bg-fallback" wx:else></view>
|
||||
|
||||
<view class="page-body">
|
||||
<view class="user-row">
|
||||
<image class="user-avatar" src="{{avatarUrl}}" mode="aspectFill" binderror="onAvatarError" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{nickname || '打手'}}</text>
|
||||
<text class="user-id">会员ID: {{uid || '--'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="deposit-card">
|
||||
<view class="deposit-card-left">
|
||||
<text class="deposit-card-title">平台接单履约金</text>
|
||||
<view class="deposit-price-row">
|
||||
<text class="deposit-currency">¥</text>
|
||||
<text class="deposit-price">{{yajinDisplay}}</text>
|
||||
</view>
|
||||
<text class="deposit-validity">有效期:永久有效</text>
|
||||
</view>
|
||||
<view class="deposit-card-right">
|
||||
<view class="icon-clip icon-clip-deposit" wx:if="{{imgUrls.hero}}">
|
||||
<image class="icon-clip-img" src="{{imgUrls.hero}}" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="deposit-status">{{yajinActive ? '已缴纳' : '未激活'}}</view>
|
||||
</view>
|
||||
<view class="deposit-shimmer"></view>
|
||||
</view>
|
||||
|
||||
<view class="section-card">
|
||||
<text class="section-title">履约金规则</text>
|
||||
<view class="rule-list">
|
||||
<view wx:for="{{depositRules}}" wx:key="index" class="rule-item">
|
||||
<text class="rule-num">{{index + 1}}.</text>
|
||||
<text class="rule-text">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-card">
|
||||
<text class="section-title">平台履约金权益</text>
|
||||
<view class="benefit-grid">
|
||||
<view wx:for="{{depositBenefits}}" wx:key="label" class="benefit-item">
|
||||
<view wx:if="{{item.iconUrl}}" class="icon-clip icon-clip-deposit-benefit">
|
||||
<image class="icon-clip-img" src="{{item.iconUrl}}" mode="aspectFill" />
|
||||
</view>
|
||||
<text>{{item.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer-bar">
|
||||
<view class="agree-row" bindtap="toggleAgree">
|
||||
<view class="agree-check {{agreed ? 'agree-check--on' : ''}}">{{agreed ? '✓' : ''}}</view>
|
||||
<text class="agree-text">已阅读并同意</text>
|
||||
<text class="agree-link">《平台履约金缴纳规则》</text>
|
||||
</view>
|
||||
<view class="pay-btn {{agreed ? '' : 'pay-btn--disabled'}}" bindtap="onTapPay">
|
||||
<text>缴纳履约金{{payAmountLabel}}</text>
|
||||
<view class="pay-btn-shimmer"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 金额输入弹窗 -->
|
||||
<view class="modal-mask" wx:if="{{showAmountModal}}" bindtap="hideAmountModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">缴纳履约金</text>
|
||||
<text class="modal-sub">支持 1-10000 元整数</text>
|
||||
<view class="amount-input-wrap">
|
||||
<text class="amount-prefix">¥</text>
|
||||
<input class="amount-input" type="digit" value="{{yajinAmount}}" bindinput="onYajinInput" focus="{{showAmountModal}}" />
|
||||
</view>
|
||||
<view class="quick-row">
|
||||
<view wx:for="{{quickAmounts}}" wx:key="*this" class="quick-chip {{yajinAmount == item ? 'active' : ''}}" bindtap="setQuickAmount" data-amount="{{item}}">{{item}}元</view>
|
||||
</view>
|
||||
<view class="modal-actions">
|
||||
<view class="modal-btn cancel" bindtap="hideAmountModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="onConfirmAmount">下一步</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<view class="modal-mask" wx:if="{{showPayMethodModal}}" bindtap="hidePayMethodModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">选择支付方式</text>
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="wx">
|
||||
<text class="pay-method-name">微信支付</text>
|
||||
<text class="pay-method-desc">安全快捷</text>
|
||||
</view>
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="balance">
|
||||
<text class="pay-method-name">余额抵扣</text>
|
||||
<text class="pay-method-desc">使用佣金或分红</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-mask" wx:if="{{showBalanceModal}}" bindtap="hideBalanceModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">选择抵扣身份</text>
|
||||
<view wx:for="{{balanceOptions}}" wx:key="id" class="balance-item" bindtap="onBalanceSelect" data-id="{{item.id}}">
|
||||
<text>{{item.jieshao}}</text>
|
||||
<text class="balance-need">需¥{{item.xuyao}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-mask" wx:if="{{showConfirmModal}}" bindtap="hideConfirmModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">确认抵扣</text>
|
||||
<view class="confirm-row"><text>身份</text><text>{{selectedBalanceInfo.jieshao}}</text></view>
|
||||
<view class="confirm-row"><text>金额</text><text>¥{{selectedBalanceInfo.xuyao}}</text></view>
|
||||
<view class="modal-actions">
|
||||
<view class="modal-btn cancel" bindtap="hideConfirmModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="confirmBalancePay">确认支付</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="loading-mask" wx:if="{{payLoading}}">
|
||||
<view class="loading-box">
|
||||
<view class="loading-spinner"></view>
|
||||
<text>{{loadingText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<global-notification id="global-notification" />
|
||||
<popup-notice id="popupNotice" />
|
||||
279
pages/fighter-recharge/fighter-deposit.wxss
Normal file
279
pages/fighter-recharge/fighter-deposit.wxss
Normal file
@@ -0,0 +1,279 @@
|
||||
@import '../../styles/xym-recharge-shared.wxss';
|
||||
|
||||
.deposit-page {
|
||||
min-height: 100vh;
|
||||
background: #fff9f0;
|
||||
padding-bottom: calc(220rpx + env(safe-area-inset-bottom));
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.page-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.page-bg-fallback {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 360rpx;
|
||||
background: linear-gradient(180deg, #ffe082 0%, #fff9f0 100%);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 24rpx 30rpx 0;
|
||||
}
|
||||
|
||||
.user-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.deposit-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 28rpx;
|
||||
background: linear-gradient(135deg, #fff8e1 0%, #ffe0b2 100%);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(255, 183, 77, 0.25);
|
||||
}
|
||||
|
||||
.deposit-card-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.deposit-card-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
.deposit-price-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin: 16rpx 0 8rpx;
|
||||
}
|
||||
|
||||
.deposit-currency {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.deposit-price {
|
||||
font-size: 64rpx;
|
||||
font-weight: 800;
|
||||
color: #222;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.deposit-validity {
|
||||
font-size: 24rpx;
|
||||
color: #947641;
|
||||
}
|
||||
|
||||
.deposit-card-right {
|
||||
position: relative;
|
||||
width: 200rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon-clip {
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.icon-clip-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-clip-deposit {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
.icon-clip-deposit-benefit {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 14rpx;
|
||||
}
|
||||
|
||||
.deposit-status {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.deposit-shimmer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, 0.45) 50%, transparent 60%);
|
||||
animation: card-shimmer 3s infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.benefit-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20rpx 16rpx;
|
||||
}
|
||||
|
||||
.benefit-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
font-size: 26rpx;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20rpx 30rpx calc(20rpx + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 249, 240, 0.96);
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.agree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.agree-check {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #ccc;
|
||||
margin-right: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.agree-check--on {
|
||||
background: #f5a623;
|
||||
border-color: #f5a623;
|
||||
}
|
||||
|
||||
.agree-link {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.pay-btn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
background: linear-gradient(90deg, #fae04d, #ffc0a3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #492f00;
|
||||
box-shadow: 0 8rpx 20rpx rgba(245, 166, 35, 0.35);
|
||||
}
|
||||
|
||||
.pay-btn--disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.pay-btn-shimmer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, 0.5) 50%, transparent 60%);
|
||||
animation: btn-shimmer 2.5s infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes card-shimmer {
|
||||
0% { transform: translateX(-120%); }
|
||||
100% { transform: translateX(120%); }
|
||||
}
|
||||
|
||||
@keyframes btn-shimmer {
|
||||
0% { transform: translateX(-120%); }
|
||||
100% { transform: translateX(120%); }
|
||||
}
|
||||
@@ -1,72 +1,57 @@
|
||||
// pages/dashou-chongzhi/index.js
|
||||
// pages/fighter-recharge/fighter-recharge.js
|
||||
import request from '../../utils/request';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { ensurePhoneAuth } from '../../utils/phone-auth.js';
|
||||
import { ICON_KEYS, resolveConfiguredIcon, resolveMiniappIcon, ICON_FOLDER, refreshPindaoConfig } from '../../utils/miniapp-icons.js';
|
||||
import { resolveAvatarFromStorage, onAvatarImageError } from '../../utils/avatar.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
const PLATFORM_ADVANTAGE_META = [
|
||||
{ key: ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_1, title: '订单丰富', desc: '平台单、商家单多种类型' },
|
||||
{ key: ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_2, title: '俱乐部专属', desc: '按俱乐部独立配置权益' },
|
||||
{ key: ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_3, title: '客服支持', desc: '遇到问题随时联系客服' },
|
||||
{ key: ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_4, title: '安全可靠', desc: '资金与订单平台保障' },
|
||||
];
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// ========== 全局数据 ==========
|
||||
clubmber: [], // 会员列表(注意:全局变量中是 clumber,这里保持原样)
|
||||
yajin: 0, // 押金
|
||||
jifen: 0, // 积分(注意:全局变量中是 jinfen)
|
||||
jifenProgress: 0, // 积分进度条角度
|
||||
|
||||
// ========== 会员商品列表 ==========
|
||||
huiyuanList: [], // 从后端获取的会员列表
|
||||
currentHuiyuanIndex: 0, // 当前选中的会员索引
|
||||
currentHuiyuan: {}, // 当前选中的会员详情
|
||||
|
||||
// ========== 押金充值相关 ==========
|
||||
showYajinModal: false, // 显示押金弹窗
|
||||
yajinAmount: '100', // 押金金额
|
||||
|
||||
// ========== 支付相关 ==========
|
||||
payLoading: false, // 支付加载状态
|
||||
imgUrls: { pageBg: '' },
|
||||
avatarUrl: '',
|
||||
nickname: '',
|
||||
uid: '',
|
||||
memberBadge: '会员',
|
||||
agreed: false,
|
||||
memberBenefits: [],
|
||||
platformAdvantages: [],
|
||||
clubmber: [],
|
||||
jifen: 0,
|
||||
huiyuanList: [],
|
||||
currentHuiyuanIndex: 0,
|
||||
currentHuiyuan: {},
|
||||
payLoading: false,
|
||||
loadingText: '支付中...',
|
||||
|
||||
// ========== 订单ID记录 ==========
|
||||
currentDingdanid: '', // 当前操作的订单ID
|
||||
|
||||
// ========== 计算高度 ==========
|
||||
huiyuanListHeight: 120,
|
||||
|
||||
// ========== 会员详情规则 ==========
|
||||
currentDingdanid: '',
|
||||
detailRules: [],
|
||||
|
||||
// ========== 新增:跳转参数处理 ==========
|
||||
// 用于接收从其他页面跳转过来的参数,决定是否自动滚动
|
||||
jumpParams: {},
|
||||
|
||||
// ========== 新增:支付方式选择 ==========
|
||||
showPayMethodModal: false, // 支付方式选择弹窗
|
||||
currentBuyType: null, // 当前购买类型:1会员 2押金 3积分
|
||||
currentHuiyuanId: null, // 当前选中的会员ID(用于会员购买)
|
||||
currentBuyIsTrial: false, // 正式/体验:仅体验禁余额抵扣
|
||||
currentYajinAmount: null, // 当前押金金额(用于押金购买)
|
||||
|
||||
// ========== 新增:余额抵扣身份选择 ==========
|
||||
showBalanceModal: false, // 余额抵扣身份选择弹窗
|
||||
balanceOptions: [], // 后端返回的身份列表
|
||||
selectedBalanceId: null, // 选中的身份ID
|
||||
|
||||
// ========== 新增:余额抵扣确认弹窗 ==========
|
||||
showConfirmModal: false, // 余额抵扣确认弹窗
|
||||
selectedBalanceInfo: null, // 选中的身份详情(用于确认弹窗)
|
||||
|
||||
// ========== 新增:会员详情弹窗 ==========
|
||||
showPayMethodModal: false,
|
||||
currentBuyType: null,
|
||||
currentHuiyuanId: null,
|
||||
currentBuyIsTrial: false,
|
||||
showBalanceModal: false,
|
||||
balanceOptions: [],
|
||||
selectedBalanceId: null,
|
||||
showConfirmModal: false,
|
||||
selectedBalanceInfo: null,
|
||||
showMemberModal: false,
|
||||
currentMemberDetail: null,
|
||||
},
|
||||
|
||||
// ========== 生命周期函数 ==========
|
||||
onLoad(options) {
|
||||
//console.log('页面加载,跳转参数:', options);
|
||||
|
||||
// ✅ 新增:保存跳转参数
|
||||
this.setData({
|
||||
jumpParams: options || {}
|
||||
});
|
||||
|
||||
this.setData({ jumpParams: options || {} });
|
||||
this.initImages();
|
||||
this.loadUserInfo();
|
||||
this.initPage();
|
||||
},
|
||||
|
||||
@@ -86,6 +71,7 @@ onHide() {
|
||||
this.registerNotificationComponent();
|
||||
this.loadFromGlobalData();
|
||||
this.checkAndRefreshData();
|
||||
this.refreshConfiguredIcons();
|
||||
|
||||
// 调用统一弹窗组件检查并展示公告
|
||||
PopupService.checkAndShow(this, 'dashouchongzhi');
|
||||
@@ -112,16 +98,139 @@ onHide() {
|
||||
|
||||
// ========== 初始化页面 ==========
|
||||
async initPage() {
|
||||
// 从全局数据加载
|
||||
this.loadFromGlobalData();
|
||||
|
||||
// 获取会员商品列表
|
||||
await this.fetchHuiyuanList();
|
||||
|
||||
// 计算会员列表高度
|
||||
this.calculateHuiyuanHeight();
|
||||
},
|
||||
|
||||
initImages() {
|
||||
this.setData({
|
||||
imgUrls: {
|
||||
pageBg: resolveConfiguredIcon(app, ICON_KEYS.FIGHTER_RECHARGE_VIP_BG),
|
||||
},
|
||||
platformAdvantages: this.buildPlatformAdvantages(),
|
||||
});
|
||||
},
|
||||
|
||||
async refreshConfiguredIcons() {
|
||||
try {
|
||||
await refreshPindaoConfig(app);
|
||||
} catch (e) {
|
||||
console.warn('refreshConfiguredIcons failed', e);
|
||||
}
|
||||
this.initImages();
|
||||
const list = this.data.huiyuanList || [];
|
||||
const idx = this.data.currentHuiyuanIndex || 0;
|
||||
if (list.length) {
|
||||
this.applyCurrentHuiyuan(list[idx] || {}, idx);
|
||||
}
|
||||
this.loadUserInfo();
|
||||
},
|
||||
|
||||
_icon(key, fallbackRel) {
|
||||
return resolveMiniappIcon(app, key, fallbackRel);
|
||||
},
|
||||
|
||||
buildPlatformAdvantages() {
|
||||
const fb = (name) => `${ICON_FOLDER}/${name}.png`;
|
||||
const keys = [
|
||||
ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_1,
|
||||
ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_2,
|
||||
ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_3,
|
||||
ICON_KEYS.FIGHTER_RECHARGE_ADVANTAGE_4,
|
||||
];
|
||||
return PLATFORM_ADVANTAGE_META.map((item, i) => ({
|
||||
title: item.title,
|
||||
desc: item.desc,
|
||||
iconUrl: this._icon(keys[i], fb(keys[i])),
|
||||
}));
|
||||
},
|
||||
|
||||
loadUserInfo() {
|
||||
const gd = app.globalData || {};
|
||||
this.setData({
|
||||
avatarUrl: resolveAvatarFromStorage(app),
|
||||
nickname: gd.dashouNicheng || gd.nicheng || '打手',
|
||||
uid: wx.getStorageSync('uid') || gd.uid || '',
|
||||
});
|
||||
},
|
||||
|
||||
onAvatarError() {
|
||||
onAvatarImageError(this, 'avatarUrl', app);
|
||||
},
|
||||
|
||||
resolveOssUrl(path) {
|
||||
if (!path) return '';
|
||||
if (path.startsWith('http')) return path;
|
||||
return (app.globalData.ossImageUrl || '') + path;
|
||||
},
|
||||
|
||||
buildMemberBenefits(huiyuan = {}) {
|
||||
const name = huiyuan.mingzi || '会员';
|
||||
const formalPrice = huiyuan.jiage || '0';
|
||||
const trialPrice = huiyuan.trial_price || '0';
|
||||
const fb = (name) => `${ICON_FOLDER}/${name}.png`;
|
||||
const meta = [
|
||||
{
|
||||
key: ICON_KEYS.FIGHTER_RECHARGE_BENEFIT_1,
|
||||
title: '充值立得',
|
||||
desc: `开通${name}正式版 ¥${formalPrice},享优先接单`,
|
||||
},
|
||||
{
|
||||
key: ICON_KEYS.FIGHTER_RECHARGE_BENEFIT_2,
|
||||
title: '体验尝鲜',
|
||||
desc: huiyuan.can_buy_trial
|
||||
? `体验版 ¥${trialPrice}/${huiyuan.trial_days || 0}天,终身1次`
|
||||
: '正式会员支持余额抵扣',
|
||||
},
|
||||
{
|
||||
key: ICON_KEYS.FIGHTER_RECHARGE_BENEFIT_3,
|
||||
title: '专属特权',
|
||||
desc: '解锁平台单、优质接单等待遇',
|
||||
},
|
||||
];
|
||||
return meta.map((item) => ({
|
||||
title: item.title,
|
||||
desc: item.desc,
|
||||
iconUrl: this._icon(item.key, fb(item.key)),
|
||||
}));
|
||||
},
|
||||
|
||||
applyCurrentHuiyuan(huiyuan, index) {
|
||||
this.setData({
|
||||
currentHuiyuanIndex: index,
|
||||
currentHuiyuan: huiyuan,
|
||||
detailRules: this.buildDetailRules(huiyuan),
|
||||
memberBenefits: this.buildMemberBenefits(huiyuan),
|
||||
memberBadge: huiyuan.mingzi || '会员',
|
||||
});
|
||||
},
|
||||
|
||||
toggleAgree() {
|
||||
this.setData({ agreed: !this.data.agreed });
|
||||
},
|
||||
|
||||
goToDeposit() {
|
||||
wx.navigateTo({ url: '/pages/fighter-recharge/fighter-deposit' });
|
||||
},
|
||||
|
||||
onTapTrial(e) {
|
||||
if (!this.data.agreed) {
|
||||
wx.showToast({ title: '请先阅读并同意会员协议', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.onBuyHuiyuanClick(e);
|
||||
},
|
||||
|
||||
onTapFormal(e) {
|
||||
if (!this.data.agreed) {
|
||||
wx.showToast({ title: '请先阅读并同意会员协议', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.onBuyHuiyuanClick(e);
|
||||
},
|
||||
|
||||
stopPropagation() {},
|
||||
|
||||
// ========== 从全局变量加载数据 ==========
|
||||
loadFromGlobalData() {
|
||||
const app = getApp();
|
||||
@@ -130,37 +239,12 @@ onHide() {
|
||||
// ✅ 重要:保持字段对应关系
|
||||
// 全局变量中是 clumber,页面中是 clubmber
|
||||
// 全局变量中是 jinfen,页面中是 jifen
|
||||
this.setData({
|
||||
clubmber: globalData.clumber || [],
|
||||
yajin: globalData.yajin || 0,
|
||||
jifen: globalData.jinfen || 0 // ✅ 注意:这里从 jinfen 读取
|
||||
});
|
||||
|
||||
// 计算积分进度条
|
||||
this.calculateJifenProgress();
|
||||
},
|
||||
|
||||
// ========== 计算积分进度条角度 ==========
|
||||
calculateJifenProgress() {
|
||||
const jifen = this.data.jifen || 0;
|
||||
const progress = (jifen / 10) * 360; // 满分10分
|
||||
this.setData({
|
||||
jifenProgress: progress
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 计算会员列表高度 ==========
|
||||
calculateHuiyuanHeight() {
|
||||
const clubmber = this.data.clubmber || [];
|
||||
|
||||
const itemHeight = 100; // 每个会员标签高度
|
||||
const maxHeight = 300; // 最大高度(3个会员)
|
||||
|
||||
let height = clubmber.length * itemHeight;
|
||||
height = Math.min(height, maxHeight);
|
||||
const rawClubmber = globalData.clumber || [];
|
||||
const clubmber = rawClubmber.filter((c) => c && c.huiyuanid);
|
||||
|
||||
this.setData({
|
||||
huiyuanListHeight: height
|
||||
clubmber: clubmber,
|
||||
jifen: globalData.jinfen || 0,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -174,17 +258,15 @@ onHide() {
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const huiyuanList = res.data.data || [];
|
||||
|
||||
// 处理数据,标记已购买的会员
|
||||
const processedList = this.processHuiyuanList(huiyuanList);
|
||||
|
||||
const currentHuiyuan = processedList[0] || {};
|
||||
this.setData({
|
||||
huiyuanList: processedList,
|
||||
currentHuiyuan,
|
||||
detailRules: this.buildDetailRules(currentHuiyuan)
|
||||
});
|
||||
const huiyuanList = this.processHuiyuanList(res.data.data || []);
|
||||
let index = 0;
|
||||
const targetId = this.data.jumpParams.huiyuanId;
|
||||
if (targetId) {
|
||||
const found = huiyuanList.findIndex((h) => String(h.id) === String(targetId));
|
||||
if (found >= 0) index = found;
|
||||
}
|
||||
this.applyCurrentHuiyuan(huiyuanList[index] || {}, index);
|
||||
this.setData({ huiyuanList });
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '获取会员列表失败',
|
||||
@@ -211,7 +293,9 @@ onHide() {
|
||||
return {
|
||||
...item,
|
||||
isBought: !!boughtItem,
|
||||
buyInfo: boughtItem || null
|
||||
buyInfo: boughtItem || null,
|
||||
cardImageUrl: this.resolveOssUrl(item.card_image),
|
||||
cardBgUrl: this.resolveOssUrl(item.card_bg),
|
||||
};
|
||||
});
|
||||
},
|
||||
@@ -220,43 +304,27 @@ onHide() {
|
||||
onSwiperChange(e) {
|
||||
const current = e.detail.current;
|
||||
const huiyuanList = this.data.huiyuanList || [];
|
||||
const currentHuiyuan = huiyuanList[current] || {};
|
||||
|
||||
this.setData({
|
||||
currentHuiyuanIndex: current,
|
||||
currentHuiyuan,
|
||||
detailRules: this.buildDetailRules(currentHuiyuan)
|
||||
});
|
||||
this.applyCurrentHuiyuan(huiyuanList[current] || {}, current);
|
||||
},
|
||||
|
||||
// ========== 手动选择会员 ==========
|
||||
selectHuiyuan(e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const huiyuanList = this.data.huiyuanList || [];
|
||||
|
||||
const currentHuiyuan = huiyuanList[index] || {};
|
||||
this.setData({
|
||||
currentHuiyuanIndex: index,
|
||||
currentHuiyuan,
|
||||
detailRules: this.buildDetailRules(currentHuiyuan)
|
||||
});
|
||||
this.applyCurrentHuiyuan(huiyuanList[index] || {}, index);
|
||||
},
|
||||
|
||||
buildDetailRules(huiyuan = {}) {
|
||||
const formalDays = huiyuan.formal_days || 30;
|
||||
const rules = [
|
||||
`正式会员有效期为${formalDays}天,从购买当天开始计算`,
|
||||
'会员期间享受优先接单特权',
|
||||
'支持随时续费,未过期续费天数叠加,已过期从今天重新计算'
|
||||
`有效期${formalDays}天`,
|
||||
'可接多种订单类型',
|
||||
'支持续费,天数叠加',
|
||||
'正式会员支持余额抵扣',
|
||||
];
|
||||
if (huiyuan.can_buy_trial) {
|
||||
rules.push(`体验会员终身仅可购买1次(${huiyuan.trial_days || 0}天),体验期仅限制佣金提现`);
|
||||
rules[3] = `体验版¥${huiyuan.trial_price}/${huiyuan.trial_days}天`;
|
||||
}
|
||||
if (huiyuan.trial_enabled && !huiyuan.can_buy_trial) {
|
||||
rules.push('您已使用过该会员的体验资格');
|
||||
}
|
||||
rules.push('体验会员仅支持微信支付;正式会员可使用佣金、分红或押金抵扣');
|
||||
return rules;
|
||||
return rules.slice(0, 4);
|
||||
},
|
||||
|
||||
// ========== 格式化到期时间 ==========
|
||||
@@ -310,109 +378,10 @@ onHide() {
|
||||
// ========== 处理自动滚动 ==========
|
||||
handleAutoScroll() {
|
||||
const { jumpParams } = this.data;
|
||||
|
||||
if (!jumpParams || !jumpParams.needScroll) return;
|
||||
|
||||
if (!jumpParams || jumpParams.scrollTo !== 'jifen') return;
|
||||
setTimeout(() => {
|
||||
let scrollTop = 0;
|
||||
|
||||
if (jumpParams.scrollTo === 'bottom') {
|
||||
scrollTop = 2000;
|
||||
} else if (jumpParams.scrollTo === 'member') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollTop > 0) {
|
||||
wx.pageScrollTo({
|
||||
scrollTop: scrollTop,
|
||||
duration: 300
|
||||
});
|
||||
}
|
||||
}, 800);
|
||||
},
|
||||
|
||||
// ========== 押金充值相关 ==========
|
||||
showYajinModal() {
|
||||
this.setData({
|
||||
showYajinModal: true,
|
||||
yajinAmount: '100'
|
||||
});
|
||||
},
|
||||
|
||||
hideYajinModal() {
|
||||
this.setData({
|
||||
showYajinModal: false
|
||||
});
|
||||
},
|
||||
|
||||
onYajinInput(e) {
|
||||
let value = e.detail.value;
|
||||
value = value.replace(/[^\d]/g, '');
|
||||
|
||||
if (value) {
|
||||
const num = parseInt(value);
|
||||
if (num < 1) value = '1';
|
||||
if (num > 10000) value = '10000';
|
||||
}
|
||||
|
||||
this.setData({
|
||||
yajinAmount: value
|
||||
});
|
||||
},
|
||||
|
||||
setYajinAmount(e) {
|
||||
const amount = e.currentTarget.dataset.amount;
|
||||
this.setData({
|
||||
yajinAmount: amount
|
||||
});
|
||||
},
|
||||
|
||||
async handleYajinPay() {
|
||||
const amount = this.data.yajinAmount;
|
||||
|
||||
if (!amount || amount < 1 || amount > 10000) {
|
||||
wx.showToast({
|
||||
title: '请输入1-10000元的金额',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('发起支付中...');
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/yajingoumai',
|
||||
method: 'POST',
|
||||
data: {
|
||||
jine: parseInt(amount)
|
||||
}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const payParams = res.data.payParams;
|
||||
const dingdanid = res.data.dingdanid;
|
||||
|
||||
this.setData({
|
||||
currentDingdanid: dingdanid
|
||||
});
|
||||
|
||||
await this.wechatPay(payParams, 'yajin');
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '支付发起失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('押金支付失败:', error);
|
||||
wx.showToast({
|
||||
title: '支付失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
wx.pageScrollTo({ selector: '#jifen-section', duration: 300 });
|
||||
}, 600);
|
||||
},
|
||||
|
||||
// ========== 积分充值相关 ==========
|
||||
@@ -455,19 +424,8 @@ onHide() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTrial) {
|
||||
const { currentHuiyuan, huiyuanList } = this.data;
|
||||
const target = (currentHuiyuan && currentHuiyuan.id === huiyuanId)
|
||||
? currentHuiyuan
|
||||
: (huiyuanList || []).find(item => item.id === huiyuanId);
|
||||
if (target && !target.can_buy_trial) {
|
||||
wx.showToast({
|
||||
title: '您已使用过该会员的体验资格',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const confirmed = await this.confirmWechatPurchase('huiyuan');
|
||||
if (!confirmed) return;
|
||||
|
||||
this.showLoading('发起会员支付中...');
|
||||
|
||||
@@ -524,6 +482,9 @@ onHide() {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await this.confirmWechatPurchase('jifen');
|
||||
if (!confirmed) return;
|
||||
|
||||
this.showLoading('发起积分支付中...');
|
||||
|
||||
try {
|
||||
@@ -834,12 +795,33 @@ onHide() {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
},
|
||||
|
||||
/** 微信支付下单前确认(仅微信购买会员/押金/积分) */
|
||||
confirmWechatPurchase(kind) {
|
||||
const labelMap = {
|
||||
huiyuan: '会员',
|
||||
yajin: '押金',
|
||||
jifen: '积分',
|
||||
};
|
||||
const label = labelMap[kind] || '服务';
|
||||
return new Promise((resolve) => {
|
||||
wx.showModal({
|
||||
title: '确认微信支付',
|
||||
content: `您即将通过微信支付购买${label}。请点击「确认购买」后才会发起支付;购买成功后不可随意退款,请确认无误后再继续。`,
|
||||
confirmText: '确认购买',
|
||||
cancelText: '再想想',
|
||||
success: (res) => resolve(!!res.confirm),
|
||||
fail: () => resolve(false),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 新增功能(余额抵扣等) ==========
|
||||
onBuyHuiyuanClick(e) {
|
||||
const huiyuanId = e.currentTarget.dataset.id;
|
||||
const isTrial = e.currentTarget.dataset.trial === true || e.currentTarget.dataset.trial === 'true';
|
||||
|
||||
if (isTrial) {
|
||||
this.setData({ currentBuyIsTrial: true });
|
||||
this.handleHuiyuanBuy(e);
|
||||
return;
|
||||
}
|
||||
@@ -848,25 +830,7 @@ onHide() {
|
||||
currentBuyType: 1,
|
||||
currentHuiyuanId: huiyuanId,
|
||||
currentBuyIsTrial: false,
|
||||
showPayMethodModal: true
|
||||
});
|
||||
},
|
||||
|
||||
onYajinClick() {
|
||||
this.showYajinModal();
|
||||
},
|
||||
|
||||
onYajinConfirm() {
|
||||
const amount = this.data.yajinAmount;
|
||||
if (!amount || amount < 1 || amount > 10000) {
|
||||
wx.showToast({ title: '请输入1-10000元的金额', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.hideYajinModal();
|
||||
this.setData({
|
||||
currentBuyType: 2,
|
||||
currentYajinAmount: amount,
|
||||
showPayMethodModal: true
|
||||
showPayMethodModal: true,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -877,29 +841,20 @@ onHide() {
|
||||
// 🔥 修改点2:选择支付方式时再次校验积分状态(防止异步过程中积分变化)
|
||||
onPayMethodSelect(e) {
|
||||
const method = e.currentTarget.dataset.method;
|
||||
const { currentBuyType, currentHuiyuanId, currentYajinAmount, jifen } = this.data;
|
||||
const { currentBuyType, currentHuiyuanId, jifen } = this.data;
|
||||
this.hidePayMethodModal();
|
||||
|
||||
// 如果是积分购买,且积分已满,直接拒绝
|
||||
if (currentBuyType === 3 && jifen >= 10) {
|
||||
wx.showToast({
|
||||
title: '积分已满,无需补充',
|
||||
icon: 'none'
|
||||
});
|
||||
wx.showToast({ title: '积分已满,无需补充', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === 'wx') {
|
||||
if (currentBuyType === 1) {
|
||||
this.handleHuiyuanBuy({
|
||||
currentTarget: { dataset: { id: currentHuiyuanId, trial: 'false' } }
|
||||
});
|
||||
} else if (currentBuyType === 2) {
|
||||
this.setData({ yajinAmount: currentYajinAmount }, () => {
|
||||
this.handleYajinPay();
|
||||
currentTarget: { dataset: { id: currentHuiyuanId, trial: this.data.currentBuyIsTrial ? 'true' : 'false' } },
|
||||
});
|
||||
} else if (currentBuyType === 3) {
|
||||
// ✅ 调用已修正的积分支付函数
|
||||
this.handleJifenBuy({ currentTarget: { dataset: { disabled: 'false' } } });
|
||||
}
|
||||
} else if (method === 'balance') {
|
||||
@@ -909,7 +864,7 @@ onHide() {
|
||||
|
||||
// 🔥 修改点3:请求余额抵扣选项前校验积分状态
|
||||
async fetchBalanceOptions() {
|
||||
const { currentBuyType, currentHuiyuanId, currentYajinAmount, currentBuyIsTrial, jifen } = this.data;
|
||||
const { currentBuyType, currentHuiyuanId, currentBuyIsTrial, jifen } = this.data;
|
||||
|
||||
if (currentBuyType === 1 && currentBuyIsTrial) {
|
||||
wx.showToast({ title: '体验会员仅支持微信支付', icon: 'none' });
|
||||
@@ -934,9 +889,8 @@ onHide() {
|
||||
data: {
|
||||
leixing: currentBuyType,
|
||||
huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
|
||||
is_trial: currentBuyType === 1 ? false : undefined,
|
||||
yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
|
||||
}
|
||||
is_trial: currentBuyType === 1 ? currentBuyIsTrial : undefined,
|
||||
},
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
const options = res.data.data || [];
|
||||
@@ -977,7 +931,7 @@ onHide() {
|
||||
},
|
||||
|
||||
async confirmBalancePay() {
|
||||
const { selectedBalanceId, currentBuyType, currentHuiyuanId, currentYajinAmount, currentBuyIsTrial } = this.data;
|
||||
const { selectedBalanceId, currentBuyType, currentHuiyuanId, currentBuyIsTrial } = this.data;
|
||||
if (!selectedBalanceId) return;
|
||||
|
||||
this.hideConfirmModal();
|
||||
@@ -992,14 +946,13 @@ onHide() {
|
||||
shenfen_id: selectedBalanceId,
|
||||
huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
|
||||
is_trial: currentBuyType === 1 ? currentBuyIsTrial : undefined,
|
||||
yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const responseData = res.data.data || {};
|
||||
await this.updateAfterPayment(
|
||||
currentBuyType === 1 ? 'huiyuan' : (currentBuyType === 2 ? 'yajin' : 'jifen'),
|
||||
currentBuyType === 1 ? 'huiyuan' : 'jifen',
|
||||
currentBuyType === 1 ? currentHuiyuanId : null,
|
||||
responseData
|
||||
);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"popup-notice": "/components/popup-notice/popup-notice"
|
||||
},
|
||||
"navigationBarTitleText": "充值中心",
|
||||
"navigationBarBackgroundColor": "#0a0e2a",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0a0e2a",
|
||||
"backgroundTextStyle": "light",
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
"navigationBarTitleText": "VIP",
|
||||
"navigationBarBackgroundColor": "#FFF9F0",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#FFF9F0"
|
||||
}
|
||||
|
||||
@@ -1,528 +1,210 @@
|
||||
<!--pages/dashou-chongzhi/index.wxml-->
|
||||
<view class="page-container">
|
||||
<!-- ❌ 删除自定义导航栏区块 -->
|
||||
<!-- VIP 会员 + 积分充值页 -->
|
||||
<view class="vip-page">
|
||||
<image wx:if="{{imgUrls.pageBg}}" class="page-bg" src="{{imgUrls.pageBg}}" mode="widthFix" />
|
||||
<view class="page-bg-fallback" wx:else></view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- VIP会员区域 - 放在上面 -->
|
||||
<!-- ======================= -->
|
||||
<view class="vip-section">
|
||||
<view class="section-header">
|
||||
<view class="header-decoration">
|
||||
<view class="dec-line"></view>
|
||||
<view class="dec-diamond"></view>
|
||||
<text class="dec-text">VIP会员充值</text>
|
||||
<view class="dec-diamond"></view>
|
||||
<view class="dec-line"></view>
|
||||
<view class="page-body">
|
||||
<view class="user-row">
|
||||
<image class="user-avatar" src="{{avatarUrl}}" mode="aspectFill" binderror="onAvatarError" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{nickname || '打手'}}</text>
|
||||
<text class="user-id">会员ID: {{uid || '--'}}</text>
|
||||
</view>
|
||||
<text class="section-subtitle">选择你的会员</text>
|
||||
<view class="member-badge">{{memberBadge}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员卡片滑动区域 -->
|
||||
<swiper
|
||||
class="vip-swiper"
|
||||
current="{{currentHuiyuanIndex}}"
|
||||
circular="{{true}}"
|
||||
previous-margin="120rpx"
|
||||
next-margin="120rpx"
|
||||
duration="500"
|
||||
easing-function="easeOutCubic"
|
||||
circular="{{huiyuanList.length > 1}}"
|
||||
previous-margin="40rpx"
|
||||
next-margin="40rpx"
|
||||
duration="400"
|
||||
bindchange="onSwiperChange">
|
||||
<swiper-item wx:for="{{huiyuanList}}" wx:key="id">
|
||||
<view class="vip-card {{index === currentHuiyuanIndex ? 'active' : ''}}"
|
||||
data-index="{{index}}" bindtap="selectHuiyuan">
|
||||
<!-- 机甲风格边框 -->
|
||||
<view class="card-frame">
|
||||
<view class="frame-corner tl"></view>
|
||||
<view class="frame-corner tr"></view>
|
||||
<view class="frame-corner bl"></view>
|
||||
<view class="frame-corner br"></view>
|
||||
<view class="frame-line top"></view>
|
||||
<view class="frame-line right"></view>
|
||||
<view class="frame-line bottom"></view>
|
||||
<view class="frame-line left"></view>
|
||||
<view class="vip-card {{index === currentHuiyuanIndex ? 'vip-card--active' : ''}}" data-index="{{index}}" bindtap="selectHuiyuan">
|
||||
<image wx:if="{{item.cardBgUrl}}" class="vip-card-bg-img" src="{{item.cardBgUrl}}" mode="aspectFill" />
|
||||
<view class="vip-card-bg" wx:else></view>
|
||||
<view class="vip-card-inner">
|
||||
<view class="vip-card-left">
|
||||
<view class="vip-title-row">
|
||||
<text class="vip-name">{{item.mingzi}}</text>
|
||||
<view class="vip-status">{{item.isBought ? '已激活' : '未激活'}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<view class="vip-badge" wx:if="{{item.isBought}}">
|
||||
<text class="badge-text">已拥有</text>
|
||||
<view class="vip-price-row">
|
||||
<text class="vip-currency">¥</text>
|
||||
<text class="vip-price">{{item.jiage}}</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-title">
|
||||
<text class="title-text">{{item.mingzi}}</text>
|
||||
<view class="title-underline"></view>
|
||||
<text class="vip-validity">有效期 {{item.formal_days || 30}} 天</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-number">{{item.jiage}}</text>
|
||||
<text class="price-unit">/{{item.formal_days || 30}}天</text>
|
||||
<view class="vip-card-right">
|
||||
<view class="icon-clip icon-clip-vip" wx:if="{{item.cardImageUrl}}">
|
||||
<image class="icon-clip-img" src="{{item.cardImageUrl}}" mode="aspectFill" />
|
||||
</view>
|
||||
|
||||
<view class="vip-features">
|
||||
<view class="feature-item" wx:for="{{item.features || ['特权功能', '专属标识', '优先接单']}}"
|
||||
wx:key="index">
|
||||
<image class="feature-icon" src="/static/images/check-tech.png"></image>
|
||||
<text class="feature-text">{{item}}</text>
|
||||
<view class="vip-logo-fallback" wx:else>
|
||||
<text class="vip-logo-text">VIP</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选中状态指示器 -->
|
||||
<view class="card-indicator" wx:if="{{index === currentHuiyuanIndex}}">
|
||||
<view class="indicator-triangle"></view>
|
||||
<view class="indicator-glow"></view>
|
||||
</view>
|
||||
<view class="vip-shimmer"></view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 会员详细介绍 -->
|
||||
<view class="vip-detail">
|
||||
<view class="detail-header">
|
||||
<view class="detail-title">
|
||||
<image class="detail-icon" src="/static/images/info-tech.png"></image>
|
||||
<text class="title-text">{{currentHuiyuan.mingzi}} - 会员详情</text>
|
||||
<view class="section-head">
|
||||
<view class="section-bar"></view>
|
||||
<text class="section-title">{{currentHuiyuan.mingzi || '会员'}}规则</text>
|
||||
</view>
|
||||
<view class="detail-tech"></view>
|
||||
</view>
|
||||
|
||||
<view class="detail-content">
|
||||
<scroll-view class="detail-scroll" scroll-y>
|
||||
<text class="detail-text">{{currentHuiyuan.jieshao || '加载中...'}}</text>
|
||||
|
||||
<view class="detail-rules">
|
||||
<view class="rule-item" wx:for="{{detailRules}}" wx:key="index">
|
||||
<view class="rule-number">{{index + 1}}</view>
|
||||
<scroll-view class="rules-scroll" scroll-x enable-flex>
|
||||
<view class="rules-row">
|
||||
<view wx:for="{{detailRules}}" wx:key="index" class="rule-card">
|
||||
<view class="rule-num">{{index + 1}}</view>
|
||||
<text class="rule-text">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="section-head">
|
||||
<view class="section-bar"></view>
|
||||
<text class="section-title">{{currentHuiyuan.mingzi || '会员'}}专属权益</text>
|
||||
</view>
|
||||
<view class="benefits-row">
|
||||
<view wx:for="{{memberBenefits}}" wx:key="title" class="benefit-card">
|
||||
<view wx:if="{{item.iconUrl}}" class="icon-clip icon-clip-benefit">
|
||||
<image class="icon-clip-img" src="{{item.iconUrl}}" mode="aspectFill" />
|
||||
</view>
|
||||
<text class="benefit-title">{{item.title}}</text>
|
||||
<text class="benefit-desc">{{item.desc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 购买区域 - 点击按钮先弹出支付方式选择 -->
|
||||
<view class="buy-section">
|
||||
<view class="buy-info">
|
||||
<view class="price-display">
|
||||
<!-- 🔥 将“机甲能源”改为“价格” -->
|
||||
<text class="price-label">价格:</text>
|
||||
<text class="price-value">¥{{currentHuiyuan.jiage}}</text>
|
||||
<text class="price-days">/{{currentHuiyuan.formal_days || 30}}天</text>
|
||||
<view class="section-head">
|
||||
<view class="section-bar"></view>
|
||||
<text class="section-title">平台优势</text>
|
||||
</view>
|
||||
<view class="advantage-grid">
|
||||
<view wx:for="{{platformAdvantages}}" wx:key="title" class="advantage-item">
|
||||
<view wx:if="{{item.iconUrl}}" class="icon-clip icon-clip-advantage">
|
||||
<image class="icon-clip-img" src="{{item.iconUrl}}" mode="aspectFill" />
|
||||
</view>
|
||||
<text class="advantage-title">{{item.title}}</text>
|
||||
<text class="advantage-desc">{{item.desc}}</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 trial-hint" wx:if="{{currentHuiyuan.can_buy_trial}}">
|
||||
体验版仅微信支付 ¥{{currentHuiyuan.trial_price}}/{{currentHuiyuan.trial_days}}天,终身1次
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="buy-action">
|
||||
<view class="tech-buy-btn {{currentHuiyuan.isBought ? 'renew' : ''}}"
|
||||
bindtap="onBuyHuiyuanClick" data-id="{{currentHuiyuan.id}}" data-trial="false">
|
||||
<view class="buy-btn-bg"></view>
|
||||
<view class="buy-btn-glow"></view>
|
||||
<text class="buy-btn-text">
|
||||
{{currentHuiyuan.isBought ? '正式续费' : '购买正式版'}}
|
||||
</text>
|
||||
<view class="buy-btn-energy">
|
||||
<view class="energy-dot"></view>
|
||||
<view class="energy-dot"></view>
|
||||
<view class="energy-dot"></view>
|
||||
<view id="jifen-section" class="jifen-section">
|
||||
<view class="section-head">
|
||||
<view class="section-bar"></view>
|
||||
<text class="section-title">积分充值</text>
|
||||
</view>
|
||||
<view class="jifen-card">
|
||||
<view class="jifen-left">
|
||||
<text class="jifen-label">当前积分</text>
|
||||
<text class="jifen-value">{{jifen}}</text>
|
||||
<text class="jifen-tip" wx:if="{{jifen < 10}}">积分不足将影响接单与提现</text>
|
||||
<text class="jifen-tip jifen-tip--ok" wx:else>积分充足</text>
|
||||
</view>
|
||||
<view class="jifen-btn {{jifen >= 10 ? 'jifen-btn--disabled' : ''}}" bindtap="onJifenClick" data-disabled="{{jifen >= 10}}">
|
||||
{{jifen >= 10 ? '已满' : '补充积分'}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="owned-section" wx:if="{{clubmber.length > 0}}">
|
||||
<view class="section-head">
|
||||
<view class="section-bar"></view>
|
||||
<text class="section-title">我的会员</text>
|
||||
</view>
|
||||
<view wx:for="{{clubmber}}" wx:key="huiyuanid" class="owned-item" data-item="{{item}}" bindtap="showMemberDetail">
|
||||
<text class="owned-name">{{item.huiyuanming}}</text>
|
||||
<text class="owned-expire">到期 {{formatDaoqiTime(item.daoqi)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer-bar">
|
||||
<view class="deposit-banner" bindtap="goToDeposit">
|
||||
<text class="deposit-banner-text">缴纳履约金,快人一步</text>
|
||||
<text class="deposit-banner-link">去缴纳 ></text>
|
||||
</view>
|
||||
<view class="agree-row" bindtap="toggleAgree">
|
||||
<view class="agree-check {{agreed ? 'agree-check--on' : ''}}">{{agreed ? '✓' : ''}}</view>
|
||||
<text class="agree-text">已阅读并同意</text>
|
||||
<text class="agree-link">《{{currentHuiyuan.mingzi || '会员'}}协议》</text>
|
||||
</view>
|
||||
<view class="buy-row">
|
||||
<view
|
||||
wx:if="{{currentHuiyuan.can_buy_trial}}"
|
||||
class="tech-buy-btn trial-buy-btn"
|
||||
bindtap="onBuyHuiyuanClick"
|
||||
class="buy-btn buy-btn--outline"
|
||||
bindtap="onTapTrial"
|
||||
data-id="{{currentHuiyuan.id}}"
|
||||
data-trial="true"
|
||||
>
|
||||
<view class="buy-btn-bg"></view>
|
||||
<text class="buy-btn-text">购买体验版</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 三卡片区域 - 放在下面 -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-grid">
|
||||
<!-- 会员区域 - 高度限制最多3个,超出可滚动 -->
|
||||
<view class="tech-card huiyuan-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<text class="title-text">我的会员</text>
|
||||
<view class="title-text">👑</view>
|
||||
</view>
|
||||
<view class="card-badge">{{clubmber.length}}</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="huiyuan-scroll"
|
||||
scroll-y
|
||||
enable-flex
|
||||
style="height: {{huiyuanListHeight}}rpx">
|
||||
<view wx:for="{{clubmber}}" wx:key="huiyuanid" class="huiyuan-item" data-item="{{item}}" bindtap="showMemberDetail">
|
||||
<view class="huiyuan-tag tech-tag">
|
||||
<view class="tag-triangle"></view>
|
||||
<text class="tag-name">{{item.huiyuanming}}</text>
|
||||
<text class="tag-time">到期: {{formatDaoqiTime(item.daoqi)}}</text>
|
||||
<view class="tag-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{clubmber.length === 0}}" class="empty-huiyuan">
|
||||
<image class="empty-icon" src="/static/images/empty-member.png"></image>
|
||||
<text class="empty-text">暂无会员</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="card-footer">
|
||||
<text class="footer-text">共{{clubmber.length}}个会员</text>
|
||||
<view class="footer-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 押金区域 - 点击充值按钮弹出押金输入弹窗 -->
|
||||
<view class="tech-card yajin-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<text class="title-text">我的押金</text>
|
||||
<view class="title-text">💰</view>
|
||||
</view>
|
||||
<view class="card-corner"></view>
|
||||
</view>
|
||||
|
||||
<view class="yajin-display">
|
||||
<view class="amount-container">
|
||||
<text class="amount-symbol">¥</text>
|
||||
<text class="amount-number">{{yajin}}</text>
|
||||
</view>
|
||||
<text class="amount-unit">可用余额</text>
|
||||
</view>
|
||||
|
||||
<view class="tech-line"></view>
|
||||
|
||||
<view class="action-container">
|
||||
<view class="tech-btn yajin-btn" bindtap="onYajinClick">
|
||||
<view class="btn-bg"></view>
|
||||
<text class="btn-text">立即充值</text>
|
||||
<view class="btn-arrow" style="display:none;">➤</view>
|
||||
<view class="btn-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-corner corner-bottom"></view>
|
||||
</view>
|
||||
|
||||
<!-- 积分区域 - 点击补充按钮弹出支付方式选择 -->
|
||||
<view class="tech-card jifen-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<text class="title-text">我的积分</text>
|
||||
<view class="title-text">⭐</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="jifen-display">
|
||||
<view class="circle-progress">
|
||||
<view class="circle-bg"></view>
|
||||
<view class="circle-mask" style="transform: rotate({{jifenProgress}}deg);"></view>
|
||||
<view class="circle-fill" style="transform: rotate({{jifenProgress}}deg);"></view>
|
||||
<view class="circle-center">
|
||||
<text class="jifen-value">{{jifen}}</text>
|
||||
<text class="jifen-label">积分</text>
|
||||
</view>
|
||||
<view class="circle-dots">
|
||||
<view class="circle-dot" wx:for="{{10}}" wx:key="index"
|
||||
style="transform: rotate({{index * 36}}deg);"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="jifen-status">
|
||||
<text class="status-text" wx:if="{{jifen < 10}}">积分不足,影响接单,提现权限</text>
|
||||
<text class="status-text full" wx:else>积分充足,无需补充</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-container">
|
||||
<view class="tech-btn jifen-btn" bindtap="onJifenClick" data-disabled="{{jifen >= 10}}">
|
||||
<view class="btn-bg"></view>
|
||||
<text class="btn-text">{{jifen >= 10 ? '积分已满' : '立即补充'}}</text>
|
||||
<view class="btn-energy" wx:if="{{jifen < 10}}"></view>
|
||||
<view class="btn-glow" wx:if="{{jifen < 10}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 押金充值弹窗(原有,确认事件改为 onYajinConfirm) -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-modal" wx:if="{{showYajinModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideYajinModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">押金充值</text>
|
||||
<text class="modal-subtitle">充值保证金</text>
|
||||
<view class="modal-close" bindtap="hideYajinModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="input-section">
|
||||
<view class="input-label">
|
||||
<image class="label-icon" src="/static/images/money-tech.png"></image>
|
||||
<text class="label-text">充值金额 (1-10000元)</text>
|
||||
</view>
|
||||
|
||||
<view class="input-container">
|
||||
<view class="input-prefix">¥</view>
|
||||
<input
|
||||
class="yajin-input"
|
||||
type="digit"
|
||||
placeholder="输入充值金额"
|
||||
placeholder-class="input-placeholder"
|
||||
value="{{yajinAmount}}"
|
||||
bindinput="onYajinInput"
|
||||
maxlength="5"
|
||||
focus="{{showYajinModal}}"/>
|
||||
<view class="input-underline"></view>
|
||||
</view>
|
||||
|
||||
<text class="input-tip">支持1-10000元整数充值</text>
|
||||
</view>
|
||||
|
||||
<view class="quick-section">
|
||||
<text class="quick-title">快速选择</text>
|
||||
<view class="quick-buttons">
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '100' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="100">
|
||||
<text class="quick-text">100元</text>
|
||||
data-trial="true">
|
||||
体验会员 (¥{{currentHuiyuan.trial_price}})
|
||||
</view>
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '500' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="500">
|
||||
<text class="quick-text">500元</text>
|
||||
</view>
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '1000' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="1000">
|
||||
<text class="quick-text">1000元</text>
|
||||
</view>
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '5000' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="5000">
|
||||
<text class="quick-text">5000元</text>
|
||||
class="buy-btn buy-btn--primary {{currentHuiyuan.can_buy_trial ? '' : 'buy-btn--full'}}"
|
||||
bindtap="onTapFormal"
|
||||
data-id="{{currentHuiyuan.id}}"
|
||||
data-trial="false">
|
||||
{{currentHuiyuan.isBought ? '续费正式会员' : '升级正式会员'}} (¥{{currentHuiyuan.jiage}})
|
||||
<view class="buy-btn-shimmer"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" bindtap="hideYajinModal">
|
||||
<text class="btn-text">取消</text>
|
||||
</view>
|
||||
<view class="modal-divider"></view>
|
||||
<view class="modal-btn confirm-btn" bindtap="onYajinConfirm">
|
||||
<text class="btn-text">确认支付</text>
|
||||
<view class="btn-sparkle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 支付方式选择弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showPayMethodModal}}">
|
||||
<view class="modal-backdrop" bindtap="hidePayMethodModal"></view>
|
||||
<view class="modal-container small">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择支付方式</text>
|
||||
<view class="modal-close" bindtap="hidePayMethodModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="pay-method-list">
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="wx">
|
||||
<image class="method-icon" src="/static/images/wechat-pay.png"></image>
|
||||
<text class="method-name">微信支付</text>
|
||||
<text class="method-desc">安全快捷</text>
|
||||
</view>
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="balance">
|
||||
<image class="method-icon" src="/static/images/balance-pay.png"></image>
|
||||
<text class="method-name">余额抵扣</text>
|
||||
<text class="method-desc">使用佣金或分红</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 余额抵扣身份选择弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showBalanceModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideBalanceModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择抵扣身份</text>
|
||||
<view class="modal-close" bindtap="hideBalanceModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="balance-list">
|
||||
<view wx:for="{{balanceOptions}}" wx:key="id" class="balance-item"
|
||||
data-id="{{item.id}}" bindtap="onBalanceSelect">
|
||||
<view class="balance-info">
|
||||
<text class="balance-name">{{item.jieshao}}</text>
|
||||
<text class="balance-amount">需¥{{item.xuyao}}</text>
|
||||
</view>
|
||||
<view class="balance-arrow">➤</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 余额抵扣确认弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showConfirmModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideConfirmModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">确认抵扣</text>
|
||||
<view class="modal-close" bindtap="hideConfirmModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="confirm-detail">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">抵扣身份:</text>
|
||||
<text class="detail-value">{{selectedBalanceInfo && selectedBalanceInfo.jieshao}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">所需金额:</text>
|
||||
<text class="detail-value">¥{{selectedBalanceInfo && selectedBalanceInfo.xuyao}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">实际扣除:</text>
|
||||
<text class="detail-value">¥{{selectedBalanceInfo && selectedBalanceInfo.xuyao}}</text>
|
||||
</view>
|
||||
<view class="detail-tip">确认后将从您的余额中扣除</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" bindtap="hideConfirmModal">
|
||||
<text class="btn-text">取消</text>
|
||||
</view>
|
||||
<view class="modal-divider"></view>
|
||||
<view class="modal-btn confirm-btn" bindtap="confirmBalancePay">
|
||||
<text class="btn-text">确认支付</text>
|
||||
<view class="btn-sparkle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 会员详情弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showMemberModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideMemberModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">会员详情</text>
|
||||
<view class="modal-close" bindtap="hideMemberModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="member-detail">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">会员名称:</text>
|
||||
<text class="detail-value">{{currentMemberDetail && currentMemberDetail.huiyuanming}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">到期时间:</text>
|
||||
<text class="detail-value">{{currentMemberDetail && currentMemberDetail.daoqi}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">剩余天数:</text>
|
||||
<text class="detail-value">{{formatRemainTime(currentMemberDetail && currentMemberDetail.daoqi)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ❌ 删除“进入提示弹窗”整个区块 -->
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 支付加载动画(原有) -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-loading" wx:if="{{payLoading}}">
|
||||
<view class="loading-backdrop"></view>
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner">
|
||||
<view class="spinner-ring ring-1"></view>
|
||||
<view class="spinner-ring ring-2"></view>
|
||||
<view class="spinner-ring ring-3"></view>
|
||||
<view class="spinner-core"></view>
|
||||
<view class="spinner-energy"></view>
|
||||
</view>
|
||||
<text class="loading-text">{{loadingText}}</text>
|
||||
<text class="loading-subtext">正在连接支付系统...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 底部机甲装饰 -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-footer">
|
||||
<view class="footer-line"></view>
|
||||
<view class="footer-text">星阙网络 © 科技</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 通知组件 -->
|
||||
<global-notification id="global-notification" />
|
||||
<!-- 支付方式 -->
|
||||
<view class="modal-mask" wx:if="{{showPayMethodModal}}" bindtap="hidePayMethodModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">选择支付方式</text>
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="wx">
|
||||
<text class="pay-method-name">微信支付</text>
|
||||
<text class="pay-method-desc">安全快捷</text>
|
||||
</view>
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="balance">
|
||||
<text class="pay-method-name">余额抵扣</text>
|
||||
<text class="pay-method-desc">使用佣金或分红</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 🆕 统一弹窗组件 -->
|
||||
<view class="modal-mask" wx:if="{{showBalanceModal}}" bindtap="hideBalanceModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">选择抵扣身份</text>
|
||||
<view wx:for="{{balanceOptions}}" wx:key="id" class="balance-item" bindtap="onBalanceSelect" data-id="{{item.id}}">
|
||||
<text>{{item.jieshao}}</text>
|
||||
<text class="balance-need">需¥{{item.xuyao}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-mask" wx:if="{{showConfirmModal}}" bindtap="hideConfirmModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">确认抵扣</text>
|
||||
<view class="confirm-row"><text>身份</text><text>{{selectedBalanceInfo.jieshao}}</text></view>
|
||||
<view class="confirm-row"><text>金额</text><text>¥{{selectedBalanceInfo.xuyao}}</text></view>
|
||||
<view class="modal-actions">
|
||||
<view class="modal-btn cancel" bindtap="hideConfirmModal">取消</view>
|
||||
<view class="modal-btn confirm" bindtap="confirmBalancePay">确认支付</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-mask" wx:if="{{showMemberModal}}" bindtap="hideMemberModal">
|
||||
<view class="modal-panel" catchtap="stopPropagation">
|
||||
<text class="modal-title">会员详情</text>
|
||||
<view class="confirm-row"><text>名称</text><text>{{currentMemberDetail.huiyuanming}}</text></view>
|
||||
<view class="confirm-row"><text>到期</text><text>{{currentMemberDetail.daoqi}}</text></view>
|
||||
<view class="confirm-row"><text>剩余</text><text>{{formatRemainTime(currentMemberDetail.daoqi)}}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="loading-mask" wx:if="{{payLoading}}">
|
||||
<view class="loading-box">
|
||||
<view class="loading-spinner"></view>
|
||||
<text>{{loadingText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<global-notification id="global-notification" />
|
||||
<popup-notice id="popupNotice" />
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,25 @@
|
||||
// pages/dashouzhongxin/dashouzhongxin.js
|
||||
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 { fetchMyZhidingOrders, processZhidingList, filterZhidingBannerList } from '../../utils/zhiding-order.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,
|
||||
refreshPindaoConfig,
|
||||
getPindaoConfig,
|
||||
resolvePindaoImages,
|
||||
warmupPindaoConfig,
|
||||
PINDAO_ICON_FALLBACK,
|
||||
defaultIconRel,
|
||||
ICON_FOLDER,
|
||||
} from '../../utils/miniapp-icons.js'
|
||||
import { getDefaultAvatarUrl } from '../../utils/avatar.js'
|
||||
|
||||
const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC'
|
||||
const app = getApp()
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
@@ -20,8 +34,7 @@ Page(createPage({
|
||||
shangjiaCertified: false,
|
||||
totalAssets: '0.00',
|
||||
assetBreakdown: [],
|
||||
pendingAuthList: [],
|
||||
showAuthSection: false,
|
||||
moreFuncList: [],
|
||||
inviteCode: '',
|
||||
guanshiInviteCode: '',
|
||||
zuzhangInviteCode: '',
|
||||
@@ -43,6 +56,7 @@ Page(createPage({
|
||||
zuzhangInviterCache: null,
|
||||
chenghaoList: [],
|
||||
guanshiChenghaoList: [],
|
||||
identityTagList: [],
|
||||
gszhstatus: '',
|
||||
yaoqingzongshu: 0,
|
||||
fenyongzonge: '0.00',
|
||||
@@ -58,7 +72,6 @@ Page(createPage({
|
||||
tongguoZongshu: 0,
|
||||
pendingShenheCount: 0,
|
||||
layoutSparse: false,
|
||||
scrollRefreshing: false,
|
||||
rankBoardList: [
|
||||
{ type: 'dashou', name: '接单员榜', sub: '接单成交', icon: '', shapeClass: 'rank-shape-left', themeClass: 'rank-theme-blue' },
|
||||
{ type: 'guanshi', name: '管事榜', sub: '邀请收入', icon: '', shapeClass: 'rank-shape-right', themeClass: 'rank-theme-gold' },
|
||||
@@ -68,95 +81,69 @@ Page(createPage({
|
||||
|
||||
statusBar: 20,
|
||||
navBar: 44,
|
||||
pindaoVisible: false,
|
||||
pindaoTitle: '频道',
|
||||
pindaoChannelNo: '',
|
||||
examEnabled: false,
|
||||
examPassed: false,
|
||||
pindaoImages: [],
|
||||
|
||||
scrollViewRefreshing: false,
|
||||
showRechargeBanners: false,
|
||||
showPindaoEntry: true,
|
||||
|
||||
userBg: '',
|
||||
qiepianBanner: '',
|
||||
ksIcon: '',
|
||||
penaltyTipIcon: '',
|
||||
followExpanded: false,
|
||||
followClass: '',
|
||||
pendingPenaltyCount: 0,
|
||||
myZhidingList: [],
|
||||
myZhidingCount: 0,
|
||||
orderNavXym: [],
|
||||
tradeNavXym: [],
|
||||
inviteNavXym: [],
|
||||
},
|
||||
|
||||
_buildImgUrls(ossImageUrl) {
|
||||
const dsBase = ossImageUrl + 'beijing/dashouduan/';
|
||||
const iconBase = ossImageUrl + 'beijing/dashouduan/dashoutubiaobeijing/';
|
||||
const gsBase = ossImageUrl + 'beijing/guanshiduan/';
|
||||
const khBase = ossImageUrl + 'beijing/kaohe/';
|
||||
const app = getApp();
|
||||
const icon = (key, fallback) => resolveMiniappIcon(app, key, fallback || defaultIconRel(key));
|
||||
return {
|
||||
pageBg: dsBase + 'page-bg.jpg',
|
||||
userCardBg: dsBase + 'user-card-bg.png',
|
||||
vipCardBg: dsBase + 'vip-card-bg.jpg',
|
||||
assetPanelBg: dsBase + 'asset-panel-bg.png',
|
||||
rankHubBg: dsBase + 'rank-hub-bg.png',
|
||||
rankTileBg: dsBase + 'rank-tile-bg.png',
|
||||
funcPanelBg: dsBase + 'func-panel-bg.png',
|
||||
listBg: dsBase + 'list-card-bg.png',
|
||||
topBg: iconBase + 'top-bg.jpg',
|
||||
cardBg: iconBase + 'card-bg.jpg',
|
||||
iconRefresh: gsBase + 'icon-refresh.png',
|
||||
iconCopy: gsBase + 'icon-copy.png',
|
||||
iconVip: dsBase + 'icon-vip.png',
|
||||
iconArrowLight: dsBase + 'icon-arrow-light.png',
|
||||
iconSectionAsset: dsBase + 'icon-section-asset.png',
|
||||
iconSectionRank: dsBase + 'icon-section-rank.png',
|
||||
iconSectionFunc: dsBase + 'icon-section-func.png',
|
||||
iconSectionGuanshi: dsBase + 'icon-section-guanshi.png',
|
||||
iconSectionZuzhang: dsBase + 'icon-section-zuzhang.png',
|
||||
iconSectionKaohe: dsBase + 'icon-section-kaohe.png',
|
||||
iconSectionMore: dsBase + 'icon-section-more.png',
|
||||
iconRankDashou: dsBase + 'rank-dashou.png',
|
||||
iconRankGuanshi: dsBase + 'rank-guanshi.png',
|
||||
iconRankZuzhang: dsBase + 'rank-zuzhang.png',
|
||||
iconRankShangjia: dsBase + 'rank-shangjia.png',
|
||||
iconGame: iconBase + 'icon-game.jpg',
|
||||
iconRecharge: iconBase + 'icon-recharge.jpg',
|
||||
iconOrders: iconBase + 'icon-orders.jpg',
|
||||
iconPunish: iconBase + 'icon-punish.jpg',
|
||||
iconRank: iconBase + 'icon-rank.jpg',
|
||||
iconInvite: gsBase + 'icon-invite.png',
|
||||
iconMedal: iconBase + 'icon-medal.jpg',
|
||||
iconEdit: iconBase + 'icon-edit.jpg',
|
||||
iconBook: iconBase + 'icon-book.jpg',
|
||||
iconArrow: gsBase + 'icon-arrow.png',
|
||||
iconSub: gsBase + 'icon-sub.png',
|
||||
iconRecord: gsBase + 'icon-record.png',
|
||||
iconPoster: gsBase + 'icon-poster.png',
|
||||
iconWithdraw: gsBase + 'icon-withdraw.png',
|
||||
iconContact: gsBase + 'icon-contact.png',
|
||||
avatarDefault: ossImageUrl + 'avatar/default.jpg',
|
||||
iconClear: ossImageUrl + 'beijing/tubiao/grzx_qingchu.jpg',
|
||||
iconSwitch: '/images/_exit.png',
|
||||
iconKefu: ossImageUrl + 'beijing/tubiao/grzx_kefu.jpg',
|
||||
iconKuaishou: ossImageUrl + 'beijing/tubiao/grzx_guanzhualong.jpg',
|
||||
iconKaoheDafen: khBase + 'daofen.png',
|
||||
iconKaoheJilu: khBase + 'jilu.png',
|
||||
iconKaoheZhongxin: khBase + 'zhongxin.png',
|
||||
iconShangjia: ossImageUrl + 'beijing/tubiao/grzx_shangjia.jpg',
|
||||
iconDashouAuth: ossImageUrl + 'beijing/tubiao/grzx_dashou.jpg',
|
||||
iconGuanshiAuth: ossImageUrl + 'beijing/tubiao/grzx_guanshi.jpg',
|
||||
iconZuzhangAuth: ossImageUrl + 'beijing/tubiao/grzx_zuzhang.jpg',
|
||||
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',
|
||||
iconRefresh: icon(ICON_KEYS.ICON_REFRESH, 'beijing/guanshiduan/icon-refresh.png'),
|
||||
iconCopy: icon(ICON_KEYS.FIGHTER_MINE_COPY, 'beijing/guanshiduan/icon-copy.png'),
|
||||
iconEdit: icon(ICON_KEYS.FIGHTER_MINE_EDIT, 'beijing/dashouduan/dashoutubiaobeijing/icon-edit.jpg'),
|
||||
iconPunish: icon(ICON_KEYS.FIGHTER_MINE_PUNISH, 'beijing/dashouduan/dashoutubiaobeijing/icon-punish.jpg'),
|
||||
iconMedal: icon(ICON_KEYS.FIGHTER_MINE_MEDAL, 'beijing/dashouduan/dashoutubiaobeijing/icon-medal.jpg'),
|
||||
iconBook: icon(ICON_KEYS.FIGHTER_MINE_BOOK, 'beijing/dashouduan/dashoutubiaobeijing/icon-book.jpg'),
|
||||
iconRank: icon(ICON_KEYS.FIGHTER_MINE_RANK, 'beijing/dashouduan/dashoutubiaobeijing/icon-rank.jpg'),
|
||||
iconInvite: icon(ICON_KEYS.FIGHTER_MINE_INVITE, 'beijing/guanshiduan/icon-invite.png'),
|
||||
iconContact: icon(ICON_KEYS.FIGHTER_MINE_CONTACT, 'beijing/guanshiduan/icon-contact.png'),
|
||||
iconKefu: icon(ICON_KEYS.FIGHTER_MINE_KEFU, 'beijing/tubiao/grzx_kefu.jpg'),
|
||||
iconPindao: icon(ICON_KEYS.MINE_PINDAO, PINDAO_ICON_FALLBACK),
|
||||
iconKaoheDafen: icon(ICON_KEYS.FIGHTER_MINE_KAOHE_DAFEN, 'beijing/kaohe/daofen.png'),
|
||||
iconKaoheJilu: icon(ICON_KEYS.FIGHTER_MINE_KAOHE_JILU, 'beijing/kaohe/jilu.png'),
|
||||
iconKaoheZhongxin: icon(ICON_KEYS.FIGHTER_MINE_KAOHE_ZHONGXIN, 'beijing/kaohe/zhongxin.png'),
|
||||
iconShangjia: icon(ICON_KEYS.FIGHTER_MINE_SHANGJIA, 'beijing/tubiao/grzx_shangjia.jpg'),
|
||||
iconDashouAuth: icon(ICON_KEYS.FIGHTER_MINE_DASHOU_AUTH, 'beijing/tubiao/grzx_dashou.jpg'),
|
||||
iconClear: icon(ICON_KEYS.FIGHTER_MINE_CLEAR, 'beijing/tubiao/grzx_qingchu.jpg'),
|
||||
iconSwitch: icon(ICON_KEYS.FIGHTER_MINE_SWITCH, `${ICON_FOLDER}/fighter_mine_switch.png`),
|
||||
iconRecord: icon(ICON_KEYS.FIGHTER_MINE_RECORD, 'beijing/guanshiduan/icon-record.png'),
|
||||
bannerVip: icon(ICON_KEYS.FIGHTER_RECHARGE_MEMBER, `${ICON_FOLDER}/fighter_recharge_member.png`),
|
||||
bannerDeposit: icon(ICON_KEYS.FIGHTER_RECHARGE_DEPOSIT, `${ICON_FOLDER}/fighter_recharge_deposit.png`),
|
||||
avatarDefault: getDefaultAvatarUrl(app),
|
||||
};
|
||||
},
|
||||
|
||||
_applyRankBoardIcons(imgUrls) {
|
||||
const iconMap = {
|
||||
dashou: imgUrls.iconRankDashou,
|
||||
guanshi: imgUrls.iconRankGuanshi,
|
||||
zuzhang: imgUrls.iconRankZuzhang,
|
||||
shangjia: imgUrls.iconRankShangjia,
|
||||
};
|
||||
return (this.data.rankBoardList || []).map((item) => ({
|
||||
...item,
|
||||
icon: iconMap[item.type] || imgUrls.iconRank,
|
||||
}));
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const app = getApp();
|
||||
const sys = wx.getSystemInfoSync();
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
const imgUrls = this._buildImgUrls(ossImageUrl);
|
||||
const nav = this._buildNavLists(app);
|
||||
this.setData({
|
||||
imgUrls,
|
||||
rankBoardList: this._applyRankBoardIcons(imgUrls),
|
||||
...nav,
|
||||
statusBar: sys.statusBarHeight || 20,
|
||||
navBar: (sys.statusBarHeight || 20) + 44,
|
||||
});
|
||||
@@ -180,11 +167,60 @@ Page(createPage({
|
||||
}
|
||||
}
|
||||
this.checkColdStartPopup('dashouduan');
|
||||
this.syncConfiguredAssets();
|
||||
},
|
||||
|
||||
_buildNavLists(app) {
|
||||
const icon = (key) => resolveMiniappIcon(app, key, defaultIconRel(key));
|
||||
return {
|
||||
orderNavXym: [
|
||||
{ name: '待完成', statusKey: 'jinxingzhong', icon: icon(ICON_KEYS.FIGHTER_ORDER_PENDING), badge: 0, showBadge: false },
|
||||
{ name: '待结算', statusKey: 'jiesuanzhong', icon: icon(ICON_KEYS.FIGHTER_ORDER_SETTLING), badge: 0, showBadge: false },
|
||||
{ name: '已完成', statusKey: 'yiwancheng', icon: icon(ICON_KEYS.FIGHTER_ORDER_DONE), badge: 0, showBadge: false },
|
||||
{ name: '已关闭', statusKey: 'yituikuan', icon: icon(ICON_KEYS.FIGHTER_ORDER_CLOSED), badge: 0, showBadge: false },
|
||||
],
|
||||
tradeNavXym: [
|
||||
{ name: '佣金记录', type: 'commission', icon: icon(ICON_KEYS.FIGHTER_TRADE_COMMISSION) },
|
||||
{ name: '提现记录', type: 'withdraw', icon: icon(ICON_KEYS.FIGHTER_TRADE_WITHDRAW) },
|
||||
{ name: '结算记录', type: 'settle', icon: icon(ICON_KEYS.FIGHTER_TRADE_SETTLE) },
|
||||
{ name: '扣款记录', type: 'deduct', icon: icon(ICON_KEYS.FIGHTER_TRADE_DEDUCT) },
|
||||
{ name: '充值记录', type: 'recharge', icon: icon(ICON_KEYS.FIGHTER_TRADE_RECHARGE) },
|
||||
],
|
||||
inviteNavXym: [
|
||||
{ name: '我的下级', action: 'subordinate', icon: icon(ICON_KEYS.FIGHTER_INVITE_SUBORDINATE) },
|
||||
{ name: '推广海报', action: 'poster', icon: icon(ICON_KEYS.FIGHTER_INVITE_POSTER) },
|
||||
{ name: '邀请码', action: 'inviteCode', icon: icon(ICON_KEYS.FIGHTER_INVITE_CODE) },
|
||||
],
|
||||
ksIcon: icon(ICON_KEYS.FIGHTER_KUAISHOU_CASH),
|
||||
qiepianBanner: icon(ICON_KEYS.FIGHTER_MINE_QIEPIAN),
|
||||
penaltyTipIcon: icon(ICON_KEYS.FIGHTER_MINE_PENALTY_TIP),
|
||||
};
|
||||
},
|
||||
|
||||
syncConfiguredAssets() {
|
||||
const app = getApp();
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
const imgUrls = this._buildImgUrls(ossImageUrl);
|
||||
const nav = this._buildNavLists(app);
|
||||
const bannerVip = imgUrls.bannerVip;
|
||||
const bannerDeposit = imgUrls.bannerDeposit;
|
||||
this.setData({
|
||||
imgUrls,
|
||||
...nav,
|
||||
showRechargeBanners: !!(bannerVip || bannerDeposit),
|
||||
// showPindaoEntry: ... || this.data.isKaoheguan 临时隐藏考核官
|
||||
showPindaoEntry: this.data.isDashou || this.data.isGuanshi || this.data.isZuzhang,
|
||||
}, () => {
|
||||
this._buildMoreFuncList();
|
||||
});
|
||||
},
|
||||
|
||||
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());
|
||||
@@ -196,11 +232,15 @@ Page(createPage({
|
||||
this.setData({ inviterCache, zuzhangInviterCache });
|
||||
|
||||
this.registerNotificationComponent();
|
||||
warmupPindaoConfig(app);
|
||||
this.checkRoleStatuses();
|
||||
this.applyCachedProfileFromGlobal();
|
||||
this.syncConfiguredAssets();
|
||||
this.loadPenaltyCount();
|
||||
if (isCenterPageActive(this, 'isDashou', 'dashoustatus')) {
|
||||
if (!this.data.isDashou) this.setData({ isDashou: true });
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
this.loadExamStatus();
|
||||
this.loadMyZhidingOrders();
|
||||
} else if (isRoleStatusActive(wx.getStorageSync('guanshistatus')) ||
|
||||
isRoleStatusActive(wx.getStorageSync('zuzhangstatus')) ||
|
||||
isRoleStatusActive(wx.getStorageSync('kaoheguanstatus'))) {
|
||||
@@ -221,6 +261,7 @@ Page(createPage({
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
if (app.startImWhenReady) app.startImWhenReady();
|
||||
},
|
||||
|
||||
checkRoleStatuses() {
|
||||
@@ -237,7 +278,7 @@ Page(createPage({
|
||||
layoutSparse: subRoleCount === 0,
|
||||
}, () => {
|
||||
this._updateTotalAssets();
|
||||
this._updatePendingAuth();
|
||||
this._buildMoreFuncList();
|
||||
});
|
||||
if (isDashou) {
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
@@ -263,97 +304,87 @@ Page(createPage({
|
||||
total += this._parseMoney(d.ketixian);
|
||||
breakdown.push({ label: '组长余额', amount: d.ketixian || '0.00' });
|
||||
}
|
||||
if (d.isKaoheguan) {
|
||||
total += this._parseMoney(d.kaoheYue);
|
||||
breakdown.push({ label: '考核余额', amount: d.kaoheYue || '0.00' });
|
||||
}
|
||||
// 临时隐藏考核官余额展示,恢复时取消注释
|
||||
// if (d.isKaoheguan) {
|
||||
// total += this._parseMoney(d.kaoheYue);
|
||||
// breakdown.push({ label: '考核余额', amount: d.kaoheYue || '0.00' });
|
||||
// }
|
||||
this.setData({ totalAssets: total.toFixed(2), assetBreakdown: breakdown });
|
||||
},
|
||||
|
||||
/** 打手端:已认证不展示;商家始终第一位 */
|
||||
_updatePendingAuth() {
|
||||
/** 更多功能:常用优先,按身份动态展示 */
|
||||
_buildMoreFuncList() {
|
||||
const d = this.data;
|
||||
const img = d.imgUrls || {};
|
||||
const list = [];
|
||||
list.push({
|
||||
type: 'shangjia',
|
||||
name: '商家',
|
||||
icon: img.iconShangjia,
|
||||
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: '去认证' });
|
||||
}
|
||||
this.setData({
|
||||
pendingAuthList: list,
|
||||
showAuthSection: list.length > 0,
|
||||
});
|
||||
},
|
||||
|
||||
applyCachedProfileFromGlobal() {
|
||||
const app = getApp();
|
||||
const g = app.globalData;
|
||||
const guanshi = g.guanshi || {};
|
||||
const patch = {
|
||||
dashouNicheng: g.dashouNicheng || this.data.dashouNicheng || '',
|
||||
zhanghaoStatus: g.zhanghaoStatus ?? this.data.zhanghaoStatus,
|
||||
yongjin: g.yongjin || this.data.yongjin || '0.00',
|
||||
zonge: g.zonge || this.data.zonge || '0.00',
|
||||
yajin: g.yajin || this.data.yajin || '0.00',
|
||||
chenghao: g.chenghao || this.data.chenghao || '',
|
||||
jinfen: g.jinfen || this.data.jinfen || '0',
|
||||
clumber: g.clumber || this.data.clumber || [],
|
||||
chengjiaoliang: g.chengjiaoliang || this.data.chengjiaoliang || '0',
|
||||
zaixianZhuangtai: g.zaixianZhuangtai ?? this.data.zaixianZhuangtai ?? 0,
|
||||
dashouzhuangtai: g.dashouzhuangtai ?? this.data.dashouzhuangtai,
|
||||
gszhstatus: guanshi.gszhstatus || this.data.gszhstatus || '',
|
||||
yaoqingzongshu: guanshi.yaoqingzongshu ?? this.data.yaoqingzongshu ?? 0,
|
||||
fenyongzonge: guanshi.fenyongzonge || this.data.fenyongzonge || '0.00',
|
||||
fenyongtixian: guanshi.fenyongtixian || this.data.fenyongtixian || '0.00',
|
||||
const push = (action, name, icon, extra = {}) => {
|
||||
list.push({ action, name, icon, ...extra });
|
||||
};
|
||||
this.setData(patch, () => this._updateTotalAssets());
|
||||
|
||||
if (d.isZuzhang) {
|
||||
push('goToYaoqingGuanshi', '邀请管事', img.iconInvite);
|
||||
}
|
||||
push('contactInviter', '联系管事', img.iconInvite);
|
||||
if (d.isGuanshi) {
|
||||
push('contactZuzhang', '联系组长', img.iconContact);
|
||||
}
|
||||
push('goToKefu', '在线客服', img.iconKefu);
|
||||
push('onTapShangjiaAuth', d.shangjiaCertified ? '进入商家端' : '商家认证', img.iconShangjia);
|
||||
push('onTapStaffAuth', isStaffMode() ? '商家客服工作台' : '商家客服入驻', img.iconKefu || img.iconShangjia);
|
||||
if (d.showPindaoEntry) {
|
||||
push('openPindaoModal', '频道', img.iconPindao);
|
||||
}
|
||||
|
||||
push('goToMyPunishment', '我的处罚', img.iconPunish);
|
||||
// push('goToKaohe', '考核金牌', img.iconMedal); // 临时隐藏,恢复时取消注释
|
||||
push('goToDashouExam', '接单考试', img.iconBook);
|
||||
push('goToRanking', '总排行榜', img.iconRank, { rankType: 'dashou' });
|
||||
|
||||
// 会员记录 / 分红记录已合并至「佣金记录」页 Tab 切换
|
||||
// 临时隐藏考核官入口,恢复时取消注释
|
||||
// if (d.isKaoheguan) {
|
||||
// push('goToKaoheDafen', '考核打分', img.iconKaoheDafen);
|
||||
// push('goToKaoheJilu', '考核记录', img.iconKaoheJilu);
|
||||
// push('goToKaoheZhongxin', '考核中心', img.iconKaoheZhongxin);
|
||||
// }
|
||||
|
||||
if (!d.isDashou) {
|
||||
push('goToAuth', '接单员认证', img.iconDashouAuth, { authType: 'dashou' });
|
||||
}
|
||||
|
||||
push('goToRules', '用户规则', img.iconBook);
|
||||
push('switchToNormal', '返回点单端', img.iconSwitch);
|
||||
push('clearCache', '退出登录', img.iconClear);
|
||||
|
||||
this.setData({ moreFuncList: list });
|
||||
},
|
||||
|
||||
async onPullDownRefresh() {
|
||||
if (this._pullRefreshing) return;
|
||||
this._pullRefreshing = true;
|
||||
this.setData({ scrollRefreshing: true });
|
||||
try {
|
||||
this.checkRoleStatuses();
|
||||
await Promise.allSettled([
|
||||
this._fetchDashouInfoSilent(),
|
||||
this.fetchChenghaoList(),
|
||||
this._fetchGuanshiInfoSilent(),
|
||||
this.fetchGuanshiChenghaoList(),
|
||||
this._fetchZuzhangInfoSilent(),
|
||||
this._fetchKaoheguanInfoSilent(),
|
||||
]);
|
||||
this.checkRoleStatuses();
|
||||
wx.showToast({ title: '已更新', icon: 'success', duration: 1200 });
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '刷新失败', icon: 'none' });
|
||||
} finally {
|
||||
this._pullRefreshing = false;
|
||||
this.setData({ scrollRefreshing: false });
|
||||
onMoreFuncTap(e) {
|
||||
const { action, authType, rankType } = e.currentTarget.dataset;
|
||||
if (action === 'goToAuth' && authType) {
|
||||
this.goToAuth({ currentTarget: { dataset: { type: authType } } });
|
||||
return;
|
||||
}
|
||||
if (action === 'goToRanking') {
|
||||
this.goToRanking({ currentTarget: { dataset: { type: rankType || 'dashou' } } });
|
||||
return;
|
||||
}
|
||||
if (action === 'onTapStaffAuth') {
|
||||
this.onTapAuthItem({ currentTarget: { dataset: { type: 'staff' } } });
|
||||
return;
|
||||
}
|
||||
if (action === 'onTapShangjiaAuth') {
|
||||
this.onTapShangjiaAuth();
|
||||
return;
|
||||
}
|
||||
if (typeof this[action] === 'function') {
|
||||
this[action]();
|
||||
}
|
||||
},
|
||||
|
||||
async refreshAllInfo(showToast = true) {
|
||||
this.throttledRefresh(async () => {
|
||||
if (this._profileRefreshing) return;
|
||||
this._profileRefreshing = true;
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
this.checkRoleStatuses();
|
||||
@@ -364,6 +395,8 @@ Page(createPage({
|
||||
this.fetchGuanshiChenghaoList(),
|
||||
this._fetchZuzhangInfoSilent(),
|
||||
this._fetchKaoheguanInfoSilent(),
|
||||
this.loadIdentityTags(),
|
||||
this.loadMyZhidingOrders(),
|
||||
]);
|
||||
this.checkRoleStatuses();
|
||||
const dashouOk = results[0].status === 'fulfilled';
|
||||
@@ -379,9 +412,9 @@ Page(createPage({
|
||||
wx.showToast({ title: '刷新失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this._profileRefreshing = false;
|
||||
this.setData({ isLoading: false });
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
refreshDashouInfo() {
|
||||
@@ -392,6 +425,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) {
|
||||
@@ -685,12 +746,15 @@ Page(createPage({
|
||||
|
||||
goToInviteUser() { wx.navigateTo({ url: '/pages/invite-fighter/invite-fighter' }); },
|
||||
goToMySubordinate() { wx.navigateTo({ url: '/pages/my-fighter/my-fighter' }); },
|
||||
goToRechargeRecord() { wx.navigateTo({ url: '/pages/recharge-log/recharge-log' }); },
|
||||
goToRechargeRecord() { wx.navigateTo({ url: '/pages/recharge-log/recharge-log?tab=guanshi' }); },
|
||||
goToCommissionLog(tab = 'dashou') {
|
||||
wx.navigateTo({ url: `/pages/recharge-log/recharge-log?tab=${tab}` });
|
||||
},
|
||||
goToGuanshiRanking() {
|
||||
wx.navigateTo({ url: '/pages/fighter-rank/fighter-rank?type=guanshi' });
|
||||
},
|
||||
goToPromoPoster() { wx.navigateTo({ url: '/pages/poster/poster' }); },
|
||||
goToFenhongJilu() { wx.navigateTo({ url: '/pages/leader-bonus-log/leader-bonus-log' }); },
|
||||
goToFenhongJilu() { wx.navigateTo({ url: '/pages/recharge-log/recharge-log?tab=zuzhang' }); },
|
||||
goToYaoqingGuanshi() { wx.navigateTo({ url: '/pages/invite-manager/invite-manager' }); },
|
||||
goToGuanshiAuthPage() { wx.navigateTo({ url: '/pages/verify/verify?type=guanshi' }); },
|
||||
|
||||
@@ -737,7 +801,7 @@ Page(createPage({
|
||||
}
|
||||
},
|
||||
|
||||
// 联系邀请人
|
||||
// 联系管事(邀请人)
|
||||
async contactInviter() {
|
||||
wx.showLoading({ title: '获取邀请人...', mask: true });
|
||||
try {
|
||||
@@ -1105,15 +1169,174 @@ Page(createPage({
|
||||
}
|
||||
wx.navigateTo({ url: '/pages/verify/verify?type=shangjia' });
|
||||
},
|
||||
goToReceiveOrder() { wx.navigateTo({ url: '/pages/accept-order/accept-order' }) },
|
||||
goToMyOrders() { wx.navigateTo({ url: '/pages/fighter-orders/fighter-orders' }) },
|
||||
goToReceiveOrder() { wx.switchTab({ url: '/pages/accept-order/accept-order' }) },
|
||||
|
||||
async loadMyZhidingOrders() {
|
||||
if (!isCenterPageActive(this, 'isDashou', 'dashoustatus')) return;
|
||||
try {
|
||||
const raw = await fetchMyZhidingOrders();
|
||||
const uid = this.data.uid || wx.getStorageSync('uid');
|
||||
const list = filterZhidingBannerList(processZhidingList(raw, getApp(), uid));
|
||||
this.setData({ myZhidingList: list, myZhidingCount: list.length });
|
||||
} catch (e) {
|
||||
console.warn('fighter loadMyZhidingOrders', e);
|
||||
}
|
||||
},
|
||||
|
||||
goToZhidingFromMine() {
|
||||
const app = getApp();
|
||||
const first = (this.data.myZhidingList || [])[0];
|
||||
if (first && first.dingdan_id) {
|
||||
app.globalData._acceptOrderHighlight = first.dingdan_id;
|
||||
}
|
||||
wx.switchTab({
|
||||
url: '/pages/accept-order/accept-order',
|
||||
fail: () => wx.showToast({ title: '跳转失败', icon: 'none' }),
|
||||
});
|
||||
},
|
||||
|
||||
goToDashouExam() { wx.navigateTo({ url: '/pages/dashou-exam/dashou-exam' }) },
|
||||
|
||||
goToMyOrders() { wx.switchTab({ url: '/pages/fighter-orders/fighter-orders' }) },
|
||||
|
||||
goToOrdersWithStatus(e) {
|
||||
const statusKey = e.currentTarget.dataset.status;
|
||||
if (statusKey) {
|
||||
getApp().globalData._fighterOrdersStatusKey = statusKey;
|
||||
}
|
||||
wx.switchTab({ url: '/pages/fighter-orders/fighter-orders' });
|
||||
},
|
||||
|
||||
goToAllOrders() {
|
||||
getApp().globalData._fighterOrdersStatusKey = 'all';
|
||||
wx.switchTab({ url: '/pages/fighter-orders/fighter-orders' });
|
||||
},
|
||||
|
||||
goToTradeNav(e) {
|
||||
const type = e.currentTarget.dataset.type;
|
||||
if (type === 'commission') {
|
||||
this.goToCommissionLog('dashou');
|
||||
return;
|
||||
}
|
||||
if (type === 'recharge') {
|
||||
if (this.data.isGuanshi) {
|
||||
this.goToCommissionLog('guanshi');
|
||||
} else {
|
||||
this.onComingSoon();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type === 'withdraw') {
|
||||
wx.navigateTo({ url: '/pages/withdraw/withdraw-records' });
|
||||
return;
|
||||
}
|
||||
if (type === 'deduct') {
|
||||
wx.navigateTo({ url: '/pages/fighter-ledger/fighter-ledger?type=deduct' });
|
||||
return;
|
||||
}
|
||||
wx.navigateTo({ url: `/pages/fighter-ledger/fighter-ledger?type=${type}` });
|
||||
},
|
||||
|
||||
goToWalletDetail() {
|
||||
this.goToCommissionLog('dashou');
|
||||
},
|
||||
|
||||
onInviteNavTap(e) {
|
||||
if (!this.data.isGuanshi) {
|
||||
this.goToGuanshiAuthPage();
|
||||
return;
|
||||
}
|
||||
const action = e.currentTarget.dataset.action;
|
||||
if (action === 'subordinate') {
|
||||
this.goToMySubordinate();
|
||||
return;
|
||||
}
|
||||
if (action === 'poster') {
|
||||
this.goToPromoPoster();
|
||||
return;
|
||||
}
|
||||
if (action === 'inviteCode') {
|
||||
this.goToInviteUser();
|
||||
}
|
||||
},
|
||||
|
||||
onComingSoon() {
|
||||
wx.showToast({ title: '该功能待开发', icon: 'none' });
|
||||
},
|
||||
|
||||
toggleFollowKuaishou() {
|
||||
const next = !this.data.followExpanded;
|
||||
this.setData({ followExpanded: next, followClass: next ? 'expanded' : '' });
|
||||
},
|
||||
|
||||
onQiepianTap() {
|
||||
this.onComingSoon();
|
||||
},
|
||||
|
||||
async loadPenaltyCount() {
|
||||
if (!this.data.isDashou && !this.data.isGuanshi) return;
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/cffktjhq', method: 'POST' });
|
||||
const body = res?.data;
|
||||
if (body && (body.code === 200 || body.code === 0) && body.data) {
|
||||
const d = body.data;
|
||||
const cnt = Number(d.daichuli_fakuan || d.fakuan_wait || d.pending || 0);
|
||||
this.setData({ pendingPenaltyCount: cnt > 0 ? cnt : 0 });
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
},
|
||||
|
||||
goToMyPunishment() { wx.navigateTo({ url: '/pages/penalty/penalty/penalty' }) },
|
||||
goToRecharge() { wx.navigateTo({ url: '/pages/fighter-recharge/fighter-recharge' }) },
|
||||
|
||||
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) { /* 静默 */ }
|
||||
},
|
||||
goToRecharge() { wx.navigateTo({ url: '/pages/fighter-recharge/fighter-recharge' }); },
|
||||
goToRechargeMember() { wx.navigateTo({ url: '/pages/fighter-recharge/fighter-recharge' }); },
|
||||
goToRechargeDeposit() { wx.navigateTo({ url: '/pages/fighter-recharge/fighter-deposit' }); },
|
||||
goToRanking(e) {
|
||||
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,10 +2,11 @@
|
||||
"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": "#f5f3ff",
|
||||
"backgroundColor": "#fff8e1",
|
||||
"backgroundTextStyle": "dark",
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 50
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<view class="btn-bg unreg-btn" bindtap="onRegister"><text class="unreg-btn-text">立即注册</text></view>
|
||||
<view class="unreg-btn-secondary" bindtap="quickRegisterAsPlatform"><text>平台直签注册</text></view>
|
||||
<view class="unreg-link" bindtap="switchToNormal"><text>返回点单端</text></view>
|
||||
<view class="unreg-link" bindtap="clearCache"><text>清除缓存,重新登录</text></view>
|
||||
<view class="unreg-link" bindtap="clearCache"><text>退出登录</text></view>
|
||||
</view>
|
||||
</view>
|
||||
<tab-bar/>
|
||||
@@ -23,14 +23,12 @@
|
||||
|
||||
<!-- 已注册 -->
|
||||
<block wx:else>
|
||||
<view class="user-page">
|
||||
<view class="user-page xym-user">
|
||||
<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 class="refresh-float" bindtap="onTapRefresh">
|
||||
<image class="refresh-float-ico" src="{{imgUrls.iconRefresh}}" mode="aspectFit"/>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
@@ -41,11 +39,10 @@
|
||||
refresher-enabled="{{true}}"
|
||||
refresher-threshold="80"
|
||||
refresher-default-style="black"
|
||||
refresher-background="#f5f3ff"
|
||||
refresher-triggered="{{scrollRefreshing}}"
|
||||
refresher-background="#fff8e1"
|
||||
refresher-triggered="{{scrollViewRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
>
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info flexb">
|
||||
<view class="flex">
|
||||
<image class="avatar" src="{{avatarUrl || imgUrls.avatarDefault}}" mode="aspectFill" bindtap="previewAvatar"/>
|
||||
@@ -57,20 +54,23 @@
|
||||
<text class="role-tag">接单员</text>
|
||||
<text class="role-tag" wx:if="{{isGuanshi}}">管事</text>
|
||||
<text class="role-tag" wx:if="{{isZuzhang}}">组长</text>
|
||||
<text class="role-tag" wx:if="{{isKaoheguan}}">考核</text>
|
||||
<!-- 临时隐藏考核身份标签,恢复时取消注释 -->
|
||||
<!-- <text class="role-tag" wx:if="{{isKaoheguan}}">考核</text> -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="sn-row" bindtap="copyUid">
|
||||
<view class="sn-row flex" bindtap="copyUid">
|
||||
<text class="sn">ID: {{uid || '--'}}</text>
|
||||
<image class="copy-ico" src="{{imgUrls.iconCopy}}" mode="aspectFit"/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<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}}"/>
|
||||
@@ -81,11 +81,15 @@
|
||||
<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) -->
|
||||
<view class="wallet-card flexb">
|
||||
<view class="wallet-left">
|
||||
<view bindtap="goToWalletDetail">
|
||||
<view class="w-label">我的佣金(元)</view>
|
||||
<view class="w-num">¥{{totalAssets}}</view>
|
||||
<view class="w-sub">可提现 ¥{{yongjin || '0.00'}} · 查看</view>
|
||||
@@ -96,13 +100,19 @@
|
||||
</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="goToRechargeMember"/>
|
||||
<image wx:if="{{imgUrls.bannerDeposit}}" class="banner-img" src="{{imgUrls.bannerDeposit}}" mode="widthFix" bindtap="goToRechargeDeposit"/>
|
||||
</view>
|
||||
<view class="recharge-text-row flex" wx:else>
|
||||
<view class="recharge-text-btn" catchtap="goToRechargeMember">充值会员</view>
|
||||
<view class="recharge-text-btn" catchtap="goToRechargeDeposit">充值保证金</view>
|
||||
</view>
|
||||
|
||||
<view class="qiepian-wrap" bindtap="onQiepianTap">
|
||||
<image class="qiepian-banner" src="{{qiepianBanner}}" mode="widthFix"/>
|
||||
</view>
|
||||
|
||||
<!-- 资产统计(逍遥梦 freeze-row) -->
|
||||
<view class="freeze-row flexa">
|
||||
<view class="freeze-col flexmc">
|
||||
<text class="freeze-num">{{jinfen || '0'}}</text>
|
||||
@@ -118,125 +128,77 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 角色扩展统计(从左往右排列,不均分撑满) -->
|
||||
<view class="stats-bar" wx:if="{{isGuanshi || isZuzhang || isKaoheguan}}">
|
||||
<view class="stat-cell" wx:if="{{isGuanshi}}"><text class="stat-val">{{yaoqingzongshu || 0}}</text><text class="stat-lbl">邀请总数</text></view>
|
||||
<view class="stat-cell" wx:if="{{isGuanshi}}"><text class="stat-val">{{yichongzhiDashou || 0}}</text><text class="stat-lbl">已充值</text></view>
|
||||
<view class="stat-cell" wx:if="{{isGuanshi}}"><text class="stat-val">{{fenyongzonge || '0.00'}}</text><text class="stat-lbl">分成总额</text></view>
|
||||
<view class="stat-cell" wx:if="{{isGuanshi}}"><text class="stat-val">{{zhuanhualv || 0}}%</text><text class="stat-lbl">有效率</text></view>
|
||||
<view class="stat-cell" wx:if="{{isZuzhang}}"><text class="stat-val">{{guanshiCount || 0}}</text><text class="stat-lbl">邀请管事</text></view>
|
||||
<view class="stat-cell" wx:if="{{isZuzhang}}"><text class="stat-val">{{fenhongZonge || '0.00'}}</text><text class="stat-lbl">分红总额</text></view>
|
||||
<view class="stat-cell" wx:if="{{isKaoheguan}}"><text class="stat-val">{{shenheZongshu || 0}}</text><text class="stat-lbl">审核总数</text></view>
|
||||
<view class="stat-cell" wx:if="{{isKaoheguan}}"><text class="stat-val">{{tongguoZongshu || 0}}</text><text class="stat-lbl">通过总数</text></view>
|
||||
<view class="stat-cell" wx:if="{{isKaoheguan}}"><text class="stat-val">{{pendingShenheCount || 0}}</text><text class="stat-lbl">待审核</text></view>
|
||||
<view wx:if="{{pendingPenaltyCount > 0}}" class="tip-bar flexb" bindtap="goToMyPunishment">
|
||||
<view class="flex">
|
||||
<image class="tip-ico" src="{{penaltyTipIcon}}" mode="aspectFit"/>
|
||||
<text>你有{{pendingPenaltyCount}}条罚单待处理, 前往处理</text>
|
||||
</view>
|
||||
<text class="tip-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<!-- 接单服务(对应逍遥梦「我的订单」四宫格) -->
|
||||
<view class="panel shadow">
|
||||
<view class="panel-title flexb"><text class="lg">接单服务</text></view>
|
||||
<view class="order-nav flexa">
|
||||
<view class="nav-item flexmc" bindtap="goToReceiveOrder">
|
||||
<image class="nav-icon" src="{{imgUrls.iconGame}}" mode="aspectFit"/><text class="sm">抢单大厅</text>
|
||||
<view class="my-box shadow order-nav-box">
|
||||
<view class="panel-title flexb" bindtap="goToAllOrders">
|
||||
<text class="lg">我的订单</text>
|
||||
<text class="muted">全部订单 ›</text>
|
||||
</view>
|
||||
<view class="nav-item flexmc" bindtap="goToMyOrders">
|
||||
<image class="nav-icon" src="{{imgUrls.iconOrders}}" mode="aspectFit"/><text class="sm">我的订单</text>
|
||||
<view class="order-nav">
|
||||
<view wx:for="{{orderNavXym}}" wx:key="name" class="nav-item" data-status="{{item.statusKey}}" bindtap="goToOrdersWithStatus">
|
||||
<view class="icon-wrap">
|
||||
<view wx:if="{{item.showBadge && item.badge > 0}}" class="nav-badge">{{item.badge}}</view>
|
||||
<image class="nav-icon" src="{{item.icon}}" mode="aspectFit"/>
|
||||
</view>
|
||||
<view class="nav-item flexmc" bindtap="goToMyPunishment">
|
||||
<image class="nav-icon" src="{{imgUrls.iconPunish}}" mode="aspectFit"/><text class="sm">我的处罚</text>
|
||||
</view>
|
||||
<view class="nav-item flexmc" bindtap="goToKaohe">
|
||||
<image class="nav-icon" src="{{imgUrls.iconMedal}}" mode="aspectFit"/><text class="sm">考核金牌</text>
|
||||
<text class="sm">{{item.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 荣耀排行榜(横向滑动,省空间) -->
|
||||
<view class="rank-hub panel shadow">
|
||||
<view class="panel-title flexb">
|
||||
<text class="lg">荣耀排行榜</text>
|
||||
<text class="rank-hub-tip">TOP50</text>
|
||||
<view class="my-box shadow trade-box">
|
||||
<view class="section-hd">交易明细</view>
|
||||
<view class="trade-list">
|
||||
<view wx:for="{{tradeNavXym}}" wx:key="name" class="trade-item" data-type="{{item.type}}" bindtap="goToTradeNav">
|
||||
<image class="trade-icon" src="{{item.icon}}" mode="aspectFit"/>
|
||||
<text class="sm">{{item.name}}</text>
|
||||
</view>
|
||||
<scroll-view scroll-x class="rank-hub-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view class="rank-hub-track">
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="my-box shadow invite-box">
|
||||
<view class="section-hd">好友邀请</view>
|
||||
<view class="invite-row">
|
||||
<view wx:for="{{inviteNavXym}}" wx:key="name" class="invite-item" data-action="{{item.action}}" bindtap="onInviteNavTap">
|
||||
<image class="invite-icon" src="{{item.icon}}" mode="aspectFit"/>
|
||||
<text class="sm">{{item.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="my-box shadow more-box">
|
||||
<view class="section-hd">更多功能</view>
|
||||
<view class="func-list">
|
||||
<view
|
||||
wx:for="{{rankBoardList}}"
|
||||
wx:key="type"
|
||||
class="rank-tile {{item.themeClass}}"
|
||||
bindtap="goToRanking"
|
||||
data-type="{{item.type}}"
|
||||
wx:for="{{moreFuncList}}"
|
||||
wx:key="action"
|
||||
class="func-item-xym"
|
||||
bindtap="onMoreFuncTap"
|
||||
data-action="{{item.action}}"
|
||||
data-auth-type="{{item.authType}}"
|
||||
data-rank-type="{{item.rankType}}"
|
||||
>
|
||||
<view class="rank-tile-inner">
|
||||
<image class="rank-tile-icon" src="{{item.icon}}" mode="aspectFit"/>
|
||||
<view class="rank-tile-text">
|
||||
<text class="rank-tile-name">{{item.name}}</text>
|
||||
<text class="rank-tile-sub">{{item.sub}}</text>
|
||||
</view>
|
||||
<text class="rank-tile-go">进入</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 管事功能 -->
|
||||
<view class="panel shadow" wx:if="{{isGuanshi}}">
|
||||
<view class="panel-title"><text class="lg">管事功能</text></view>
|
||||
<view class="func-grid">
|
||||
<view class="func-item flexmc" bindtap="goToInviteUser"><image class="func-icon" src="{{imgUrls.iconInvite}}" mode="aspectFit"/><text class="func-txt">邀请用户</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToMySubordinate"><image class="func-icon" src="{{imgUrls.iconSub}}" mode="aspectFit"/><text class="func-txt">我的下级</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToRechargeRecord"><image class="func-icon" src="{{imgUrls.iconRecord}}" mode="aspectFit"/><text class="func-txt">会员记录</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToPromoPoster"><image class="func-icon" src="{{imgUrls.iconPoster}}" mode="aspectFit"/><text class="func-txt">推广海报</text></view>
|
||||
<view class="func-item flexmc" bindtap="contactZuzhang"><image class="func-icon" src="{{imgUrls.iconContact}}" mode="aspectFit"/><text class="func-txt">联系组长</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 组长功能 -->
|
||||
<view class="panel shadow" wx:if="{{isZuzhang}}">
|
||||
<view class="panel-title"><text class="lg">组长功能</text></view>
|
||||
<view class="func-grid">
|
||||
<view class="func-item flexmc" bindtap="goToFenhongJilu"><image class="func-icon" src="{{imgUrls.iconRecord}}" mode="aspectFit"/><text class="func-txt">分红记录</text></view>
|
||||
<view class="func-item flexmc" bindtap="goToYaoqingGuanshi"><image class="func-icon" src="{{imgUrls.iconInvite}}" mode="aspectFit"/><text class="func-txt">邀请管事</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 考核官功能 -->
|
||||
<view class="panel shadow" wx:if="{{isKaoheguan}}">
|
||||
<view class="panel-title"><text class="lg">考核官功能</text></view>
|
||||
<view class="func-grid">
|
||||
<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>
|
||||
|
||||
<!-- 身份认证 -->
|
||||
<view class="panel shadow" wx:if="{{showAuthSection}}">
|
||||
<view class="panel-title"><text class="lg">身份认证</text></view>
|
||||
<view class="func-grid">
|
||||
<view class="func-item flexmc" wx:for="{{pendingAuthList}}" wx:key="type" bindtap="onTapAuthItem" data-type="{{item.type}}">
|
||||
<image class="func-icon" src="{{item.icon}}" mode="aspectFit"/>
|
||||
<text class="func-txt">{{item.name}}</text>
|
||||
<text class="func-tag {{item.done ? 'func-tag-done' : ''}}">{{item.tag}}</text>
|
||||
<view class="func-icon-wrap"><image class="func-icon-xym" src="{{item.icon}}" mode="aspectFit"/></view>
|
||||
<text class="func-txt-xym">{{item.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更多功能 -->
|
||||
<view class="panel shadow">
|
||||
<view class="panel-title"><text class="lg">更多功能</text></view>
|
||||
<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" 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>
|
||||
<view class="func-item flexmc" bindtap="clearCache"><image class="func-icon" src="{{imgUrls.iconClear}}" mode="aspectFit"/><text class="func-txt">清除缓存</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="load-tip">— 已加载全部 —</view>
|
||||
<view class="content-bottom"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="follow-kuaishou {{followClass}}" bindtap="toggleFollowKuaishou">
|
||||
<image class="ks-icon" src="{{ksIcon}}" mode="aspectFit"/>
|
||||
<text wx:if="{{followExpanded}}" class="ks-text" catchtap="goToGuanzhuKs">关注快手</text>
|
||||
</view>
|
||||
|
||||
<tab-bar/>
|
||||
</view>
|
||||
</block>
|
||||
@@ -247,3 +209,10 @@
|
||||
|
||||
<global-notification id="global-notification"/>
|
||||
<popup-notice id="popupNotice"/>
|
||||
<pindao-modal
|
||||
visible="{{pindaoVisible}}"
|
||||
title="{{pindaoTitle}}"
|
||||
channelNo="{{pindaoChannelNo}}"
|
||||
images="{{pindaoImages}}"
|
||||
bind:close="closePindaoModal"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* 打手端个人中心 - 逍遥梦 UI(逻辑不变) */
|
||||
@import '../../styles/dashou-xym-user.wxss';
|
||||
|
||||
page {
|
||||
background: #f5f3ff;
|
||||
background: #fff8e1;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@@ -16,7 +17,7 @@ page {
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(180deg, #c4b5fd 0%, #fff 28%, #f5f3ff 100%);
|
||||
background: linear-gradient(180deg, #f7dc51 0%, #fff 28%, #fff8e1 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -24,6 +25,7 @@ page {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
flex-shrink: 0;
|
||||
background: linear-gradient(180deg, #f7dc51 0%, #ffd061 100%);
|
||||
}
|
||||
|
||||
.status-bar,
|
||||
@@ -40,6 +42,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;
|
||||
@@ -52,10 +67,6 @@ page {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.content-bottom {
|
||||
height: 12rpx;
|
||||
}
|
||||
|
||||
.refresh-float {
|
||||
position: fixed;
|
||||
right: 24rpx;
|
||||
@@ -112,7 +123,7 @@ page {
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
}
|
||||
|
||||
@@ -134,6 +145,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;
|
||||
@@ -169,7 +194,7 @@ page {
|
||||
margin: 15rpx 30rpx 0;
|
||||
padding: 24rpx 20rpx;
|
||||
border-radius: 26rpx;
|
||||
background: #ede9fe;
|
||||
background: #fef6d4;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -210,7 +235,7 @@ page {
|
||||
font-weight: 700;
|
||||
border: 2rpx solid #fff;
|
||||
border-radius: 60rpx;
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -233,11 +258,28 @@ 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;
|
||||
border-radius: 15rpx;
|
||||
background: #ede9fe;
|
||||
background: #fef6d4;
|
||||
}
|
||||
|
||||
.freeze-col {
|
||||
@@ -259,7 +301,7 @@ page {
|
||||
}
|
||||
|
||||
.btn-bg {
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
border-radius: 30px;
|
||||
color: #492f00;
|
||||
}
|
||||
@@ -300,39 +342,6 @@ page {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.stats-bar {
|
||||
margin: 12rpx 30rpx 0;
|
||||
padding: 16rpx 16rpx 8rpx;
|
||||
background: #fdfcfa;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.stat-cell {
|
||||
width: 25%;
|
||||
padding: 8rpx 0 12rpx;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-val {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.stat-lbl {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin: 20rpx 20rpx 12rpx;
|
||||
background: #fdfcfa;
|
||||
@@ -356,11 +365,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 {
|
||||
@@ -405,7 +438,7 @@ page {
|
||||
}
|
||||
|
||||
.func-tag-done {
|
||||
color: #9333ea;
|
||||
color: #c9a962;
|
||||
}
|
||||
|
||||
.rank-hub-tip {
|
||||
@@ -484,7 +517,7 @@ page {
|
||||
}
|
||||
|
||||
.rank-theme-gold {
|
||||
background: linear-gradient(135deg, #a8842a 0%, #c4b5fd 100%);
|
||||
background: linear-gradient(135deg, #a8842a 0%, #f5d563 100%);
|
||||
}
|
||||
|
||||
.rank-theme-gold .rank-tile-name,
|
||||
@@ -498,7 +531,7 @@ page {
|
||||
}
|
||||
|
||||
.rank-theme-orange {
|
||||
background: linear-gradient(135deg, #7c3aed 0%, #c4b5fd 100%);
|
||||
background: linear-gradient(135deg, #d4652a 0%, #f5a962 100%);
|
||||
}
|
||||
|
||||
/* 未注册 */
|
||||
@@ -576,7 +609,7 @@ page {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 4rpx solid rgba(0, 0, 0, 0.08);
|
||||
border-top-color: #9333ea;
|
||||
border-top-color: #ffd061;
|
||||
border-radius: 50%;
|
||||
animation: fighterSpin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@@ -69,20 +69,20 @@ page {
|
||||
/* 火苗图标燃烧动画 */
|
||||
.fire-icon {
|
||||
animation: fireBurn 0.8s infinite alternate ease-in-out;
|
||||
filter: drop-shadow(0 0 10rpx #9333ea);
|
||||
filter: drop-shadow(0 0 10rpx #ffaa00);
|
||||
}
|
||||
@keyframes fireBurn {
|
||||
0% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 10rpx #9333ea); }
|
||||
25% { transform: scale(1.1) rotate(2deg); filter: drop-shadow(0 0 20rpx #9333ea); }
|
||||
50% { transform: scale(1.05) rotate(-2deg); filter: drop-shadow(0 0 30rpx #9333ea); }
|
||||
75% { transform: scale(1.15) rotate(3deg); filter: drop-shadow(0 0 40rpx #9333ea); }
|
||||
100% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 10rpx #9333ea); }
|
||||
0% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 10rpx #ffaa00); }
|
||||
25% { transform: scale(1.1) rotate(2deg); filter: drop-shadow(0 0 20rpx #ff5500); }
|
||||
50% { transform: scale(1.05) rotate(-2deg); filter: drop-shadow(0 0 30rpx #ffaa00); }
|
||||
75% { transform: scale(1.15) rotate(3deg); filter: drop-shadow(0 0 40rpx #ffaa00); }
|
||||
100% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 10rpx #ffaa00); }
|
||||
}
|
||||
.jinpai-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #9333ea;
|
||||
text-shadow: 0 0 30rpx #9333ea, 0 0 60rpx #9333ea;
|
||||
color: #ffaa00;
|
||||
text-shadow: 0 0 30rpx #ffaa00, 0 0 60rpx #ffaa00;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.refresh-btn {
|
||||
@@ -255,7 +255,7 @@ page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: 2rpx solid #9333ea;
|
||||
border: 2rpx solid #ffaa00;
|
||||
clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%);
|
||||
padding: 15rpx 30rpx;
|
||||
position: relative;
|
||||
@@ -269,7 +269,7 @@ page {
|
||||
.withdraw-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
}
|
||||
.withdraw-glow {
|
||||
position: absolute;
|
||||
@@ -491,7 +491,7 @@ page {
|
||||
content: '';
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border: 4rpx solid #9333ea;
|
||||
border: 4rpx solid #ffaa00;
|
||||
border-radius: 50%;
|
||||
border-bottom-color: transparent;
|
||||
animation: spin 1.5s reverse infinite;
|
||||
|
||||
@@ -2,7 +2,15 @@ const app = getApp();
|
||||
import { formatDate } from '../../static/lib/utils';
|
||||
import { sendGroupMessage } from '../../utils/message-sender';
|
||||
import { getLocalImUserId } from '../../utils/im-user.js';
|
||||
import { resolveAvatarUrl } from '../../utils/avatar.js';
|
||||
import { resolveAvatarUrl, getDefaultAvatarUrl, resolveAvatarFromStorage } from '../../utils/avatar.js';
|
||||
import { getZhuangtaiText, isPairGroupId, normalizeGroupMessage, recordConversationOpen } from '../../utils/group-chat.js';
|
||||
import request from '../../utils/request';
|
||||
import {
|
||||
fetchHistoryMessages,
|
||||
getOldestHistoryTimestamp,
|
||||
mergeHistoryMessages,
|
||||
waitChatImReady,
|
||||
} from '../../utils/chat-history.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -11,6 +19,10 @@ Page({
|
||||
groupAvatar: '',
|
||||
orderId: '',
|
||||
isCross: 0,
|
||||
orderZhuangtai: null,
|
||||
orderZhuangtaiText: '',
|
||||
orderJieshao: '',
|
||||
orderJine: '',
|
||||
currentUser: null,
|
||||
messages: [],
|
||||
inputText: '',
|
||||
@@ -30,7 +42,8 @@ Page({
|
||||
pendingImage: '',
|
||||
pendingImageFile: null,
|
||||
keyboardHeight: 0,
|
||||
bottomSafeHeight: 10
|
||||
bottomSafeHeight: 140,
|
||||
defaultAvatarUrl: '',
|
||||
},
|
||||
onLoad(options) {
|
||||
let p = { groupId: '', groupName: '订单群聊', groupAvatar: '', orderId: '', isCross: 0,
|
||||
@@ -41,37 +54,79 @@ Page({
|
||||
p = { ...p, ...d };
|
||||
} catch (e) {}
|
||||
}
|
||||
const orderIdStr = String(p.orderId || '').replace(/^group_/, '') ||
|
||||
(p.groupId && !isPairGroupId(p.groupId) ? String(p.groupId).replace(/^group_/, '') : '');
|
||||
const groupId = p.groupId
|
||||
? String(p.groupId)
|
||||
: (orderIdStr ? `group_${orderIdStr}` : '');
|
||||
const zt = p.orderZhuangtai;
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
this.setData({
|
||||
groupId: p.groupId,
|
||||
groupId,
|
||||
groupName: p.groupName,
|
||||
groupAvatar: p.groupAvatar,
|
||||
orderId: p.orderId,
|
||||
isCross: p.isCross
|
||||
groupAvatar: resolveAvatarUrl(p.groupAvatar, app) || defAvatar,
|
||||
orderId: orderIdStr,
|
||||
isCross: p.isCross,
|
||||
orderJieshao: p.orderDesc || p.orderJieshao || '',
|
||||
orderJine: p.orderJine || '',
|
||||
orderZhuangtai: zt,
|
||||
orderZhuangtaiText: zt != null ? getZhuangtaiText(zt) : '',
|
||||
defaultAvatarUrl: defAvatar,
|
||||
});
|
||||
wx.setNavigationBarTitle({ title: this.data.groupName });
|
||||
if (p.currentUserId) {
|
||||
this.setData({
|
||||
currentUser: {
|
||||
id: p.currentUserId,
|
||||
name: p.currentUserName || `用户${p.currentUserId.replace(/^[A-Za-z]+/,'')}`,
|
||||
avatar: resolveAvatarUrl(p.currentUserAvatar, app)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.ensureCurrentUser(p);
|
||||
},
|
||||
|
||||
ensureCurrentUser(p) {
|
||||
p = p || {};
|
||||
const localImId = getLocalImUserId(app);
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
const userId = localImId || p.currentUserId || '';
|
||||
if (!userId) {
|
||||
this.initCurrentUser();
|
||||
return;
|
||||
}
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const rawAvatar = p.currentUserAvatar
|
||||
|| wx.getStorageSync('touxiang')
|
||||
|| app.globalData.currentUser?.avatar
|
||||
|| '';
|
||||
const name = p.currentUserName
|
||||
|| app.globalData.currentUser?.name
|
||||
|| wx.getStorageSync('nicheng')
|
||||
|| app.globalData.dashouNicheng
|
||||
|| app.globalData.nicheng
|
||||
|| `用户${uid}`;
|
||||
const avatar = resolveAvatarUrl(rawAvatar, app) || defAvatar;
|
||||
const currentUser = { id: userId, name, avatar: avatar || defAvatar };
|
||||
app.globalData.currentUser = { ...app.globalData.currentUser, ...currentUser };
|
||||
this.setData({ currentUser });
|
||||
},
|
||||
|
||||
getMyChatProfile() {
|
||||
const cu = this.data.currentUser;
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
if (cu && cu.id) {
|
||||
return {
|
||||
id: cu.id,
|
||||
name: cu.name || `用户${wx.getStorageSync('uid') || ''}`,
|
||||
avatar: resolveAvatarUrl(cu.avatar, app) || defAvatar,
|
||||
};
|
||||
}
|
||||
const imId = getLocalImUserId(app);
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
return {
|
||||
id: imId || '',
|
||||
name: wx.getStorageSync('nicheng') || app.globalData.nicheng || `用户${uid}`,
|
||||
avatar: resolveAvatarFromStorage(app) || defAvatar,
|
||||
};
|
||||
},
|
||||
|
||||
initCurrentUser() {
|
||||
const imId = getLocalImUserId(app);
|
||||
const uid = wx.getStorageSync('uid');
|
||||
this.setData({
|
||||
currentUser: {
|
||||
id: imId || ('Boss' + uid),
|
||||
name: app.globalData.currentUser?.name || `用户${uid}`,
|
||||
avatar: resolveAvatarUrl(wx.getStorageSync('touxiang'), app),
|
||||
},
|
||||
});
|
||||
const profile = this.getMyChatProfile();
|
||||
if (!profile.id) return;
|
||||
this.setData({ currentUser: profile });
|
||||
app.globalData.currentUser = { ...app.globalData.currentUser, ...profile };
|
||||
},
|
||||
|
||||
buildImageFile(tempPath, size) {
|
||||
@@ -84,14 +139,99 @@ Page({
|
||||
|
||||
normalizeMessage(msg) {
|
||||
if (!msg) return msg;
|
||||
msg = normalizeGroupMessage(msg);
|
||||
if (!msg.payload) msg.payload = {};
|
||||
if (msg.type === 'image') {
|
||||
const p = msg.payload;
|
||||
if (!p.url) p.url = p.thumbnail || p.thumb || p.imageUrl || '';
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
const me = this.getMyChatProfile();
|
||||
if (me.id && msg.senderId === me.id) {
|
||||
msg.senderData = {
|
||||
name: me.name,
|
||||
avatar: me.avatar || defAvatar,
|
||||
};
|
||||
} else if (msg.senderData?.avatar) {
|
||||
msg.senderData.avatar = resolveAvatarUrl(msg.senderData.avatar, app) || defAvatar;
|
||||
} else if (msg.senderData) {
|
||||
msg.senderData.avatar = defAvatar;
|
||||
}
|
||||
if (msg.type === 'image' && msg.payload?.url && !String(msg.payload.url).startsWith('http')) {
|
||||
msg.payload.url = resolveAvatarUrl(msg.payload.url, app) || msg.payload.url;
|
||||
}
|
||||
return msg;
|
||||
},
|
||||
|
||||
mapIdentityForApi() {
|
||||
const role = app.globalData.currentRole || wx.getStorageSync('currentRole') || 'normal';
|
||||
if (role === 'dashou') return 'dashou';
|
||||
if (role === 'shangjia') return 'shangjia';
|
||||
return 'normal';
|
||||
},
|
||||
|
||||
async refreshLatestOrderBar() {
|
||||
const { groupId, orderId } = this.data;
|
||||
if (!groupId && !orderId) return;
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/ltpddd',
|
||||
method: 'POST',
|
||||
data: {
|
||||
groupId,
|
||||
dingdan_id: orderId,
|
||||
identityType: this.mapIdentityForApi(),
|
||||
},
|
||||
});
|
||||
const body = res?.data || {};
|
||||
const latest = body.data?.latest;
|
||||
if (!latest) return;
|
||||
this.setData({
|
||||
orderId: latest.dingdan_id,
|
||||
orderJieshao: latest.jieshao || '',
|
||||
orderJine: latest.jine || '',
|
||||
orderZhuangtai: latest.zhuangtai,
|
||||
orderZhuangtaiText: getZhuangtaiText(latest.zhuangtai),
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('刷新最新订单条失败', e);
|
||||
}
|
||||
},
|
||||
|
||||
async refreshOrderStatus() {
|
||||
const { orderId } = this.data;
|
||||
if (!orderId) return;
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/ltddzt',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdan_id: orderId,
|
||||
identityType: this.mapIdentityForApi(),
|
||||
},
|
||||
});
|
||||
const body = res?.data || {};
|
||||
const st = body.data;
|
||||
if (!st) return;
|
||||
this.setData({
|
||||
orderJieshao: st.jieshao || this.data.orderJieshao,
|
||||
orderJine: st.jine || this.data.orderJine,
|
||||
orderZhuangtai: st.zhuangtai,
|
||||
orderZhuangtaiText: getZhuangtaiText(st.zhuangtai),
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('刷新订单状态失败', e);
|
||||
}
|
||||
},
|
||||
|
||||
startOrderStatusPoll() {
|
||||
this.stopOrderStatusPoll();
|
||||
this._orderPollTimer = setInterval(() => this.refreshOrderStatus(), 15000);
|
||||
},
|
||||
|
||||
stopOrderStatusPoll() {
|
||||
if (this._orderPollTimer) {
|
||||
clearInterval(this._orderPollTimer);
|
||||
this._orderPollTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
mergeSentMessage(localId, sent) {
|
||||
if (!sent) {
|
||||
this.updateMsg(localId, 'success');
|
||||
@@ -112,54 +252,90 @@ Page({
|
||||
},
|
||||
|
||||
onShow() {
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
this.setData({
|
||||
defaultAvatarUrl: defAvatar,
|
||||
groupAvatar: resolveAvatarUrl(this.data.groupAvatar, app) || defAvatar,
|
||||
});
|
||||
this.ensureCurrentUser({});
|
||||
if (this.data.groupId) {
|
||||
if (!app.globalData.pageState) app.globalData.pageState = {};
|
||||
app.globalData.pageState.lastOpenedConvId = this.data.groupId;
|
||||
recordConversationOpen({ groupId: this.data.groupId });
|
||||
}
|
||||
this.refreshLatestOrderBar();
|
||||
this.startOrderStatusPoll();
|
||||
this.autoConnect();
|
||||
},
|
||||
|
||||
onHide() {
|
||||
if (this.data.groupId) {
|
||||
recordConversationOpen({ groupId: this.data.groupId });
|
||||
}
|
||||
this.stopOrderStatusPoll();
|
||||
this.clearAllListeners();
|
||||
},
|
||||
|
||||
autoConnect() {
|
||||
const targetUserId = getLocalImUserId(app);
|
||||
onUnload() {
|
||||
this.stopOrderStatusPoll();
|
||||
},
|
||||
|
||||
async autoConnect() {
|
||||
const targetUserId = this.data.currentUser?.id || getLocalImUserId(app);
|
||||
if (!targetUserId) return;
|
||||
|
||||
const afterReady = async () => {
|
||||
await this.subscribeGroupIfNeeded();
|
||||
this.setupAllListeners();
|
||||
await this.loadHistory(true);
|
||||
this.markGroupMessageAsRead();
|
||||
};
|
||||
|
||||
try {
|
||||
const ok = await waitChatImReady(app, 12000);
|
||||
if (ok) {
|
||||
await afterReady();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[群聊] IM 连接失败', e);
|
||||
}
|
||||
|
||||
const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
|
||||
if (status === 'connected' || status === 'reconnected') {
|
||||
const currentUserId = wx.goEasy.im ? wx.goEasy.im.userId : null;
|
||||
if (currentUserId === targetUserId) {
|
||||
this.subscribeGroupIfNeeded();
|
||||
this.setupAllListeners();
|
||||
this.loadHistory(true);
|
||||
await afterReady();
|
||||
return;
|
||||
} else {
|
||||
wx.goEasy.disconnect();
|
||||
}
|
||||
wx.goEasy.disconnect();
|
||||
}
|
||||
this.connectGoEasy(targetUserId);
|
||||
},
|
||||
|
||||
connectGoEasy(userId) {
|
||||
const { currentUser } = this.data;
|
||||
if (!currentUser) return;
|
||||
const me = this.getMyChatProfile();
|
||||
if (!me.id) return;
|
||||
const defAvatar = getDefaultAvatarUrl(app);
|
||||
wx.goEasy.connect({
|
||||
id: userId,
|
||||
data: {
|
||||
name: currentUser.name,
|
||||
avatar: resolveAvatarUrl(currentUser.avatar, app),
|
||||
name: me.name,
|
||||
avatar: me.avatar || defAvatar,
|
||||
},
|
||||
onSuccess: () => {
|
||||
this.subscribeGroupIfNeeded();
|
||||
this.subscribeGroupIfNeeded().then(() => {
|
||||
this.setupAllListeners();
|
||||
this.loadHistory(true).then(()=>{
|
||||
this.markGroupMessageAsRead
|
||||
|
||||
this.loadHistory(true);
|
||||
this.markGroupMessageAsRead();
|
||||
});
|
||||
},
|
||||
onFailed: (error) => {
|
||||
if (error.code === 408) {
|
||||
this.subscribeGroupIfNeeded();
|
||||
this.subscribeGroupIfNeeded().then(() => {
|
||||
this.setupAllListeners();
|
||||
this.loadHistory(true);
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: '连接失败,请重试', icon: 'none' });
|
||||
}
|
||||
@@ -169,11 +345,16 @@ Page({
|
||||
|
||||
subscribeGroupIfNeeded() {
|
||||
const { groupId } = this.data;
|
||||
if (!groupId) return;
|
||||
if (!groupId || !wx.goEasy?.im?.subscribeGroup) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
wx.goEasy.im.subscribeGroup({
|
||||
groupIds: [groupId],
|
||||
onSuccess: () => {},
|
||||
onFailed: () => {}
|
||||
groupIds: [String(groupId)],
|
||||
onSuccess: () => resolve(true),
|
||||
onFailed: (error) => {
|
||||
console.warn('subscribeGroup failed', error);
|
||||
resolve(false);
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -197,55 +378,93 @@ Page({
|
||||
|
||||
onPullDownRefresh() {
|
||||
if (this.data.loadingHistory) return;
|
||||
if (!this.data.hasMore) {
|
||||
wx.showToast({ title: '没有更早的消息了', icon: 'none' });
|
||||
this.setData({ isRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
return;
|
||||
}
|
||||
this.setData({ isRefreshing: true });
|
||||
this.loadHistory(false).finally(() => { this.setData({ isRefreshing: false }); wx.stopPullDownRefresh(); });
|
||||
this.loadHistory(false).finally(() => {
|
||||
this.setData({ isRefreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
loadHistory(refresh) {
|
||||
if (!refresh && !this.data.hasMore) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
this.setData({ loadingHistory: true });
|
||||
const { groupId, lastTimestamp } = this.data;
|
||||
const ts = refresh ? null : lastTimestamp;
|
||||
wx.goEasy.im.history({
|
||||
type: wx.GoEasy.IM_SCENE.GROUP,
|
||||
id: groupId,
|
||||
lastTimestamp: ts,
|
||||
limit: 20,
|
||||
onSuccess: (res) => {
|
||||
let list = res.content || [];
|
||||
list.sort((a, b) => a.timestamp - b.timestamp);
|
||||
list.forEach((m, idx) => {
|
||||
mapHistoryMessage(m, idx, list) {
|
||||
m = this.normalizeMessage(m);
|
||||
list[idx] = m;
|
||||
m.formattedTime = formatDate(m.timestamp);
|
||||
if (idx === 0) m.showTime = true;
|
||||
else m.showTime = (m.timestamp - list[idx-1].timestamp) / 60000 > 5;
|
||||
else m.showTime = (m.timestamp - list[idx - 1].timestamp) / 60000 > 5;
|
||||
if (!m.senderData) m.senderData = { name: '未知用户', avatar: '' };
|
||||
return m;
|
||||
},
|
||||
|
||||
async fetchGroupHistoryPage(gid, lastTimestamp) {
|
||||
return fetchHistoryMessages({
|
||||
scene: wx.GoEasy.IM_SCENE.GROUP,
|
||||
id: gid,
|
||||
lastTimestamp,
|
||||
limit: 20,
|
||||
});
|
||||
let final = refresh ? list : [...list, ...this.data.messages];
|
||||
if (!refresh) {
|
||||
for (let i = 0; i < final.length; i++) {
|
||||
if (i === 0) final[i].showTime = true;
|
||||
else final[i].showTime = (final[i].timestamp - final[i-1].timestamp) / 60000 > 5;
|
||||
},
|
||||
|
||||
async loadHistory(refresh) {
|
||||
if (!refresh && !this.data.hasMore) return;
|
||||
if (this.data.loadingHistory) return;
|
||||
if (!this.data.groupId) return;
|
||||
|
||||
this.setData({ loadingHistory: true });
|
||||
try {
|
||||
const ready = await waitChatImReady(app, 12000);
|
||||
if (!ready) {
|
||||
wx.showToast({ title: '消息服务未连接,请稍后重试', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
await this.subscribeGroupIfNeeded();
|
||||
|
||||
const ts = refresh
|
||||
? null
|
||||
: getOldestHistoryTimestamp(this.data.messages, this.data.lastTimestamp);
|
||||
if (!refresh && ts == null) {
|
||||
this.setData({ hasMore: false });
|
||||
wx.showToast({ title: '没有更早的消息了', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
let raw = await this.fetchGroupHistoryPage(this.data.groupId, ts);
|
||||
|
||||
if (refresh && raw.length === 0 && isPairGroupId(this.data.groupId) && this.data.orderId) {
|
||||
const legacyId = `group_${this.data.orderId}`;
|
||||
if (legacyId !== this.data.groupId) {
|
||||
raw = await this.fetchGroupHistoryPage(legacyId, ts);
|
||||
}
|
||||
}
|
||||
const ids = new Set();
|
||||
const unique = [];
|
||||
for (const m of final) { if (!ids.has(m.messageId)) { ids.add(m.messageId); unique.push(m); } }
|
||||
|
||||
const merged = mergeHistoryMessages({
|
||||
incoming: raw,
|
||||
existing: this.data.messages,
|
||||
refresh,
|
||||
limit: 20,
|
||||
mapMessage: (m, idx, list) => this.mapHistoryMessage(m, idx, list),
|
||||
});
|
||||
|
||||
const update = {
|
||||
messages: unique,
|
||||
hasMore: list.length >= 20,
|
||||
lastTimestamp: unique.length ? unique[0].timestamp : lastTimestamp,
|
||||
loadingHistory: false
|
||||
messages: merged.messages,
|
||||
hasMore: merged.hasMore,
|
||||
lastTimestamp: merged.lastTimestamp,
|
||||
};
|
||||
if (refresh) update.scrollToView = 'msg-bottom';
|
||||
this.setData(update);
|
||||
resolve();
|
||||
},
|
||||
onFailed: () => { this.setData({ loadingHistory: false }); resolve(); }
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('[群聊] 历史消息加载失败', e);
|
||||
if (!refresh) {
|
||||
wx.showToast({ title: '加载历史消息失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this.setData({ loadingHistory: false });
|
||||
}
|
||||
},
|
||||
|
||||
listenNewMsg() {
|
||||
@@ -531,7 +750,9 @@ Page({
|
||||
orderId: order.dingdan_id,
|
||||
jieshao: order.jieshao,
|
||||
jine: order.jine,
|
||||
beizhu: order.beizhu
|
||||
beizhu: order.beizhu,
|
||||
zhuangtai: order.zhuangtai,
|
||||
nicheng: order.nicheng,
|
||||
};
|
||||
sendGroupMessage({
|
||||
msgData: {
|
||||
@@ -555,6 +776,10 @@ Page({
|
||||
},
|
||||
onSuccess: (messageId, status) => {
|
||||
that.updateMsg(messageId, status);
|
||||
that.closeOrderSender();
|
||||
if (status === 'success') {
|
||||
wx.showToast({ title: '订单已发送', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -566,5 +791,24 @@ Page({
|
||||
this.setData({ detailText: text, showDetailModal: true });
|
||||
},
|
||||
|
||||
onKeyboardHeightChange(e) { this.setData({ keyboardHeight: e.detail.height }); }
|
||||
onKeyboardHeightChange(e) { this.setData({ keyboardHeight: e.detail.height }); },
|
||||
|
||||
onAvatarError(e) {
|
||||
const def = getDefaultAvatarUrl(app);
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
if (idx != null && idx !== '') {
|
||||
this.setData({ [`messages[${idx}].senderData.avatar`]: def });
|
||||
}
|
||||
},
|
||||
|
||||
onSelfAvatarError() {
|
||||
const def = getDefaultAvatarUrl(app);
|
||||
if (this.data.currentUser) {
|
||||
this.setData({ 'currentUser.avatar': def });
|
||||
}
|
||||
},
|
||||
|
||||
tapOrderBar() {
|
||||
this.openOrderSender();
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"navigationBarTitleText": "群聊",
|
||||
"usingComponents": {
|
||||
"order-card": "/components/order-card/order-card"
|
||||
"order-card": "/components/order-card/order-card",
|
||||
"order-sender": "/components/order-sender/order-sender"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,28 @@
|
||||
<view class="chat-page">
|
||||
<view class="order-status-bar" wx:if="{{orderId}}" bindtap="tapOrderBar">
|
||||
<view class="order-status-main">
|
||||
<view class="order-status-left">
|
||||
<text class="order-status-id">订单 {{orderId}}</text>
|
||||
<text class="order-status-price" wx:if="{{orderJine}}">¥{{orderJine}}</text>
|
||||
</view>
|
||||
<text class="order-status-tag" wx:if="{{orderZhuangtaiText}}">{{orderZhuangtaiText}}</text>
|
||||
</view>
|
||||
<text class="order-status-desc" wx:if="{{orderJieshao}}">{{orderJieshao}}</text>
|
||||
<text class="order-status-hint">点击查看历史订单并发送</text>
|
||||
</view>
|
||||
<scroll-view class="chat-msg-list" scroll-y="true"
|
||||
scroll-into-view="{{scrollToView}}"
|
||||
refresher-enabled="{{true}}"
|
||||
refresher-triggered="{{isRefreshing}}"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
style="top: 20rpx; bottom: calc({{bottomSafeHeight}}rpx + {{keyboardHeight}}px + env(safe-area-inset-bottom) + 20rpx);">
|
||||
style="top: {{orderId ? '120rpx' : '20rpx'}}; bottom: calc({{bottomSafeHeight}}rpx + {{keyboardHeight}}px + env(safe-area-inset-bottom) + 24rpx);">
|
||||
<view class="chat-loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<block wx:for="{{messages}}" wx:key="messageId">
|
||||
<block wx:for="{{messages}}" wx:key="messageId" wx:for-index="msgIdx">
|
||||
<view class="chat-msg-wrapper" data-messageid="{{item.messageId}}" bindlongpress="showAction">
|
||||
<view class="chat-time-tag" wx:if="{{item.showTime}}">{{item.formattedTime}}</view>
|
||||
<view class="chat-msg-row {{item.senderId === currentUser.id ? 'chat-msg-row--right' : 'chat-msg-row--left'}}">
|
||||
<!-- 对方头像及昵称 -->
|
||||
<view wx:if="{{item.senderId !== currentUser.id}}" class="sender-info">
|
||||
<image class="chat-avatar" src="{{item.senderData.avatar || '/images/default-avatar.png'}}" mode="aspectFill" />
|
||||
<image class="chat-avatar" src="{{item.senderData.avatar || defaultAvatarUrl}}" mode="aspectFill" binderror="onAvatarError" data-index="{{msgIdx}}" />
|
||||
<text class="sender-name">{{item.senderData.name || item.senderId}}</text>
|
||||
</view>
|
||||
<view class="chat-bubble {{item.senderId === currentUser.id ? 'chat-bubble--right' : 'chat-bubble--left'}}"
|
||||
@@ -27,15 +37,15 @@
|
||||
<text class="chat-order-info">ID: {{item.payload.orderId}}</text>
|
||||
<text class="chat-order-info">内容: {{item.payload.jieshao}}</text>
|
||||
<text class="chat-order-info">金额: ¥{{item.payload.jine}}</text>
|
||||
<text class="chat-order-info" wx:if="{{item.payload.beizhu}}">备注: {{item.payload.beizhu}}</text>
|
||||
<view class="chat-order-info" style="color:#007aff;margin-top:8rpx">点击查看详情</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 自己的头像 -->
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar}}" mode="aspectFill" />
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar || defaultAvatarUrl}}" mode="aspectFill" binderror="onSelfAvatarError" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view id="msg-bottom" style="height:20rpx;"></view>
|
||||
<view id="msg-bottom" style="height:56rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="chat-input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
@@ -88,5 +98,5 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<order-sender visible="{{showOrderSender}}" bind:send="onSendOrder" bind:close="closeOrderSender" />
|
||||
<order-sender visible="{{showOrderSender}}" group-id="{{groupId}}" order-id="{{orderId}}" bind:send="onSendOrder" bind:close="closeOrderSender" />
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,67 @@
|
||||
/* 使用全局 chat-* 样式 */
|
||||
|
||||
.order-status-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 11;
|
||||
background: #fff;
|
||||
padding: 16rpx 24rpx;
|
||||
border-bottom: 1rpx solid #e8e8e8;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.order-status-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.order-status-id {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.order-status-tag {
|
||||
font-size: 22rpx;
|
||||
color: #07c160;
|
||||
background: #e8f8ee;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.order-status-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.order-status-price {
|
||||
font-size: 24rpx;
|
||||
color: #ff6b00;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.order-status-hint {
|
||||
font-size: 22rpx;
|
||||
color: #007aff;
|
||||
margin-top: 6rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.order-status-desc {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
margin-top: 8rpx;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 发送者昵称样式(群聊特有) */
|
||||
.sender-info {
|
||||
display: flex;
|
||||
|
||||
14
pages/guanshiduan/guanshiduan.js
Normal file
14
pages/guanshiduan/guanshiduan.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/** 旧二维码兼容:guanshiduan → 打手中心管事注册,逻辑不变 */
|
||||
import { parseSceneOptions } from '../../utils/base-page.js';
|
||||
|
||||
Page({
|
||||
onLoad(options) {
|
||||
const parsed = parseSceneOptions(options || {});
|
||||
const inviteCode = parsed.inviteCode || options.inviteCode || '';
|
||||
let url = '/pages/fighter/fighter?registerType=guanshi';
|
||||
if (inviteCode) {
|
||||
url += `&inviteCode=${encodeURIComponent(inviteCode)}`;
|
||||
}
|
||||
wx.reLaunch({ url });
|
||||
},
|
||||
});
|
||||
1
pages/guanshiduan/guanshiduan.json
Normal file
1
pages/guanshiduan/guanshiduan.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "navigationBarTitleText": "加载中", "usingComponents": {} }
|
||||
1
pages/guanshiduan/guanshiduan.wxml
Normal file
1
pages/guanshiduan/guanshiduan.wxml
Normal file
@@ -0,0 +1 @@
|
||||
<view class="tip">正在进入管事注册…</view>
|
||||
1
pages/guanshiduan/guanshiduan.wxss
Normal file
1
pages/guanshiduan/guanshiduan.wxss
Normal file
@@ -0,0 +1 @@
|
||||
.tip { display:flex; align-items:center; justify-content:center; min-height:100vh; color:#999; font-size:28rpx; }
|
||||
@@ -5,6 +5,7 @@ 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';
|
||||
@@ -182,8 +183,7 @@ Page(createPage({
|
||||
*/
|
||||
loadGonggaoAndLunbo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 如果全局已有数据,直接使用
|
||||
if (app.globalData.shangpingonggao && app.globalData.shangpinlunbo.length > 0) {
|
||||
if (isGonggaoCacheValid(app)) {
|
||||
this.setData({
|
||||
shangpingonggao: app.globalData.shangpingonggao,
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo)
|
||||
@@ -192,26 +192,15 @@ Page(createPage({
|
||||
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 || [];
|
||||
fetchGonggaoLunbo(app, 'order_pool')
|
||||
.then((data) => {
|
||||
this.setData({
|
||||
shangpingonggao: app.globalData.shangpingonggao,
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo)
|
||||
shangpingonggao: data.shangpingonggao || '',
|
||||
lunboList: this.processImageUrls(data.shangpinlunbo || [])
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('请求失败'));
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
class="lunbo-swiper"
|
||||
indicator-dots="{{lunboList.length > 1}}"
|
||||
indicator-color="rgba(255,255,255,0.6)"
|
||||
indicator-active-color="#a855f7"
|
||||
indicator-active-color="#ffcc00"
|
||||
autoplay="{{lunboList.length > 1}}"
|
||||
interval="3000"
|
||||
circular
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(220, 255, 220, 0.85) 0%,
|
||||
rgba(237, 233, 254, 0.85) 100%);
|
||||
rgba(255, 255, 200, 0.85) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -120,7 +120,7 @@
|
||||
position: absolute;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: #a855f7;
|
||||
background: #ffcc00;
|
||||
border-radius: 50%;
|
||||
top: 15rpx;
|
||||
right: 15rpx;
|
||||
@@ -195,8 +195,8 @@
|
||||
}
|
||||
|
||||
.leixing-active .leixing-glow {
|
||||
border-color: #a855f7;
|
||||
box-shadow: 0 0 20rpx rgba(168, 85, 247, 0.5);
|
||||
border-color: #ffcc00;
|
||||
box-shadow: 0 0 20rpx rgba(255, 204, 0, 0.5);
|
||||
}
|
||||
|
||||
.leixing-active .leixing-image-box {
|
||||
@@ -239,7 +239,7 @@
|
||||
.zhuanqu-icon-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(168, 85, 247, 0.3));
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(255, 204, 0, 0.3));
|
||||
}
|
||||
|
||||
.zhuanqu-sparkle {
|
||||
@@ -249,11 +249,11 @@
|
||||
@keyframes sparkle {
|
||||
0%, 100% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(168, 85, 247, 0.3));
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(255, 204, 0, 0.3));
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1) rotate(10deg);
|
||||
filter: drop-shadow(0 4rpx 8rpx rgba(168, 85, 247, 0.6));
|
||||
filter: drop-shadow(0 4rpx 8rpx rgba(255, 204, 0, 0.6));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@
|
||||
bottom: -8rpx;
|
||||
width: 60rpx;
|
||||
height: 4rpx;
|
||||
background: linear-gradient(90deg, #a855f7, transparent);
|
||||
background: linear-gradient(90deg, #ffcc00, transparent);
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
|
||||
@@ -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('#9333ea'); // 金黄色,呼应组长主题
|
||||
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' }),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -15,8 +15,8 @@ page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(168, 85, 247, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(168, 85, 247, 0.05) 1px, transparent 1px);
|
||||
linear-gradient(rgba(255, 200, 0, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 200, 0, 0.05) 1px, transparent 1px);
|
||||
background-size: 50rpx 50rpx;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
@@ -41,14 +41,14 @@ page {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2rpx;
|
||||
background: linear-gradient(90deg, transparent, #9333ea, transparent);
|
||||
background: linear-gradient(90deg, transparent, #ffaa00, transparent);
|
||||
}
|
||||
.line-v {
|
||||
right: 50rpx;
|
||||
top: 100rpx;
|
||||
width: 2rpx;
|
||||
height: 300rpx;
|
||||
background: linear-gradient(180deg, transparent, #9333ea, transparent);
|
||||
background: linear-gradient(180deg, transparent, #ffaa00, transparent);
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -71,14 +71,14 @@ page {
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 30rpx #9333ea;
|
||||
text-shadow: 0 0 30rpx #ffaa00;
|
||||
letter-spacing: 2rpx;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #c4b5fd;
|
||||
color: #ffd966;
|
||||
opacity: 0.9;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
@@ -96,11 +96,11 @@ page {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border: 4rpx solid #9333ea;
|
||||
border: 4rpx solid #ffaa00;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 80rpx rgba(147, 51, 234, 0.4);
|
||||
box-shadow: 0 0 80rpx rgba(255, 170, 0, 0.4);
|
||||
animation: qrcodePulse 2s infinite alternate;
|
||||
}
|
||||
.qrcode-img {
|
||||
@@ -114,14 +114,14 @@ page {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(circle at 30% 30%, rgba(147, 51, 234, 0.2), transparent 70%);
|
||||
background: radial-gradient(circle at 30% 30%, rgba(255, 170, 0, 0.2), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.empty-qrcode {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border: 4rpx dashed #9333ea;
|
||||
border: 4rpx dashed #ffaa00;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -129,13 +129,13 @@ page {
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
margin-bottom: 20rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.empty-text {
|
||||
font-size: 26rpx;
|
||||
color: #c4b5fd;
|
||||
color: #ffd966;
|
||||
text-align: center;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
@@ -148,14 +148,14 @@ page {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 20rpx 30rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 2rpx solid #9333ea;
|
||||
border: 2rpx solid #ffaa00;
|
||||
margin-bottom: 30rpx;
|
||||
width: 90%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.code-label {
|
||||
font-size: 28rpx;
|
||||
color: #c4b5fd;
|
||||
color: #ffd966;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
.code-value {
|
||||
@@ -169,23 +169,23 @@ page {
|
||||
.copy-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(147, 51, 234, 0.2);
|
||||
background: rgba(255, 170, 0, 0.2);
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 40rpx;
|
||||
margin-left: 20rpx;
|
||||
border: 2rpx solid #9333ea;
|
||||
border: 2rpx solid #ffaa00;
|
||||
}
|
||||
.copy-icon {
|
||||
font-size: 28rpx;
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
.copy-text {
|
||||
font-size: 24rpx;
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
}
|
||||
.copy-btn:active {
|
||||
background: rgba(147, 51, 234, 0.4);
|
||||
background: rgba(255, 170, 0, 0.4);
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ page {
|
||||
width: 220rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border: 2rpx solid #9333ea;
|
||||
border: 2rpx solid #ffaa00;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%);
|
||||
@@ -219,22 +219,22 @@ page {
|
||||
}
|
||||
.btn:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 0 40rpx #9333ea;
|
||||
box-shadow: 0 0 40rpx #ffaa00;
|
||||
}
|
||||
.save-btn {
|
||||
border-color: #9333ea;
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
.refresh-btn {
|
||||
border-color: #9333ea;
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
.refresh-btn .btn-icon {
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
}
|
||||
.poster-btn {
|
||||
border-color: #9333ea;
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
.poster-btn .btn-icon {
|
||||
color: #9333ea;
|
||||
color: #ffaa00;
|
||||
}
|
||||
.loading-btn {
|
||||
opacity: 0.7;
|
||||
@@ -254,7 +254,7 @@ page {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
border: 4rpx solid rgba(255,255,255,0.2);
|
||||
border-top: 4rpx solid #9333ea;
|
||||
border-top: 4rpx solid #ffaa00;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 10rpx;
|
||||
@@ -272,8 +272,8 @@ page {
|
||||
width: 600rpx;
|
||||
height: 900rpx;
|
||||
background: #1a1f2e;
|
||||
border: 4rpx solid #9333ea;
|
||||
box-shadow: 0 0 60rpx #9333ea;
|
||||
border: 4rpx solid #ffaa00;
|
||||
box-shadow: 0 0 60rpx #ffaa00;
|
||||
}
|
||||
.poster-actions {
|
||||
display: flex;
|
||||
@@ -282,12 +282,12 @@ page {
|
||||
justify-content: center;
|
||||
}
|
||||
.save-poster-btn {
|
||||
background: linear-gradient(45deg, #6d28d9, #9333ea);
|
||||
border-color: #9333ea;
|
||||
background: linear-gradient(45deg, #cc8400, #ffaa00);
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
.back-btn {
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border-color: #9333ea;
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
|
||||
/* 使用说明 */
|
||||
@@ -295,19 +295,19 @@ page {
|
||||
margin-top: 30rpx;
|
||||
padding: 20rpx 40rpx;
|
||||
background: rgba(20, 20, 20, 0.5);
|
||||
border-left: 4rpx solid #9333ea;
|
||||
border-left: 4rpx solid #ffaa00;
|
||||
clip-path: polygon(0 0, 100% 0, 98% 100%, 0 100%);
|
||||
}
|
||||
.instruction-text {
|
||||
font-size: 24rpx;
|
||||
color: #c4b5fd;
|
||||
color: #ffd966;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes qrcodePulse {
|
||||
0% { box-shadow: 0 0 40rpx rgba(147, 51, 234, 0.3); }
|
||||
100% { box-shadow: 0 0 100rpx rgba(147, 51, 234, 0.6); }
|
||||
0% { box-shadow: 0 0 40rpx rgba(255, 170, 0, 0.3); }
|
||||
100% { box-shadow: 0 0 100rpx rgba(255, 170, 0, 0.6); }
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "关注星阙",
|
||||
"navigationBarTitleText": "星之界",
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
|
||||
67
pages/merchant-data-stats/merchant-data-stats.js
Normal file
67
pages/merchant-data-stats/merchant-data-stats.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import { fetchMerchantOrderStats, applyOrderStatsToPage } from '../../utils/merchant-order-stats.js';
|
||||
|
||||
Page(createPage({
|
||||
data: {
|
||||
statusBar: 20,
|
||||
loading: true,
|
||||
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,
|
||||
},
|
||||
jinriliushui: '0.00',
|
||||
jinyueliushui: '0.00',
|
||||
jinridingdan: 0,
|
||||
jinrituikuan: 0,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({ statusBar: sys.statusBarHeight || 20 });
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadData().finally(() => wx.stopPullDownRefresh());
|
||||
},
|
||||
|
||||
async loadData() {
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const statsData = await fetchMerchantOrderStats(request, {});
|
||||
applyOrderStatsToPage(this, statsData);
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/shangjiaxinxi', method: 'POST' });
|
||||
if (res.data && res.data.code === 200) {
|
||||
const d = res.data.data || {};
|
||||
this.setData({
|
||||
jinriliushui: d.jinriliushui || '0.00',
|
||||
jinyueliushui: d.jinyueliushui || '0.00',
|
||||
jinridingdan: d.jinripaidan || 0,
|
||||
jinrituikuan: d.jinrituikuan || 0,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('商家流水加载失败', e);
|
||||
}
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack();
|
||||
},
|
||||
}));
|
||||
5
pages/merchant-data-stats/merchant-data-stats.json
Normal file
5
pages/merchant-data-stats/merchant-data-stats.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
56
pages/merchant-data-stats/merchant-data-stats.wxml
Normal file
56
pages/merchant-data-stats/merchant-data-stats.wxml
Normal file
@@ -0,0 +1,56 @@
|
||||
<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}}" refresher-enabled refresher-triggered="{{loading}}" bindrefresherrefresh="onPullDownRefresh">
|
||||
<view class="sj-panel sj-mx20 data-panel">
|
||||
<view class="data-highlight flexb">
|
||||
<view class="data-hl-item">
|
||||
<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 class="data-cell">
|
||||
<text class="data-cell-num">{{jinridingdan}}</text>
|
||||
<text class="data-cell-lbl">今日派单</text>
|
||||
</view>
|
||||
<view class="data-cell">
|
||||
<text class="data-cell-num">{{jinrituikuan}}</text>
|
||||
<text class="data-cell-lbl">今日退款</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="penalty-mini" wx:if="{{penaltyStats.fakuan_pending || penaltyStats.jifen_pending}}">
|
||||
<text>处罚待处理:罚单 {{penaltyStats.fakuan_pending}}(¥{{penaltyStats.fakuan_pending_amount}})· 积分 {{penaltyStats.jifen_pending}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
84
pages/merchant-data-stats/merchant-data-stats.wxss
Normal file
84
pages/merchant-data-stats/merchant-data-stats.wxss
Normal file
@@ -0,0 +1,84 @@
|
||||
@import '../../styles/shangjia-xym-common.wxss';
|
||||
|
||||
page { background: #fff8e1; }
|
||||
|
||||
.sub-scroll { height: calc(100vh - 120rpx); }
|
||||
|
||||
.data-panel { padding: 24rpx; margin-top: 16rpx; }
|
||||
|
||||
.data-highlight {
|
||||
margin-bottom: 24rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.data-hl-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 12rpx;
|
||||
}
|
||||
|
||||
.data-hl-num {
|
||||
display: block;
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.data-hl-num.accent { color: #e65100; }
|
||||
|
||||
.data-hl-lbl {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.data-hl-sub {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.data-cell {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 16rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.data-cell-num {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.data-cell-lbl {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #888;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.data-cell-sub {
|
||||
display: block;
|
||||
font-size: 20rpx;
|
||||
color: #aaa;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.penalty-mini {
|
||||
margin-top: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ Page(createPage({
|
||||
dingdanJieshao: '', // 订单介绍
|
||||
dingdanBeizhu: '', // 订单备注
|
||||
laobanName: '', // 老板游戏昵称/UID
|
||||
waibuDingdanId: '', // 外部平台订单号
|
||||
zhidingUid: '', // 指定打手UID
|
||||
jiage: '', // 订单价格
|
||||
|
||||
@@ -214,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 })
|
||||
},
|
||||
@@ -342,6 +346,7 @@ Page(createPage({
|
||||
dingdanJieshao: '',
|
||||
dingdanBeizhu: '',
|
||||
laobanName: '',
|
||||
waibuDingdanId: '',
|
||||
zhidingUid: '',
|
||||
jiage: '',
|
||||
selectedLabelId: '',
|
||||
@@ -371,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,
|
||||
// 新增字段
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"popup-notice": "/components/popup-notice/popup-notice"
|
||||
},
|
||||
"backgroundTextStyle": "dark",
|
||||
"navigationBarBackgroundColor": "#fff8e1",
|
||||
"navigationBarBackgroundColor": "#f7dc51",
|
||||
"navigationBarTitleText": "自定义发单",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f5f5f5",
|
||||
"backgroundColor": "#fff8e1",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
@@ -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">
|
||||
@@ -144,7 +159,7 @@
|
||||
<text class="field-optional">选填</text>
|
||||
</view>
|
||||
<view class="switch-row">
|
||||
<switch checked="{{commissionEnabled}}" bindchange="onCommissionToggle" color="#9333ea"/>
|
||||
<switch checked="{{commissionEnabled}}" bindchange="onCommissionToggle" color="#ffd061"/>
|
||||
<text class="switch-label">{{commissionEnabled ? '已开启' : '未开启'}}</text>
|
||||
</view>
|
||||
<input
|
||||
|
||||
@@ -5,9 +5,21 @@ import {
|
||||
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();
|
||||
|
||||
const XYM_HOME_FALLBACK = {
|
||||
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',
|
||||
iconRegular: 'https://bintao.xmxym88.com/xcx/bintao/74.png',
|
||||
iconCustom: 'https://bintao.xmxym88.com/xcx/bintao/76.png',
|
||||
};
|
||||
|
||||
/** 与订单页「全部」Tab 一致的状态筛选 */
|
||||
const ALL_ORDER_ZHUANGTAI = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const RECENT_ORDER_SIZE = 5;
|
||||
@@ -27,16 +39,24 @@ Page(createPage({
|
||||
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: XYM_HOME_FALLBACK.noticeIco,
|
||||
statCardBg: XYM_HOME_FALLBACK.statCardBg,
|
||||
kefuKeyBtn: XYM_HOME_FALLBACK.kefuKeyBtn,
|
||||
kefuListBtn: XYM_HOME_FALLBACK.kefuListBtn,
|
||||
iconRegular: XYM_HOME_FALLBACK.iconRegular,
|
||||
iconCustom: XYM_HOME_FALLBACK.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,
|
||||
@@ -54,18 +74,30 @@ Page(createPage({
|
||||
}
|
||||
this.checkRole();
|
||||
syncStaffUi(this);
|
||||
this.waitForConfigAndLoadBanner();
|
||||
this.setupImageUrls();
|
||||
await this.waitForConfigAndLoadBanner();
|
||||
if (this.data.isShangjia) {
|
||||
await this.loadDashboard(false);
|
||||
}
|
||||
this._merchantHomeSessionReady = true;
|
||||
this._skipShowRefresh = true;
|
||||
this.checkColdStartPopup('shangjiaduan');
|
||||
},
|
||||
|
||||
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) {
|
||||
@@ -78,11 +110,26 @@ Page(createPage({
|
||||
syncStaffUi(this);
|
||||
applyStaffStatsToPage(this, getStaffContext());
|
||||
}
|
||||
|
||||
if (this.data.isShangjia) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
this.loadDashboard();
|
||||
}
|
||||
this.loadBannerData();
|
||||
if (app.startImWhenReady) app.startImWhenReady();
|
||||
|
||||
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() {
|
||||
@@ -96,10 +143,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, XYM_HOME_FALLBACK.noticeIco),
|
||||
statCardBg: icon(ICON_KEYS.MERCHANT_HOME_STAT_BG, `${ossBase}beijing/shangjiaduan/stat_card_bg.png`) || XYM_HOME_FALLBACK.statCardBg,
|
||||
kefuKeyBtn: icon(ICON_KEYS.MERCHANT_HOME_KEFU_KEY, XYM_HOME_FALLBACK.kefuKeyBtn),
|
||||
kefuListBtn: icon(ICON_KEYS.MERCHANT_HOME_KEFU_LIST, XYM_HOME_FALLBACK.kefuListBtn),
|
||||
iconRegular: icon(ICON_KEYS.MERCHANT_HOME_REGULAR, XYM_HOME_FALLBACK.iconRegular),
|
||||
iconCustom: icon(ICON_KEYS.MERCHANT_HOME_CUSTOM, XYM_HOME_FALLBACK.iconCustom),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
processImageUrls(urlList) {
|
||||
@@ -109,13 +182,13 @@ Page(createPage({
|
||||
},
|
||||
|
||||
checkRole() {
|
||||
this.setData({ isShangjia: isMerchantPortalUser() });
|
||||
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 || '',
|
||||
@@ -123,41 +196,20 @@ Page(createPage({
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!forceFetch && g.shangpingonggao) {
|
||||
this.setData({
|
||||
lunboList: this.processImageUrls(cachedList),
|
||||
gonggao: g.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 || [];
|
||||
return fetchGonggaoLunbo(app, 'merchant_home').then((data) => {
|
||||
if (!data) return;
|
||||
this.setData({
|
||||
lunboList: this.processImageUrls(app.globalData.shangpinlunbo),
|
||||
gonggao: app.globalData.shangpingonggao,
|
||||
});
|
||||
}
|
||||
},
|
||||
complete: resolve,
|
||||
});
|
||||
gonggao: app.globalData.shangpingonggao || '',
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
async loadDashboard() {
|
||||
async loadDashboard(silent = false) {
|
||||
await Promise.all([
|
||||
this.loadShangjiaBrief(),
|
||||
this.loadPendingStats(),
|
||||
this.loadRecentOrders(),
|
||||
this.loadRecentOrders(silent),
|
||||
]);
|
||||
},
|
||||
|
||||
@@ -191,36 +243,21 @@ Page(createPage({
|
||||
|
||||
async loadPendingStats() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: isStaffMode() ? STAFF_API.orderList : '/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;
|
||||
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: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
|
||||
@@ -253,20 +290,35 @@ Page(createPage({
|
||||
} catch (e) {
|
||||
console.error('最近订单加载失败', e);
|
||||
} finally {
|
||||
this._recentOrdersRequesting = false;
|
||||
if (!silent) {
|
||||
this.setData({ recentLoading: false });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSwiperChange(e) {
|
||||
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() {
|
||||
|
||||
@@ -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,18 +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>
|
||||
|
||||
<view class="staff-banner sj-mx20" wx:if="{{isStaffMode}}">
|
||||
<text class="staff-banner-txt">商家客服 · {{staffRoleName}} · 可用额度 ¥{{quotaAvailable}}</text>
|
||||
</view>
|
||||
|
||||
<view class="sj-fadan-row" wx:if="{{canDispatch}}">
|
||||
<view class="sj-fadan-img-btn sj-fadan-img-btn--left" bindtap="goExpress"/>
|
||||
<view class="sj-fadan-img-btn sj-fadan-img-btn--right" bindtap="goDispatch"/>
|
||||
<view class="sj-fadan-block" wx:if="{{canDispatch}}">
|
||||
<view class="sj-fadan-row">
|
||||
<view class="sj-fadan-img-btn sj-fadan-img-btn--left" bindtap="goRegularDispatch"/>
|
||||
<view class="sj-fadan-img-btn sj-fadan-img-btn--right" 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">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import '../../styles/shangjia-xym-common.wxss';
|
||||
|
||||
page {
|
||||
background: #f5f3ff;
|
||||
background: #fff8e1;
|
||||
}
|
||||
|
||||
.sj-page--home {
|
||||
@@ -85,14 +85,14 @@ page {
|
||||
.dot.active {
|
||||
width: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #9333ea;
|
||||
background: #ffd061;
|
||||
}
|
||||
|
||||
.sj-notice-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fdf9db;
|
||||
margin: 0 24rpx;
|
||||
margin: 12rpx 24rpx 0;
|
||||
border-radius: 10rpx;
|
||||
padding: 10rpx 16rpx;
|
||||
}
|
||||
@@ -102,6 +102,10 @@ page {
|
||||
height: 56rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12rpx;
|
||||
background-image: url('https://bintao.xmxym88.com/xcx/bintao/38.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.sj-notice-txt {
|
||||
@@ -111,11 +115,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 {
|
||||
@@ -127,16 +142,41 @@ page {
|
||||
|
||||
.sj-fadan-img-btn--left {
|
||||
background-image: url('https://bintao.xmxym88.com/xcx/bintao/74.png');
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.sj-fadan-img-btn--right {
|
||||
background-image: url('https://bintao.xmxym88.com/xcx/bintao/76.png');
|
||||
margin-left: 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-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;
|
||||
@@ -151,8 +191,10 @@ page {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: url('https://bintao.xmxym88.com/xcx/bintao/3.png');
|
||||
background-size: 100% auto;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
@@ -311,7 +353,7 @@ page {
|
||||
.staff-banner {
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
background: #f5f3ff;
|
||||
background: #fff8e8;
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx solid #f0d89a;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
padding: 16rpx 32rpx;
|
||||
min-height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
background: linear-gradient(180deg, #c4b5fd, #a78bfa);
|
||||
background: linear-gradient(180deg, #fae04d, #ffc0a3);
|
||||
color: #492f00;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
@@ -108,8 +108,8 @@
|
||||
}
|
||||
|
||||
.tips-bar {
|
||||
background: #f5f3ff;
|
||||
border: 1rpx solid #c4b5fd;
|
||||
background: #fff7e6;
|
||||
border: 1rpx solid #ffd591;
|
||||
border-radius: 12rpx;
|
||||
padding: 18rpx 20rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user