优化了样式,抽象出了 WXSS/JS 库
This commit is contained in:
1 parent
6fbae9b32c
commit
3dc03f6fe5
51 files changed
+3736
-5617
No files matched your search
+38
-93
@@ -1,18 +1,9 @@
|
||||
// pages/assessor/assessor.js
|
||||
// 只新增了两个字段的接收,其余所有代码未变
|
||||
import request from '../../utils/request.js';
|
||||
import {
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
ensureRoleOnCenterPage,
|
||||
} from '../../utils/role-tab-bar.js';
|
||||
const app = getApp();
|
||||
import { createPage, request, isCenterPageActive, ensureRoleOnCenterPage } from '../../utils/base-page.js';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
isKaoheguan: false,
|
||||
isLoading: false,
|
||||
inviteCode: '',
|
||||
statusText: '用户尚未注册或账号已被封禁',
|
||||
uid: '',
|
||||
@@ -32,11 +23,9 @@ Page({
|
||||
zonge: '0.00',
|
||||
shenheZhuangtai: 0,
|
||||
bankuaiList: [],
|
||||
isRenzheng: false, // 🆕 是否认证
|
||||
pendingShenheCount: 0 // 🆕 审核中数量
|
||||
isRenzheng: false,
|
||||
pendingShenheCount: 0
|
||||
},
|
||||
lastRefreshTime: 0,
|
||||
canRefresh: true,
|
||||
planetPaused: {
|
||||
top: false,
|
||||
bottom: false,
|
||||
@@ -50,54 +39,31 @@ Page({
|
||||
onLoad() {
|
||||
wx.redirectTo({ url: '/pages/fighter/fighter' });
|
||||
return;
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const iconBase = 'beijing/kaohe/';
|
||||
this.setData({
|
||||
'icons.refresh': ossUrl + iconBase + 'shuaxin.png',
|
||||
'icons.tixian': ossUrl + iconBase + 'tixian.png',
|
||||
'icons.daofen': ossUrl + iconBase + 'daofen.png',
|
||||
'icons.jilu': ossUrl + iconBase + 'jilu.png',
|
||||
'icons.zhongxin': ossUrl + iconBase + 'zhongxin.png',
|
||||
'icons.bgImage': ossUrl + iconBase + 'top_bg.jpg'
|
||||
const icons = this.initImageUrls({
|
||||
'icons.refresh': 'beijing/kaohe/shuaxin.png',
|
||||
'icons.tixian': 'beijing/kaohe/tixian.png',
|
||||
'icons.daofen': 'beijing/kaohe/daofen.png',
|
||||
'icons.jilu': 'beijing/kaohe/jilu.png',
|
||||
'icons.zhongxin': 'beijing/kaohe/zhongxin.png',
|
||||
'icons.bgImage': 'beijing/kaohe/top_bg.jpg'
|
||||
});
|
||||
this.setData(icons);
|
||||
|
||||
const kaoheguanstatus = wx.getStorageSync('kaoheguanstatus');
|
||||
if (isRoleStatusActive(kaoheguanstatus)) {
|
||||
this.loadUserInfo();
|
||||
this.setData({ isKaoheguan: true });
|
||||
this.checkRoleStatus();
|
||||
if (this.data.isKaoheguan) {
|
||||
this.getKaoheguanInfo();
|
||||
ensureRoleOnCenterPage(this, 'kaoheguan');
|
||||
} else {
|
||||
this.setData({ isKaoheguan: false });
|
||||
}
|
||||
},
|
||||
|
||||
onReady() {
|
||||
if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) {
|
||||
ensureRoleOnCenterPage(this, 'kaoheguan');
|
||||
}
|
||||
this.checkColdStartPopup('kaoheguanduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) {
|
||||
if (!this.data.isKaoheguan) this.setData({ isKaoheguan: true });
|
||||
ensureRoleOnCenterPage(this, 'kaoheguan');
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
this.refreshKaoheInfo();
|
||||
}
|
||||
},
|
||||
|
||||
loadUserInfo() {
|
||||
const uid = wx.getStorageSync('uid') || '';
|
||||
const touxiang = wx.getStorageSync('touxiang') || '';
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg');
|
||||
let avatarUrl = defaultAvatar;
|
||||
if (touxiang) {
|
||||
avatarUrl = ossUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang);
|
||||
}
|
||||
this.setData({ uid, avatarUrl });
|
||||
},
|
||||
|
||||
async getKaoheguanInfo() {
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
@@ -120,11 +86,11 @@ Page({
|
||||
zonge: data.zonge || '0.00',
|
||||
shenheZhuangtai: data.shenhe_zhuangtai || 0,
|
||||
bankuaiList: data.bankuai_list || [],
|
||||
isRenzheng: data.is_renzheng || false, // 🆕
|
||||
pendingShenheCount: data.pending_shenhe_count || 0 // 🆕
|
||||
isRenzheng: data.is_renzheng || false,
|
||||
pendingShenheCount: data.pending_shenhe_count || 0
|
||||
}
|
||||
});
|
||||
ensureRoleOnCenterPage(this, 'kaoheguan');
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
} else {
|
||||
wx.showToast({ title: res?.data?.msg || '获取信息失败', icon: 'none' });
|
||||
this.setData({ isLoading: false });
|
||||
@@ -141,51 +107,22 @@ Page({
|
||||
},
|
||||
|
||||
async onRegister() {
|
||||
const { inviteCode } = this.data;
|
||||
if (!inviteCode) {
|
||||
wx.showToast({ title: '请输入邀请码', icon: 'none', duration: 2000 });
|
||||
return;
|
||||
}
|
||||
if (inviteCode.length > 100) {
|
||||
wx.showToast({ title: '邀请码不能超过100位', icon: 'none', duration: 2000 });
|
||||
return;
|
||||
}
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dengji/khgzc',
|
||||
method: 'POST',
|
||||
data: { inviteCode: inviteCode }
|
||||
});
|
||||
if (res && res.data.code === 200) {
|
||||
const data = res.data.data || {};
|
||||
syncRoleStatuses(data);
|
||||
if (!Object.prototype.hasOwnProperty.call(data, 'kaoheguanstatus')) {
|
||||
wx.setStorageSync('kaoheguanstatus', 1);
|
||||
}
|
||||
wx.showToast({ title: '注册成功', icon: 'success', duration: 2000 });
|
||||
this.registerWithInviteCode({
|
||||
inviteCode: this.data.inviteCode,
|
||||
apiUrl: '/dengji/khgzc',
|
||||
role: 'dashou',
|
||||
statusKey: 'kaoheguanstatus',
|
||||
successMsg: '注册成功',
|
||||
onSuccess: async () => {
|
||||
this.loadUserInfo();
|
||||
await this.getKaoheguanInfo();
|
||||
ensureRoleOnCenterPage(this, 'kaoheguan');
|
||||
} else {
|
||||
wx.showToast({ title: res?.data?.msg || '注册失败', icon: 'none', duration: 2000 });
|
||||
this.setData({ isLoading: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error);
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
this.setData({ isLoading: false });
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
refreshKaoheInfo() {
|
||||
const now = Date.now();
|
||||
const lastTime = this.data.lastRefreshTime;
|
||||
const canRefresh = this.data.canRefresh;
|
||||
if (!canRefresh || (now - lastTime < 3000)) return;
|
||||
this.setData({ lastRefreshTime: now, canRefresh: false });
|
||||
this.getKaoheguanInfo();
|
||||
setTimeout(() => this.setData({ canRefresh: true }), 3000);
|
||||
this.throttledRefresh(this.getKaoheguanInfo);
|
||||
},
|
||||
|
||||
goToWithdraw() {
|
||||
@@ -215,4 +152,12 @@ Page({
|
||||
this.setData({ [paused]: false });
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
roleConfig: {
|
||||
role: 'dashou',
|
||||
statusKey: 'kaoheguanstatus',
|
||||
dataFlag: 'isKaoheguan',
|
||||
pageId: 'kaoheguanduan',
|
||||
apiUrl: '/dengji/khgsx'
|
||||
}
|
||||
}));
|
||||
@@ -1,7 +1,8 @@
|
||||
// pages/category/category.js
|
||||
const app = getApp()
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
ossImageUrl: '',
|
||||
searchText: '',
|
||||
@@ -33,17 +34,6 @@ Page({
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
initPageData() {
|
||||
const that = this
|
||||
this.setData({ isLoading: true })
|
||||
@@ -328,4 +318,4 @@ Page({
|
||||
path: '/pages/category/category'
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
+43
-43
@@ -1,31 +1,31 @@
|
||||
<view class="chat-page">
|
||||
<scroll-view class="msg-list" scroll-y="true"
|
||||
<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);">
|
||||
<view class="loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<view class="chat-loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<block wx:for="{{messages}}" wx:key="messageId">
|
||||
<view class="msg-wrapper" data-messageid="{{item.messageId}}" bindlongpress="showAction">
|
||||
<view class="time-tag" wx:if="{{item.showTime}}">{{item.formattedTime}}</view>
|
||||
<view class="msg-row {{item.senderId === currentUser.id ? 'right' : 'left'}}">
|
||||
<image wx:if="{{item.senderId !== currentUser.id}}" class="avatar" src="{{item.senderData.avatar || toAvatar}}" mode="aspectFill" binderror="onPeerAvatarError" />
|
||||
<view class="bubble {{item.senderId === currentUser.id ? 'bubble-right' : 'bubble-left'}}"
|
||||
<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" />
|
||||
<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 : ''}}"
|
||||
bindtap="{{item.type==='text' ? 'onBubbleTap' : (item.type==='image' ? 'previewImage' : '')}}"
|
||||
data-url="{{item.type==='image' ? item.payload.url : ''}}">
|
||||
<text wx:if="{{item.type === 'text'}}" class="msg-text">{{item.payload.text}}</text>
|
||||
<image wx:elif="{{item.type === 'image'}}" class="msg-image" src="{{item.payload.url}}" mode="widthFix" />
|
||||
<view wx:elif="{{item.type === 'order'}}" class="order-bubble">
|
||||
<text class="order-label">[订单]</text>
|
||||
<text class="order-info">ID: {{item.payload.orderId}}</text>
|
||||
<text wx:if="{{item.type === 'text'}}" class="chat-msg-text">{{item.payload.text}}</text>
|
||||
<image wx:elif="{{item.type === 'image'}}" class="chat-msg-image" src="{{item.payload.url}}" mode="widthFix" />
|
||||
<view wx:elif="{{item.type === 'order'}}" class="chat-order-bubble">
|
||||
<text class="chat-order-label">[订单]</text>
|
||||
<text class="chat-order-info">ID: {{item.payload.orderId}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="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="{{item.senderData.avatar || currentUser.avatar}}" mode="aspectFill" binderror="onSelfAvatarError" data-index="{{index}}" />
|
||||
</view>
|
||||
<view wx:if="{{item.senderId === currentUser.id && item.status === 'success'}}" class="read-tag">
|
||||
<view wx:if="{{item.senderId === currentUser.id && item.status === 'success'}}" class="chat-read-tag">
|
||||
{{item.read ? '已读' : '未读'}}
|
||||
</view>
|
||||
</view>
|
||||
@@ -33,53 +33,53 @@
|
||||
<view id="msg-bottom" style="height:20rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
<view class="plus-panel {{showPlusPanel ? 'show' : ''}}" catchtap="closePlusPanel">
|
||||
<view class="plus-grid">
|
||||
<view class="plus-item" catchtap="openEmojiFromPlus">
|
||||
<text class="plus-icon">😀</text>
|
||||
<text class="plus-label">表情</text>
|
||||
<view class="chat-input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
<view class="chat-plus-panel {{showPlusPanel ? 'show' : ''}}" catchtap="closePlusPanel">
|
||||
<view class="chat-plus-grid">
|
||||
<view class="chat-plus-item" catchtap="openEmojiFromPlus">
|
||||
<text class="chat-plus-icon">😀</text>
|
||||
<text class="chat-plus-label">表情</text>
|
||||
</view>
|
||||
<view class="plus-item" catchtap="chooseImage">
|
||||
<text class="plus-icon">📷</text>
|
||||
<text class="plus-label">图片</text>
|
||||
<view class="chat-plus-item" catchtap="chooseImage">
|
||||
<text class="chat-plus-icon">📷</text>
|
||||
<text class="chat-plus-label">图片</text>
|
||||
</view>
|
||||
<view class="plus-item" catchtap="openOrderSender">
|
||||
<text class="plus-icon">📋</text>
|
||||
<text class="plus-label">订单</text>
|
||||
<view class="chat-plus-item" catchtap="openOrderSender">
|
||||
<text class="chat-plus-icon">📋</text>
|
||||
<text class="chat-plus-label">订单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="emoji-mask" wx:if="{{showEmojiPanel}}" catchtap="closeEmojiPanel"></view>
|
||||
<view class="emoji-panel {{showEmojiPanel ? 'show' : ''}}">
|
||||
<scroll-view scroll-y="true" class="emoji-scroll">
|
||||
<view class="emoji-grid">
|
||||
<view class="chat-emoji-mask" wx:if="{{showEmojiPanel}}" catchtap="closeEmojiPanel"></view>
|
||||
<view class="chat-emoji-panel {{showEmojiPanel ? 'show' : ''}}">
|
||||
<scroll-view scroll-y="true" class="chat-emoji-scroll">
|
||||
<view class="chat-emoji-grid">
|
||||
<block wx:for="{{emojiList}}" wx:key="*this">
|
||||
<view class="emoji-item" data-emoji="{{item}}" catchtap="insertEmoji">{{item}}</view>
|
||||
<view class="chat-emoji-item" data-emoji="{{item}}" catchtap="insertEmoji">{{item}}</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="pending-image" wx:if="{{pendingImage}}">
|
||||
<image class="pending-thumb" src="{{pendingImage}}" mode="aspectFill" />
|
||||
<view class="pending-remove" catchtap="clearPendingImage">✕</view>
|
||||
<view class="chat-pending-image" wx:if="{{pendingImage}}">
|
||||
<image class="chat-pending-thumb" src="{{pendingImage}}" mode="aspectFill" />
|
||||
<view class="chat-pending-remove" catchtap="clearPendingImage">✕</view>
|
||||
</view>
|
||||
|
||||
<view class="write-row">
|
||||
<input class="text-input" placeholder="消息" value="{{inputText}}" bindinput="onInput" adjust-position="{{false}}" bindkeyboardheightchange="onKeyboardHeightChange" cursor-spacing="10"/>
|
||||
<view class="send-btn" bindtap="sendMessage">发送</view>
|
||||
<view class="plus-btn" bindtap="togglePlusPanel">
|
||||
<text class="plus-icon-single">➕</text>
|
||||
<view class="chat-write-row">
|
||||
<input class="chat-text-input" placeholder="消息" value="{{inputText}}" bindinput="onInput" adjust-position="{{false}}" bindkeyboardheightchange="onKeyboardHeightChange" cursor-spacing="10"/>
|
||||
<view class="chat-send-btn" bindtap="sendMessage">发送</view>
|
||||
<view class="chat-plus-btn" bindtap="togglePlusPanel">
|
||||
<text class="chat-plus-icon-single">➕</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-modal" wx:if="{{showDetailModal}}" catchtap="hideDetail">
|
||||
<view class="detail-content" catchtap="noop">
|
||||
<text class="detail-text" user-select="true">{{detailText}}</text>
|
||||
<button class="copy-btn" bindtap="copyDetail">复制</button>
|
||||
<view class="chat-detail-modal" wx:if="{{showDetailModal}}" catchtap="hideDetail">
|
||||
<view class="chat-detail-content" catchtap="noop">
|
||||
<text class="chat-detail-text" user-select="true">{{detailText}}</text>
|
||||
<button class="chat-copy-btn" bindtap="copyDetail">复制</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
+1
-98
@@ -1,98 +1 @@
|
||||
.chat-page { width:100%; height:100vh; background:#f5f5f5; }
|
||||
|
||||
.msg-list {
|
||||
position: absolute;
|
||||
left:0; right:0;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.loading-more { text-align:center; font-size:24rpx; color:#999; padding:20rpx 0; }
|
||||
.time-tag { text-align:center; margin:28rpx 0; font-size:24rpx; color:#b0b0b0; }
|
||||
.msg-wrapper { margin-bottom:12rpx; }
|
||||
.msg-row { display:flex; align-items:flex-start; margin-bottom:4rpx; }
|
||||
.right { justify-content:flex-end; }
|
||||
.left { justify-content:flex-start; }
|
||||
.avatar { width:76rpx; height:76rpx; border-radius:50%; background:#e0e0e0; margin:0 12rpx; flex-shrink:0; }
|
||||
.bubble { max-width:70%; padding:16rpx 20rpx; border-radius:16rpx; position:relative; word-break:break-all; font-size:30rpx; line-height:1.5; }
|
||||
.bubble-left { background:#fff; border-top-left-radius:4rpx; }
|
||||
.bubble-right { background:#00aaff; color:#fff; border-top-right-radius:4rpx; }
|
||||
.msg-text { color:inherit; }
|
||||
.msg-image { max-width:240rpx; border-radius:10rpx; }
|
||||
.order-bubble { background:#fff7e0; padding:12rpx; border-radius:8rpx; }
|
||||
.order-label { font-size:24rpx; color:#e6a23c; font-weight:600; }
|
||||
.order-info { font-size:24rpx; color:#333; margin:6rpx 0; }
|
||||
|
||||
.read-tag {
|
||||
font-size:20rpx;
|
||||
color:#888;
|
||||
text-align:right;
|
||||
padding:4rpx 12rpx 0 0;
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
position: fixed;
|
||||
left:0; right:0;
|
||||
background:#f7f7f7;
|
||||
border-top:1rpx solid #ddd;
|
||||
z-index:10;
|
||||
}
|
||||
|
||||
.plus-panel {
|
||||
position: absolute;
|
||||
bottom:100%; left:0; right:0;
|
||||
background:#fff;
|
||||
border-top:1rpx solid #e0e0e0;
|
||||
padding: 20rpx;
|
||||
transform: translateY(100%);
|
||||
opacity:0;
|
||||
transition: transform 0.3s, opacity 0.3s;
|
||||
pointer-events:none;
|
||||
z-index:9;
|
||||
}
|
||||
.plus-panel.show { transform: translateY(0); opacity:1; pointer-events:auto; }
|
||||
.plus-grid { display: flex; justify-content: space-around; }
|
||||
.plus-item { display: flex; flex-direction: column; align-items: center; width: 120rpx; }
|
||||
.plus-icon { font-size: 60rpx; }
|
||||
.plus-label { font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
|
||||
.emoji-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:transparent; z-index:8; }
|
||||
.emoji-panel {
|
||||
position: absolute; bottom:100%; left:0; right:0;
|
||||
background:#fff; border-top:1rpx solid #e0e0e0;
|
||||
height:380rpx;
|
||||
transform: translateY(100%); opacity:0; transition:0.3s;
|
||||
pointer-events:none; z-index:9;
|
||||
}
|
||||
.emoji-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; }
|
||||
.emoji-scroll { height:100%; }
|
||||
.emoji-grid { display:flex; flex-wrap:wrap; padding:20rpx 10rpx; }
|
||||
.emoji-item { width:14.28%; text-align:center; padding:16rpx 0; font-size:44rpx; }
|
||||
|
||||
.pending-image { display:flex; align-items:center; padding:10rpx 20rpx; background:#fff; }
|
||||
.pending-thumb { width:80rpx; height:80rpx; border-radius:8rpx; }
|
||||
.pending-remove { font-size:30rpx; color:#999; margin-left:10rpx; }
|
||||
|
||||
.write-row { display:flex; padding:0 20rpx 10rpx; align-items:center; }
|
||||
.text-input {
|
||||
flex:1; height:76rpx; background:#fff; border-radius:12rpx;
|
||||
padding:0 20rpx; font-size:30rpx; margin-right:12rpx;
|
||||
}
|
||||
.send-btn {
|
||||
width:120rpx; height:76rpx; background:#00aaff; color:#fff;
|
||||
font-size:28rpx; font-weight:500; border-radius:12rpx;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
}
|
||||
.plus-btn {
|
||||
width:56rpx; height:56rpx;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
.plus-icon-single { font-size:46rpx; color:#00aaff; }
|
||||
|
||||
.detail-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:999; display:flex; align-items:center; justify-content:center; }
|
||||
.detail-content { width:80%; max-height:80%; background:#fff; border-radius:16rpx; padding:40rpx; }
|
||||
.detail-text { font-size:32rpx; line-height:1.6; word-break:break-all; overflow-y:auto; }
|
||||
.copy-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:20rpx; font-size:30rpx; }
|
||||
/* 使用全局 chat-* 样式 */
|
||||
+54
-54
@@ -1,37 +1,37 @@
|
||||
<view class="chat-page">
|
||||
<scroll-view class="msg-list" scroll-y="true"
|
||||
<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);">
|
||||
<view class="loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<view class="chat-loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<block wx:for="{{messages}}" wx:key="messageId">
|
||||
<view class="msg-wrapper" data-messageid="{{item.messageId}}" bindlongpress="showAction">
|
||||
<view class="time-tag" wx:if="{{item.showTime}}">{{item.formattedTime}}</view>
|
||||
<view class="msg-row {{item.senderId === currentUser.id ? 'right' : 'left'}}">
|
||||
<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="avatar" src="{{teamAvatar}}" mode="aspectFill" />
|
||||
<view class="bubble {{item.senderId === currentUser.id ? 'bubble-right' : 'bubble-left'}}"
|
||||
<image wx:if="{{item.senderId !== currentUser.id}}" class="chat-avatar" src="{{teamAvatar}}" mode="aspectFill" />
|
||||
<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 : ''}}"
|
||||
bindtap="{{item.type==='text' ? 'onBubbleTap' : (item.type==='image' ? 'previewImage' : '')}}"
|
||||
data-url="{{item.type==='image' ? item.payload.url : ''}}">
|
||||
<text wx:if="{{item.type === 'text'}}" class="msg-text">{{item.payload.text}}</text>
|
||||
<image wx:elif="{{item.type === 'image'}}" class="msg-image" src="{{item.payload.url}}" mode="widthFix" data-url="{{item.payload.url}}" bindtap="previewImage"/>
|
||||
<view wx:elif="{{item.type === 'order'}}" class="order-bubble">
|
||||
<text class="order-label">📋 订单消息</text>
|
||||
<view class="order-brief">
|
||||
<text wx:if="{{item.type === 'text'}}" class="chat-msg-text">{{item.payload.text}}</text>
|
||||
<image wx:elif="{{item.type === 'image'}}" class="chat-msg-image" src="{{item.payload.url}}" mode="widthFix" data-url="{{item.payload.url}}" bindtap="previewImage"/>
|
||||
<view wx:elif="{{item.type === 'order'}}" class="chat-order-bubble">
|
||||
<text class="chat-order-label">📋 订单消息</text>
|
||||
<view class="chat-order-info">
|
||||
<text>订单号:{{item.payload.dingdan_id || '无'}}</text>
|
||||
<text>服务:{{item.payload.jieshao || ''}}</text>
|
||||
<text class="order-price">¥{{item.payload.jine}}</text>
|
||||
<text style="color:#e6a23c;font-weight:bold">¥{{item.payload.jine}}</text>
|
||||
</view>
|
||||
<button class="order-detail-btn" catchtap="showOrderDetailPopup" data-order="{{item.payload}}" size="mini">查看详情</button>
|
||||
<button class="chat-copy-btn" catchtap="showOrderDetailPopup" data-order="{{item.payload}}" size="mini">查看详情</button>
|
||||
</view>
|
||||
<text wx:else class="msg-text">{{item.payload.text || '[未知消息]'}}</text>
|
||||
<text wx:else class="chat-msg-text">{{item.payload.text || '[未知消息]'}}</text>
|
||||
</view>
|
||||
<!-- 自己头像 -->
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="avatar" src="{{currentUser.avatar}}" mode="aspectFill" />
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar}}" mode="aspectFill" />
|
||||
</view>
|
||||
<!-- 🔥 已读/未读标签已彻底移除 -->
|
||||
</view>
|
||||
@@ -40,67 +40,67 @@
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部输入区域(保持不变) -->
|
||||
<view class="input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
<view class="plus-panel {{showPlusPanel ? 'show' : ''}}" catchtap="closePlusPanel">
|
||||
<view class="plus-grid">
|
||||
<view class="plus-item" catchtap="openEmojiFromPlus">
|
||||
<text class="plus-icon">😀</text>
|
||||
<text class="plus-label">表情</text>
|
||||
<view class="chat-input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
<view class="chat-plus-panel {{showPlusPanel ? 'show' : ''}}" catchtap="closePlusPanel">
|
||||
<view class="chat-plus-grid">
|
||||
<view class="chat-plus-item" catchtap="openEmojiFromPlus">
|
||||
<text class="chat-plus-icon">😀</text>
|
||||
<text class="chat-plus-label">表情</text>
|
||||
</view>
|
||||
<view class="plus-item" catchtap="chooseImage">
|
||||
<text class="plus-icon">📷</text>
|
||||
<text class="plus-label">图片</text>
|
||||
<view class="chat-plus-item" catchtap="chooseImage">
|
||||
<text class="chat-plus-icon">📷</text>
|
||||
<text class="chat-plus-label">图片</text>
|
||||
</view>
|
||||
<view class="plus-item" catchtap="openOrderSender">
|
||||
<text class="plus-icon">📋</text>
|
||||
<text class="plus-label">订单</text>
|
||||
<view class="chat-plus-item" catchtap="openOrderSender">
|
||||
<text class="chat-plus-icon">📋</text>
|
||||
<text class="chat-plus-label">订单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="emoji-mask" wx:if="{{showEmojiPanel}}" catchtap="closeEmojiPanel"></view>
|
||||
<view class="emoji-panel {{showEmojiPanel ? 'show' : ''}}">
|
||||
<scroll-view scroll-y="true" class="emoji-scroll">
|
||||
<view class="emoji-grid">
|
||||
<view class="chat-emoji-mask" wx:if="{{showEmojiPanel}}" catchtap="closeEmojiPanel"></view>
|
||||
<view class="chat-emoji-panel {{showEmojiPanel ? 'show' : ''}}">
|
||||
<scroll-view scroll-y="true" class="chat-emoji-scroll">
|
||||
<view class="chat-emoji-grid">
|
||||
<block wx:for="{{emojiList}}" wx:key="*this">
|
||||
<view class="emoji-item" data-emoji="{{item}}" catchtap="insertEmoji">{{item}}</view>
|
||||
<view class="chat-emoji-item" data-emoji="{{item}}" catchtap="insertEmoji">{{item}}</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="pending-image" wx:if="{{pendingImage}}">
|
||||
<image class="pending-thumb" src="{{pendingImage}}" mode="aspectFill" />
|
||||
<view class="pending-remove" catchtap="clearPendingImage">✕</view>
|
||||
<view class="chat-pending-image" wx:if="{{pendingImage}}">
|
||||
<image class="chat-pending-thumb" src="{{pendingImage}}" mode="aspectFill" />
|
||||
<view class="chat-pending-remove" catchtap="clearPendingImage">✕</view>
|
||||
</view>
|
||||
|
||||
<view class="write-row">
|
||||
<input class="text-input" placeholder="消息" value="{{inputText}}" bindinput="onInput" adjust-position="{{false}}" bindkeyboardheightchange="onKeyboardHeightChange" cursor-spacing="10"/>
|
||||
<view class="send-btn" bindtap="sendMessage">发送</view>
|
||||
<view class="plus-btn" bindtap="togglePlusPanel">
|
||||
<text class="plus-icon-single">➕</text>
|
||||
<view class="chat-write-row">
|
||||
<input class="chat-text-input" placeholder="消息" value="{{inputText}}" bindinput="onInput" adjust-position="{{false}}" bindkeyboardheightchange="onKeyboardHeightChange" cursor-spacing="10"/>
|
||||
<view class="chat-send-btn" bindtap="sendMessage">发送</view>
|
||||
<view class="chat-plus-btn" bindtap="togglePlusPanel">
|
||||
<text class="chat-plus-icon-single">➕</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文本双击放大弹窗 -->
|
||||
<view class="detail-modal" wx:if="{{showDetailModal}}" catchtap="hideDetail">
|
||||
<view class="detail-content" catchtap="noop">
|
||||
<text class="detail-text" user-select="true">{{detailText}}</text>
|
||||
<button class="copy-btn" bindtap="copyDetail">复制</button>
|
||||
<view class="chat-detail-modal" wx:if="{{showDetailModal}}" catchtap="hideDetail">
|
||||
<view class="chat-detail-content" catchtap="noop">
|
||||
<text class="chat-detail-text" user-select="true">{{detailText}}</text>
|
||||
<button class="chat-copy-btn" bindtap="copyDetail">复制</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单详情弹窗 -->
|
||||
<view class="order-detail-mask" wx:if="{{showOrderDetail}}" catchtap="hideOrderDetail">
|
||||
<view class="order-detail-panel" catchtap="noop">
|
||||
<view class="order-detail-title">订单详情</view>
|
||||
<view class="order-detail-item">订单号:{{detailOrder.dingdan_id}}</view>
|
||||
<view class="order-detail-item">服务描述:{{detailOrder.jieshao}}</view>
|
||||
<view class="order-detail-item">金额:¥{{detailOrder.jine}}</view>
|
||||
<view class="order-detail-item">类型:{{detailOrder.leixing || '无'}}</view>
|
||||
<view class="order-detail-item">时间:{{detailOrder.shijian || '无'}}</view>
|
||||
<button class="order-detail-close-btn" catchtap="hideOrderDetail">关闭</button>
|
||||
<view class="chat-detail-modal" wx:if="{{showOrderDetail}}" catchtap="hideOrderDetail">
|
||||
<view class="chat-detail-content" catchtap="noop">
|
||||
<view class="chat-detail-text">订单详情</view>
|
||||
<view class="chat-detail-text">订单号:{{detailOrder.dingdan_id}}</view>
|
||||
<view class="chat-detail-text">服务描述:{{detailOrder.jieshao}}</view>
|
||||
<view class="chat-detail-text">金额:¥{{detailOrder.jine}}</view>
|
||||
<view class="chat-detail-text">类型:{{detailOrder.leixing || '无'}}</view>
|
||||
<view class="chat-detail-text">时间:{{detailOrder.shijian || '无'}}</view>
|
||||
<button class="chat-copy-btn" catchtap="hideOrderDetail">关闭</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,67 +1 @@
|
||||
.chat-page { width:100%; height:100vh; background:#f5f5f5; }
|
||||
.msg-list { position:absolute; left:0; right:0; padding:0 20rpx; box-sizing:border-box; overflow-y:auto; }
|
||||
.loading-more { text-align:center; font-size:24rpx; color:#999; padding:20rpx 0; }
|
||||
.time-tag { text-align:center; margin:28rpx 0; font-size:24rpx; color:#b0b0b0; }
|
||||
.msg-wrapper { margin-bottom:12rpx; }
|
||||
.msg-row { display:flex; align-items:flex-start; margin-bottom:4rpx; }
|
||||
.right { justify-content:flex-end; }
|
||||
.left { justify-content:flex-start; }
|
||||
.avatar { width:76rpx; height:76rpx; border-radius:50%; background:#e0e0e0; margin:0 12rpx; flex-shrink:0; }
|
||||
.bubble { max-width:70%; padding:16rpx 20rpx; border-radius:16rpx; position:relative; word-break:break-all; font-size:30rpx; line-height:1.5; }
|
||||
.bubble-left { background:#fff; border-top-left-radius:4rpx; }
|
||||
.bubble-right { background:#00aaff; color:#fff; border-top-right-radius:4rpx; }
|
||||
.msg-text { color:inherit; }
|
||||
.msg-image { max-width:240rpx; border-radius:10rpx; }
|
||||
|
||||
/* 已读/未读标签:靠右显示 */
|
||||
.read-tag {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
font-size: 20rpx;
|
||||
color: #888;
|
||||
padding: 4rpx 12rpx 0 0;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
|
||||
/* 订单气泡 */
|
||||
.order-bubble { background:#fff7e0; padding:12rpx; border-radius:8rpx; }
|
||||
.order-label { font-size:24rpx; color:#e6a23c; font-weight:600; margin-bottom:8rpx; }
|
||||
.order-brief { font-size:24rpx; color:#333; line-height:1.6; }
|
||||
.order-price { color:#e6a23c; font-weight:bold; }
|
||||
.order-detail-btn { margin-top:10rpx; background:#e6a23c; color:#fff; border-radius:8rpx; }
|
||||
|
||||
/* 订单详情弹窗 */
|
||||
.order-detail-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.6); z-index:999; display:flex; justify-content:center; align-items:center; }
|
||||
.order-detail-panel { width:80%; background:#fff; border-radius:20rpx; padding:30rpx; }
|
||||
.order-detail-title { font-size:34rpx; font-weight:bold; margin-bottom:20rpx; color:#333; }
|
||||
.order-detail-item { font-size:28rpx; margin:16rpx 0; color:#555; }
|
||||
.order-detail-close-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:30rpx; font-size:30rpx; }
|
||||
|
||||
/* 底部输入区域 */
|
||||
.input-area { position:fixed; left:0; right:0; background:#f7f7f7; border-top:1rpx solid #ddd; z-index:10; }
|
||||
.plus-panel { position:absolute; bottom:100%; left:0; right:0; background:#fff; border-top:1rpx solid #e0e0e0; padding:20rpx; transform:translateY(100%); opacity:0; transition:0.3s; pointer-events:none; z-index:9; }
|
||||
.plus-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; }
|
||||
.plus-grid { display:flex; justify-content:space-around; }
|
||||
.plus-item { display:flex; flex-direction:column; align-items:center; width:120rpx; }
|
||||
.plus-icon { font-size:60rpx; }
|
||||
.plus-label { font-size:24rpx; color:#666; margin-top:8rpx; }
|
||||
.emoji-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:transparent; z-index:8; }
|
||||
.emoji-panel { position:absolute; bottom:100%; left:0; right:0; background:#fff; border-top:1rpx solid #e0e0e0; height:380rpx; transform:translateY(100%); opacity:0; transition:0.3s; pointer-events:none; z-index:9; }
|
||||
.emoji-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; }
|
||||
.emoji-scroll { height:100%; }
|
||||
.emoji-grid { display:flex; flex-wrap:wrap; padding:20rpx 10rpx; }
|
||||
.emoji-item { width:14.28%; text-align:center; padding:16rpx 0; font-size:44rpx; }
|
||||
.pending-image { display:flex; align-items:center; padding:10rpx 20rpx; background:#fff; }
|
||||
.pending-thumb { width:80rpx; height:80rpx; border-radius:8rpx; }
|
||||
.pending-remove { font-size:30rpx; color:#999; margin-left:10rpx; }
|
||||
.write-row { display:flex; padding:0 20rpx 10rpx; align-items:center; }
|
||||
.text-input { flex:1; height:76rpx; background:#fff; border-radius:12rpx; padding:0 20rpx; font-size:30rpx; margin-right:12rpx; }
|
||||
.send-btn { width:120rpx; height:76rpx; background:#00aaff; color:#fff; font-size:28rpx; font-weight:500; border-radius:12rpx; display:flex; align-items:center; justify-content:center; }
|
||||
.plus-btn { width:56rpx; height:56rpx; display:flex; align-items:center; justify-content:center; margin-left:10rpx; }
|
||||
.plus-icon-single { font-size:46rpx; color:#00aaff; }
|
||||
|
||||
/* 双击放大文本弹窗 */
|
||||
.detail-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:999; display:flex; align-items:center; justify-content:center; }
|
||||
.detail-content { width:80%; max-height:80%; background:#fff; border-radius:16rpx; padding:40rpx; }
|
||||
.detail-text { font-size:32rpx; line-height:1.6; word-break:break-all; overflow-y:auto; }
|
||||
.copy-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:20rpx; font-size:30rpx; }
|
||||
/* 使用全局 chat-* 样式 */
|
||||
@@ -215,14 +215,14 @@
|
||||
|
||||
<!-- 弹窗底部按钮 -->
|
||||
<view class="modal-footer">
|
||||
<view class="btn-group">
|
||||
<view class="cbtn-group">
|
||||
<!-- 联系客服按钮(始终显示) -->
|
||||
<view class="btn btn-kefu" bindtap="lianxiKefu">
|
||||
<view class="cbtn cbtn-kefu" bindtap="lianxiKefu">
|
||||
<text>联系客服</text>
|
||||
</view>
|
||||
<!-- 申诉按钮(只有待处罚状态可点击) -->
|
||||
<view
|
||||
class="btn {{ xuanzhongChufa.sqzhuangtai === 0 ? 'btn-shensu' : 'btn-shensu-disabled' }}"
|
||||
class="cbtn {{ xuanzhongChufa.sqzhuangtai === 0 ? 'cbtn-shensu' : 'cbtn-shensu-disabled' }}"
|
||||
bindtap="{{ xuanzhongChufa.sqzhuangtai === 0 ? 'openShensuModal' : '' }}"
|
||||
>
|
||||
<text>申诉</text>
|
||||
@@ -299,11 +299,11 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<view class="btn-group">
|
||||
<view class="btn btn-quxiao" bindtap="closeShensuModal">
|
||||
<view class="cbtn-group">
|
||||
<view class="cbtn cbtn-quxiao" bindtap="closeShensuModal">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
<view class="btn btn-queren" bindtap="submitShensu">
|
||||
<view class="cbtn cbtn-queren" bindtap="submitShensu">
|
||||
<text>提交申诉</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -625,12 +625,12 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.cbtn-group {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
.cbtn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
display: flex;
|
||||
@@ -645,37 +645,37 @@
|
||||
color: var(--cyber-text) !important;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
.cbtn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-kefu {
|
||||
.cbtn-kefu {
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border-color: var(--cyber-blue);
|
||||
box-shadow: 0 0 12rpx rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.btn-shensu {
|
||||
.cbtn-shensu {
|
||||
background: rgba(255, 165, 0, 0.1);
|
||||
border-color: #FFA500;
|
||||
box-shadow: 0 0 12rpx rgba(255, 165, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-shensu-disabled {
|
||||
.cbtn-shensu-disabled {
|
||||
background: rgba(102, 102, 102, 0.1);
|
||||
border-color: #666;
|
||||
box-shadow: 0 0 12rpx rgba(102, 102, 102, 0.2);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.btn-quxiao {
|
||||
.cbtn-quxiao {
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 0 12rpx rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.btn-queren {
|
||||
.cbtn-queren {
|
||||
background: rgba(0, 255, 157, 0.1);
|
||||
border-color: var(--cyber-green);
|
||||
box-shadow: 0 0 12rpx rgba(0, 255, 157, 0.2);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const app = getApp();
|
||||
import request from '../../utils/request.js';
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import { reconnectForRole } from '../../utils/role-tab-bar.js';
|
||||
import { getOrderStatusText } from '../../utils/api-helper.js';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 商品类型
|
||||
shangpinleixing: [],
|
||||
@@ -63,16 +64,6 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 加载商品类型(图片拼接)
|
||||
async loadShangpinLeixing() {
|
||||
try {
|
||||
@@ -192,7 +183,7 @@ Page({
|
||||
// ✅ 图片URL拼接(核心)
|
||||
const processed = newList.map(item => ({
|
||||
...item,
|
||||
zhuangtaiZh: this.getZhuangtaiZh(item.zhuangtai),
|
||||
zhuangtaiZh: getOrderStatusText(item.zhuangtai),
|
||||
zhuangtaiColor: statusItem.color,
|
||||
tupian: item.tupian
|
||||
? (item.tupian.startsWith('http') ? item.tupian : this.data.ossImageUrl + item.tupian)
|
||||
@@ -269,7 +260,7 @@ Page({
|
||||
this.loadCurrentStatusOrders(false);
|
||||
},
|
||||
|
||||
// ✅ 手动点击“加载更多”按钮
|
||||
// ✅ 手动点击"加载更多"按钮
|
||||
onLoadMoreTap() {
|
||||
const key = this.data.currentStatusKey;
|
||||
const tabData = this.data.dsDingdanShuju[key];
|
||||
@@ -278,15 +269,6 @@ Page({
|
||||
this.loadCurrentStatusOrders(false);
|
||||
},
|
||||
|
||||
getZhuangtaiZh(zhuangtai) {
|
||||
const map = {
|
||||
1: '已下单', 2: '进行中', 3: '已完成',
|
||||
4: '退款中', 5: '已退款', 6: '退款失败',
|
||||
7: '指定中', 8: '结算中'
|
||||
};
|
||||
return map[zhuangtai] || '未知状态';
|
||||
},
|
||||
|
||||
goToDsDingdanXiangqing(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
if (!item) return;
|
||||
@@ -304,4 +286,4 @@ Page({
|
||||
},
|
||||
|
||||
onImageError() {}
|
||||
});
|
||||
}))
|
||||
@@ -503,7 +503,7 @@
|
||||
<!-- ======================= -->
|
||||
<view class="tech-footer">
|
||||
<view class="footer-line"></view>
|
||||
<view class="footer-text">星阙网络 © 7891 科技</view>
|
||||
<view class="footer-text">星阙网络 © 科技</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
+11
-133
@@ -1,22 +1,11 @@
|
||||
// pages/dashouzhongxin/dashouzhongxin.js
|
||||
import request from '../../utils/request.js'
|
||||
import popupService from '../../services/popupService.js'
|
||||
import {
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
syncGroupFields,
|
||||
ensureRoleOnCenterPage,
|
||||
clearCacheAndEnterNormal,
|
||||
} from '../../utils/role-tab-bar.js'
|
||||
import { lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole } from '../../utils/primary-role.js'
|
||||
import { resolveAvatarUrl, resolveAvatarFromStorage } from '../../utils/avatar.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 { openPrivateChat, buildInviterPeerId } from '../../utils/im-user.js'
|
||||
|
||||
const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC'
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 图片路径对象
|
||||
imgUrls: {},
|
||||
@@ -31,7 +20,6 @@ Page({
|
||||
assetBreakdown: [],
|
||||
pendingAuthList: [],
|
||||
showAuthSection: false,
|
||||
isLoading: false,
|
||||
inviteCode: '',
|
||||
guanshiInviteCode: '',
|
||||
zuzhangInviteCode: '',
|
||||
@@ -48,8 +36,6 @@ Page({
|
||||
clumber: [],
|
||||
chengjiaoliang: '',
|
||||
zaixianZhuangtai: 0,
|
||||
lastRefreshTime: 0,
|
||||
canRefresh: true,
|
||||
isAutoRegistering: false,
|
||||
inviterCache: null,
|
||||
zuzhangInviterCache: null,
|
||||
@@ -165,17 +151,10 @@ Page({
|
||||
});
|
||||
this.checkRoleStatuses();
|
||||
|
||||
const registerType = options.registerType || 'dashou';
|
||||
if (options.scene) {
|
||||
try {
|
||||
const scene = decodeURIComponent(options.scene);
|
||||
options.inviteCode = scene;
|
||||
} catch (e) {
|
||||
console.error('scene解码失败', e);
|
||||
}
|
||||
}
|
||||
const parsed = parseSceneOptions(options);
|
||||
const registerType = parsed.registerType || 'dashou';
|
||||
const { inviteCode } = parsed;
|
||||
this.checkDashouStatus();
|
||||
const { inviteCode } = options || {};
|
||||
if (inviteCode) {
|
||||
if (registerType === 'guanshi') {
|
||||
this.setData({ guanshiInviteCode: inviteCode });
|
||||
@@ -189,26 +168,7 @@ Page({
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
// 注意:不再重复声明 app
|
||||
if (!app.globalData.hasShownPopupOnColdStart) {
|
||||
app.globalData.hasShownPopupOnColdStart = true;
|
||||
setTimeout(() => {
|
||||
popupService.checkAndShow(this, 'dashouduan');
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
|
||||
onReady() {
|
||||
if (isCenterPageActive(this, 'isDashou', 'dashoustatus')) {
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
this.checkColdStartPopup('dashouduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
@@ -324,33 +284,9 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
_isApiSuccess(body) {
|
||||
if (!body) return false;
|
||||
const code = Number(body.code);
|
||||
return code === 200 || code === 0;
|
||||
},
|
||||
|
||||
_getApiMsg(body) {
|
||||
if (!body) return '';
|
||||
return body.msg || body.message || '';
|
||||
},
|
||||
|
||||
_extractUserData(body) {
|
||||
if (!body) return {};
|
||||
if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) {
|
||||
return body.data;
|
||||
}
|
||||
const userData = { ...body };
|
||||
delete userData.code;
|
||||
delete userData.msg;
|
||||
delete userData.message;
|
||||
return userData;
|
||||
},
|
||||
|
||||
async refreshAllInfo(showToast = true) {
|
||||
const now = Date.now();
|
||||
if (!this.data.canRefresh || (now - this.data.lastRefreshTime < 3000)) return;
|
||||
this.setData({ lastRefreshTime: now, canRefresh: false, isLoading: true });
|
||||
this.throttledRefresh(async () => {
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
this.checkRoleStatuses();
|
||||
const results = await Promise.allSettled([
|
||||
@@ -376,8 +312,8 @@ Page({
|
||||
}
|
||||
} finally {
|
||||
this.setData({ isLoading: false });
|
||||
setTimeout(() => this.setData({ canRefresh: true }), 3000);
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
refreshDashouInfo() {
|
||||
@@ -875,17 +811,6 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
checkDashouStatus() {
|
||||
try {
|
||||
const dashoustatus = wx.getStorageSync('dashoustatus');
|
||||
@@ -905,11 +830,6 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
loadAvatar() {
|
||||
const avatarUrl = resolveAvatarFromStorage(getApp());
|
||||
this.setData({ avatarUrl });
|
||||
},
|
||||
|
||||
checkGlobalData() {
|
||||
const app = getApp();
|
||||
const globalData = app.globalData || {};
|
||||
@@ -1058,19 +978,6 @@ Page({
|
||||
ensureRoleOnCenterPage(this, 'dashou');
|
||||
},
|
||||
|
||||
copyUid() {
|
||||
const { uid } = this.data;
|
||||
if (!uid) {
|
||||
wx.showToast({ title: 'UID不存在', icon: 'none', duration: 1500 });
|
||||
return;
|
||||
}
|
||||
wx.setClipboardData({
|
||||
data: uid,
|
||||
success: () => wx.showToast({ title: '已复制UID', icon: 'success', duration: 1500 }),
|
||||
fail: () => wx.showToast({ title: '复制失败', icon: 'error', duration: 1500 })
|
||||
});
|
||||
},
|
||||
|
||||
previewAvatar() {
|
||||
const { avatarUrl } = this.data;
|
||||
if (avatarUrl) wx.previewImage({ urls: [avatarUrl] });
|
||||
@@ -1079,19 +986,6 @@ Page({
|
||||
goToWithdraw() { wx.navigateTo({ url: '/pages/withdraw/withdraw' }) },
|
||||
goToTotalWithdraw() { this.goToWithdraw(); },
|
||||
|
||||
goToKefu() {
|
||||
const app = getApp();
|
||||
const { link, enterpriseId } = app.globalData.kefuConfig || {};
|
||||
if (!link || !enterpriseId) {
|
||||
wx.showToast({ title: '客服配置未加载', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: { url: link },
|
||||
corpId: enterpriseId,
|
||||
});
|
||||
},
|
||||
|
||||
goToAuth(e) {
|
||||
const type = e.currentTarget.dataset.type;
|
||||
if (!type || !['dashou', 'shangjia', 'guanshi', 'zuzhang'].includes(type)) return;
|
||||
@@ -1171,20 +1065,4 @@ Page({
|
||||
});
|
||||
},
|
||||
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '清除缓存',
|
||||
content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (r) => {
|
||||
if (r.confirm) clearCacheAndEnterNormal(this);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
switchToNormal() {
|
||||
lockPrimaryRole('normal');
|
||||
wx.reLaunch({ url: '/pages/mine/mine' });
|
||||
},
|
||||
})
|
||||
}, { roleConfig: { role: 'dashou', statusKey: 'dashoustatus', dataFlag: 'isDashou', pageId: 'dashouduan', apiUrl: '/yonghu/dashouxinxi' } }))
|
||||
@@ -193,7 +193,7 @@
|
||||
>
|
||||
<view class="rank-cell-inner">
|
||||
<image class="rank-bg-icon" src="{{item.icon}}" mode="aspectFit" />
|
||||
<view class="rank-content">
|
||||
<view class="rank-cell-content">
|
||||
<image class="rank-main-icon" src="{{item.icon}}" mode="aspectFit" />
|
||||
<text class="rank-cell-name">{{item.name}}</text>
|
||||
<text class="rank-cell-sub">{{item.sub}}</text>
|
||||
|
||||
+12
-10
@@ -469,7 +469,7 @@ page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
@@ -477,25 +477,27 @@ page {
|
||||
|
||||
.rank-bg-icon {
|
||||
position: absolute;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
right: -12rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
right: 8rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(-10deg);
|
||||
opacity: 0.15;
|
||||
opacity: 0.12;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rank-content {
|
||||
.rank-cell-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.rank-main-icon {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
margin-bottom: 6rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.rank-shape-right .rank-cell-inner {
|
||||
@@ -509,7 +511,7 @@ page {
|
||||
transform: translateY(-50%) rotate(10deg);
|
||||
}
|
||||
|
||||
.rank-shape-right .rank-content {
|
||||
.rank-shape-right .rank-cell-content {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
<view class="chat-page">
|
||||
<scroll-view class="msg-list" scroll-y="true"
|
||||
<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);">
|
||||
<view class="loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<view class="chat-loading-more" wx:if="{{loadingHistory}}">加载更早消息...</view>
|
||||
<block wx:for="{{messages}}" wx:key="messageId">
|
||||
<view class="msg-wrapper" data-messageid="{{item.messageId}}" bindlongpress="showAction">
|
||||
<view class="time-tag" wx:if="{{item.showTime}}">{{item.formattedTime}}</view>
|
||||
<view class="msg-row {{item.senderId === currentUser.id ? 'right' : 'left'}}">
|
||||
<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="avatar" src="{{item.senderData.avatar || '/images/default-avatar.png'}}" mode="aspectFill" />
|
||||
<image class="chat-avatar" src="{{item.senderData.avatar || '/images/default-avatar.png'}}" mode="aspectFill" />
|
||||
<text class="sender-name">{{item.senderData.name || item.senderId}}</text>
|
||||
</view>
|
||||
<view class="bubble {{item.senderId === currentUser.id ? 'bubble-right' : 'bubble-left'}}"
|
||||
<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 : ''}}"
|
||||
bindtap="{{item.type==='text' ? 'onBubbleTap' : (item.type==='image' ? 'previewImage' : '')}}"
|
||||
data-url="{{item.type==='image' ? item.payload.url : ''}}">
|
||||
<text wx:if="{{item.type === 'text'}}" class="msg-text">{{item.payload.text}}</text>
|
||||
<image wx:elif="{{item.type === 'image'}}" class="msg-image" src="{{item.payload.url}}" mode="widthFix" />
|
||||
<view wx:elif="{{item.type === 'order'}}" class="order-bubble" data-payload="{{item.payload}}" bindtap="viewOrderDetail">
|
||||
<text class="order-label">[订单]</text>
|
||||
<text class="order-info">ID: {{item.payload.orderId}}</text>
|
||||
<text class="order-info">内容: {{item.payload.jieshao}}</text>
|
||||
<text class="order-info">金额: ¥{{item.payload.jine}}</text>
|
||||
<view class="order-detail-link">点击查看详情</view>
|
||||
<text wx:if="{{item.type === 'text'}}" class="chat-msg-text">{{item.payload.text}}</text>
|
||||
<image wx:elif="{{item.type === 'image'}}" class="chat-msg-image" src="{{item.payload.url}}" mode="widthFix" />
|
||||
<view wx:elif="{{item.type === 'order'}}" class="chat-order-bubble" data-payload="{{item.payload}}" bindtap="viewOrderDetail">
|
||||
<text class="chat-order-label">[订单]</text>
|
||||
<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>
|
||||
<view class="chat-order-info" style="color:#007aff;margin-top:8rpx">点击查看详情</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 自己的头像 -->
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="avatar" src="{{currentUser.avatar}}" mode="aspectFill" />
|
||||
<image wx:if="{{item.senderId === currentUser.id}}" class="chat-avatar" src="{{currentUser.avatar}}" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view id="msg-bottom" style="height:20rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
<view class="plus-panel {{showPlusPanel ? 'show' : ''}}" catchtap="closePlusPanel">
|
||||
<view class="plus-grid">
|
||||
<view class="plus-item" catchtap="openEmojiFromPlus">
|
||||
<text class="plus-icon">😀</text>
|
||||
<text class="plus-label">表情</text>
|
||||
<view class="chat-input-area" style="bottom: {{keyboardHeight}}px; padding-bottom: calc(10rpx + env(safe-area-inset-bottom));">
|
||||
<view class="chat-plus-panel {{showPlusPanel ? 'show' : ''}}" catchtap="closePlusPanel">
|
||||
<view class="chat-plus-grid">
|
||||
<view class="chat-plus-item" catchtap="openEmojiFromPlus">
|
||||
<text class="chat-plus-icon">😀</text>
|
||||
<text class="chat-plus-label">表情</text>
|
||||
</view>
|
||||
<view class="plus-item" catchtap="chooseImage">
|
||||
<text class="plus-icon">📷</text>
|
||||
<text class="plus-label">图片</text>
|
||||
<view class="chat-plus-item" catchtap="chooseImage">
|
||||
<text class="chat-plus-icon">📷</text>
|
||||
<text class="chat-plus-label">图片</text>
|
||||
</view>
|
||||
<view class="plus-item" catchtap="openOrderSender">
|
||||
<text class="plus-icon">📋</text>
|
||||
<text class="plus-label">订单</text>
|
||||
<view class="chat-plus-item" catchtap="openOrderSender">
|
||||
<text class="chat-plus-icon">📋</text>
|
||||
<text class="chat-plus-label">订单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="emoji-mask" wx:if="{{showEmojiPanel}}" catchtap="closeEmojiPanel"></view>
|
||||
<view class="emoji-panel {{showEmojiPanel ? 'show' : ''}}">
|
||||
<scroll-view scroll-y="true" class="emoji-scroll">
|
||||
<view class="emoji-grid">
|
||||
<view class="chat-emoji-mask" wx:if="{{showEmojiPanel}}" catchtap="closeEmojiPanel"></view>
|
||||
<view class="chat-emoji-panel {{showEmojiPanel ? 'show' : ''}}">
|
||||
<scroll-view scroll-y="true" class="chat-emoji-scroll">
|
||||
<view class="chat-emoji-grid">
|
||||
<block wx:for="{{emojiList}}" wx:key="*this">
|
||||
<view class="emoji-item" data-emoji="{{item}}" catchtap="insertEmoji">{{item}}</view>
|
||||
<view class="chat-emoji-item" data-emoji="{{item}}" catchtap="insertEmoji">{{item}}</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="pending-image" wx:if="{{pendingImage}}">
|
||||
<image class="pending-thumb" src="{{pendingImage}}" mode="aspectFill" />
|
||||
<view class="pending-remove" catchtap="clearPendingImage">✕</view>
|
||||
<view class="chat-pending-image" wx:if="{{pendingImage}}">
|
||||
<image class="chat-pending-thumb" src="{{pendingImage}}" mode="aspectFill" />
|
||||
<view class="chat-pending-remove" catchtap="clearPendingImage">✕</view>
|
||||
</view>
|
||||
|
||||
<view class="write-row">
|
||||
<input class="text-input" placeholder="发送消息" value="{{inputText}}" bindinput="onInput" adjust-position="{{false}}" bindkeyboardheightchange="onKeyboardHeightChange" cursor-spacing="10"/>
|
||||
<view class="send-btn" bindtap="sendMessage">发送</view>
|
||||
<view class="plus-btn" bindtap="togglePlusPanel">
|
||||
<text class="plus-icon-single">➕</text>
|
||||
<view class="chat-write-row">
|
||||
<input class="chat-text-input" placeholder="发送消息" value="{{inputText}}" bindinput="onInput" adjust-position="{{false}}" bindkeyboardheightchange="onKeyboardHeightChange" cursor-spacing="10"/>
|
||||
<view class="chat-send-btn" bindtap="sendMessage">发送</view>
|
||||
<view class="chat-plus-btn" bindtap="togglePlusPanel">
|
||||
<text class="chat-plus-icon-single">➕</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-modal" wx:if="{{showDetailModal}}" catchtap="hideDetail">
|
||||
<view class="detail-content" catchtap="noop">
|
||||
<text class="detail-text" user-select="true">{{detailText}}</text>
|
||||
<button class="copy-btn" bindtap="copyDetail">复制</button>
|
||||
<view class="chat-detail-modal" wx:if="{{showDetailModal}}" catchtap="hideDetail">
|
||||
<view class="chat-detail-content" catchtap="noop">
|
||||
<text class="chat-detail-text" user-select="true">{{detailText}}</text>
|
||||
<button class="chat-copy-btn" bindtap="copyDetail">复制</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,97 +1,6 @@
|
||||
.chat-page { width:100%; height:100vh; background:#f5f5f5; }
|
||||
/* 使用全局 chat-* 样式 */
|
||||
|
||||
.msg-list {
|
||||
position: absolute;
|
||||
left:0; right:0;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.loading-more { text-align:center; font-size:24rpx; color:#999; padding:20rpx 0; }
|
||||
.time-tag { text-align:center; margin:28rpx 0; font-size:24rpx; color:#b0b0b0; }
|
||||
.msg-wrapper { margin-bottom:12rpx; }
|
||||
.msg-row { display:flex; align-items:flex-start; margin-bottom:4rpx; }
|
||||
.right { justify-content:flex-end; }
|
||||
.left { justify-content:flex-start; }
|
||||
.avatar { width:76rpx; height:76rpx; border-radius:50%; background:#e0e0e0; margin:0 12rpx; flex-shrink:0; }
|
||||
.bubble { max-width:70%; padding:16rpx 20rpx; border-radius:16rpx; position:relative; word-break:break-all; font-size:30rpx; line-height:1.5; }
|
||||
.bubble-left { background:#fff; border-top-left-radius:4rpx; }
|
||||
.bubble-right { background:#00aaff; color:#fff; border-top-right-radius:4rpx; }
|
||||
.msg-text { color:inherit; }
|
||||
.msg-image { max-width:240rpx; border-radius:10rpx; }
|
||||
|
||||
.order-bubble { background:#fff7e0; padding:12rpx; border-radius:8rpx; }
|
||||
.order-label { font-size:24rpx; color:#e6a23c; font-weight:600; }
|
||||
.order-info { font-size:24rpx; color:#333; margin:6rpx 0; }
|
||||
.order-detail-link { color:#007aff; font-size:24rpx; margin-top:8rpx; }
|
||||
|
||||
.input-area {
|
||||
position: fixed;
|
||||
left:0; right:0;
|
||||
background:#f7f7f7;
|
||||
border-top:1rpx solid #ddd;
|
||||
z-index:10;
|
||||
}
|
||||
|
||||
.plus-panel {
|
||||
position: absolute;
|
||||
bottom:100%; left:0; right:0;
|
||||
background:#fff;
|
||||
border-top:1rpx solid #e0e0e0;
|
||||
padding: 20rpx;
|
||||
transform: translateY(100%);
|
||||
opacity:0;
|
||||
transition: transform 0.3s, opacity 0.3s;
|
||||
pointer-events:none;
|
||||
z-index:9;
|
||||
}
|
||||
.plus-panel.show { transform: translateY(0); opacity:1; pointer-events:auto; }
|
||||
.plus-grid { display: flex; justify-content: space-around; }
|
||||
.plus-item { display: flex; flex-direction: column; align-items: center; width: 120rpx; }
|
||||
.plus-icon { font-size: 60rpx; }
|
||||
.plus-label { font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
|
||||
.emoji-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:transparent; z-index:8; }
|
||||
.emoji-panel {
|
||||
position: absolute; bottom:100%; left:0; right:0;
|
||||
background:#fff; border-top:1rpx solid #e0e0e0;
|
||||
height:380rpx;
|
||||
transform: translateY(100%); opacity:0; transition:0.3s;
|
||||
pointer-events:none; z-index:9;
|
||||
}
|
||||
.emoji-panel.show { transform:translateY(0); opacity:1; pointer-events:auto; }
|
||||
.emoji-scroll { height:100%; }
|
||||
.emoji-grid { display:flex; flex-wrap:wrap; padding:20rpx 10rpx; }
|
||||
.emoji-item { width:14.28%; text-align:center; padding:16rpx 0; font-size:44rpx; }
|
||||
|
||||
.pending-image { display:flex; align-items:center; padding:10rpx 20rpx; background:#fff; }
|
||||
.pending-thumb { width:80rpx; height:80rpx; border-radius:8rpx; }
|
||||
.pending-remove { font-size:30rpx; color:#999; margin-left:10rpx; }
|
||||
|
||||
.write-row { display:flex; padding:0 20rpx 10rpx; align-items:center; }
|
||||
.text-input {
|
||||
flex:1; height:76rpx; background:#fff; border-radius:12rpx;
|
||||
padding:0 20rpx; font-size:30rpx; margin-right:12rpx;
|
||||
}
|
||||
.send-btn {
|
||||
width:120rpx; height:76rpx; background:#00aaff; color:#fff;
|
||||
font-size:28rpx; font-weight:500; border-radius:12rpx;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
}
|
||||
.plus-btn {
|
||||
width:56rpx; height:56rpx;
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
.plus-icon-single { font-size:46rpx; color:#00aaff; }
|
||||
|
||||
.detail-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); z-index:999; display:flex; align-items:center; justify-content:center; }
|
||||
.detail-content { width:80%; max-height:80%; background:#fff; border-radius:16rpx; padding:40rpx; }
|
||||
.detail-text { font-size:32rpx; line-height:1.6; word-break:break-all; overflow-y:auto; }
|
||||
.copy-btn { width:100%; height:80rpx; background:#00aaff; color:#fff; border-radius:12rpx; margin-top:20rpx; font-size:30rpx; }
|
||||
|
||||
/* 发送者昵称样式 */
|
||||
/* 发送者昵称样式(群聊特有) */
|
||||
.sender-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
+6
-12
@@ -4,11 +4,12 @@ import { ensureLogin } from '../../utils/login';
|
||||
import { fetchShopGoods, bindShop } from '../../utils/shop';
|
||||
import { backfillUserProfileCache, getSessionToken } from '../../utils/role-tab-bar';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
|
||||
// 扫码场景参数名
|
||||
const SCENE_PARAM = 'scene';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
ossImageUrl: '',
|
||||
lunbozhanwei: '',
|
||||
@@ -107,16 +108,6 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
getCurrentTime() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
@@ -317,6 +308,7 @@ Page({
|
||||
showGonggaoDetail() {
|
||||
this.setData({
|
||||
showGonggaoModal: true,
|
||||
_modalLeaving: false,
|
||||
gonggaoAnim: false,
|
||||
currentTime: this.getCurrentTime()
|
||||
});
|
||||
@@ -325,8 +317,10 @@ Page({
|
||||
hideGonggaoDetail() {
|
||||
this.setData({
|
||||
showGonggaoModal: false,
|
||||
_modalLeaving: true,
|
||||
gonggaoAnim: true
|
||||
});
|
||||
setTimeout(() => this.setData({ _modalLeaving: false }), 300);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -388,4 +382,4 @@ Page({
|
||||
onUnload() {
|
||||
this.setData({ gonggaoAnim: false });
|
||||
}
|
||||
});
|
||||
}))
|
||||
@@ -146,9 +146,9 @@
|
||||
</view>
|
||||
|
||||
<!-- 公告详情弹窗 -->
|
||||
<view class="gonggao-modal" wx:if="{{showGonggaoModal}}">
|
||||
<view class="modal-mask" bindtap="hideGonggaoDetail"></view>
|
||||
<view class="modal-content">
|
||||
<view class="gonggao-modal {{showGonggaoModal ? 'gonggao-modal--show' : 'gonggao-modal--hide'}}" wx:if="{{showGonggaoModal || _modalLeaving}}">
|
||||
<view class="modal-mask {{showGonggaoModal ? 'modal-mask--in' : 'modal-mask--out'}}" bindtap="hideGonggaoDetail"></view>
|
||||
<view class="modal-content {{showGonggaoModal ? 'modal-content--in' : 'modal-content--out'}}">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">公告详情</text>
|
||||
<view class="modal-close" bindtap="hideGonggaoDetail">×</view>
|
||||
|
||||
@@ -411,3 +411,82 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-mask {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 0;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-mask--in {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-mask--out {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 620rpx;
|
||||
max-height: 70vh;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: transform 0.25s ease, opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-content--in {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-content--out {
|
||||
transform: scale(0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 32rpx 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-close {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #999;
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-body {
|
||||
flex: 1;
|
||||
padding: 28rpx 32rpx 36rpx;
|
||||
max-height: 55vh;
|
||||
}
|
||||
|
||||
.gonggao-modal .modal-text {
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
line-height: 1.8;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
+32
-122
@@ -1,18 +1,9 @@
|
||||
// pages/leader/leader.js
|
||||
import request from '../../utils/request.js'
|
||||
import {
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
ensureRoleOnCenterPage,
|
||||
} from '../../utils/role-tab-bar.js'
|
||||
import { createPage, request, syncRoleStatuses, syncProfileFields, ensureRoleOnCenterPage, isCenterPageActive, parseSceneOptions } from '../../utils/base-page.js'
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 页面状态
|
||||
isZuzhang: false,
|
||||
isLoading: false,
|
||||
|
||||
// 邀请码
|
||||
inviteCode: '',
|
||||
@@ -28,21 +19,20 @@ Page({
|
||||
fenhongZonge: '0.00',
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
wx.redirectTo({ url: '/pages/fighter/fighter' });
|
||||
return;
|
||||
onLoad(options) {
|
||||
const parsed = parseSceneOptions(options)
|
||||
if (parsed.inviteCode) {
|
||||
this.setData({ inviteCode: parsed.inviteCode })
|
||||
}
|
||||
wx.redirectTo({ url: '/pages/fighter/fighter' })
|
||||
return
|
||||
// 以下保留供恢复参考
|
||||
this.checkLoginStatus()
|
||||
},
|
||||
|
||||
onReady() {
|
||||
if (isCenterPageActive(this, 'isZuzhang', 'zuzhangstatus')) {
|
||||
ensureRoleOnCenterPage(this, 'zuzhang')
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.registerNotificationComponent()
|
||||
this.checkColdStartPopup('zuzhangduan')
|
||||
if (isCenterPageActive(this, 'isZuzhang', 'zuzhangstatus')) {
|
||||
if (!this.data.isZuzhang) this.setData({ isZuzhang: true })
|
||||
ensureRoleOnCenterPage(this, 'zuzhang')
|
||||
@@ -66,92 +56,36 @@ Page({
|
||||
})
|
||||
return false
|
||||
}
|
||||
this.checkZuzhangStatus()
|
||||
this.checkRoleStatus()
|
||||
return true
|
||||
},
|
||||
|
||||
// 检查组长状态
|
||||
checkZuzhangStatus() {
|
||||
try {
|
||||
const zuzhangstatus = wx.getStorageSync('zuzhangstatus')
|
||||
if (isRoleStatusActive(zuzhangstatus)) {
|
||||
this.setData({ isZuzhang: true })
|
||||
// 角色状态检查后的额外数据加载(由 mixin 的 checkRoleStatus 调用)
|
||||
checkGlobalData() {
|
||||
this.loadUserInfo()
|
||||
this.getZuzhangInfo()
|
||||
ensureRoleOnCenterPage(this, 'zuzhang')
|
||||
} else {
|
||||
this.setData({ isZuzhang: false })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取缓存失败:', error)
|
||||
this.setData({ isZuzhang: false })
|
||||
}
|
||||
},
|
||||
|
||||
// 加载用户信息
|
||||
loadUserInfo() {
|
||||
const app = getApp()
|
||||
const globalData = app.globalData || {}
|
||||
|
||||
const touxiang = wx.getStorageSync('touxiang')
|
||||
const ossImageUrl = globalData.ossImageUrl || ''
|
||||
const morentouxiang = globalData.morentouxiang || 'avatar/default.jpg'
|
||||
|
||||
let avatarUrl = ''
|
||||
if (touxiang) {
|
||||
avatarUrl = ossImageUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang)
|
||||
} else {
|
||||
avatarUrl = ossImageUrl + (morentouxiang.startsWith('/') ? morentouxiang : '/' + morentouxiang)
|
||||
}
|
||||
|
||||
const uid = wx.getStorageSync('uid') || ''
|
||||
const nicheng = wx.getStorageSync('nicheng') || globalData.nicheng || ''
|
||||
|
||||
this.setData({ avatarUrl, uid, nicheng })
|
||||
},
|
||||
|
||||
// 注册通知组件
|
||||
registerNotificationComponent() {
|
||||
const app = getApp()
|
||||
const notificationComp = this.selectComponent('#global-notification')
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 获取组长信息(刷新)
|
||||
async getZuzhangInfo() {
|
||||
this.setData({ isLoading: true })
|
||||
try {
|
||||
const res = await request({
|
||||
this.loadData({
|
||||
url: '/yonghu/zuzhangxinxi',
|
||||
method: 'POST'
|
||||
})
|
||||
if (res && res.data.code === 200) {
|
||||
const data = res.data.data
|
||||
method: 'POST',
|
||||
errorMsg: '获取信息失败',
|
||||
onSuccess: (data) => {
|
||||
this.setData({
|
||||
ketixian: data.ketixian || '0.00',
|
||||
guanshiCount: data.guanshiCount || 0,
|
||||
fenhongZonge: data.fenhongZonge || '0.00',
|
||||
isLoading: false
|
||||
})
|
||||
wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 })
|
||||
} else {
|
||||
throw new Error(res?.data?.msg || '获取信息失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取组长信息失败:', error)
|
||||
this.setData({ isLoading: false })
|
||||
wx.showToast({ title: error.message || '刷新失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 刷新按钮点击
|
||||
refreshZuzhangInfo() {
|
||||
this.getZuzhangInfo()
|
||||
this.throttledRefresh(this.getZuzhangInfo)
|
||||
},
|
||||
|
||||
// 邀请码输入
|
||||
@@ -160,49 +94,25 @@ Page({
|
||||
},
|
||||
|
||||
// 注册组长
|
||||
async onRegister() {
|
||||
onRegister() {
|
||||
const { inviteCode } = this.data
|
||||
if (!inviteCode) {
|
||||
wx.showToast({ title: '请输入邀请码', icon: 'none', duration: 2000 })
|
||||
return
|
||||
}
|
||||
if (inviteCode.length > 100) {
|
||||
wx.showToast({ title: '邀请码不能超过100字符', icon: 'none', duration: 2000 })
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({ isLoading: true })
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/zuzhangzhuce',
|
||||
method: 'POST',
|
||||
data: { inviteCode }
|
||||
})
|
||||
if (res && res.data.code === 200) {
|
||||
const data = res.data.data || {}
|
||||
syncRoleStatuses(data)
|
||||
syncProfileFields(data)
|
||||
if (!Object.prototype.hasOwnProperty.call(data, 'zuzhangstatus')) {
|
||||
wx.setStorageSync('zuzhangstatus', 1)
|
||||
}
|
||||
this.registerWithInviteCode({
|
||||
inviteCode,
|
||||
apiUrl: '/yonghu/zuzhangzhuce',
|
||||
role: 'zuzhang',
|
||||
statusKey: 'zuzhangstatus',
|
||||
successMsg: '注册成功!',
|
||||
onSuccess: (userData) => {
|
||||
this.setData({
|
||||
isZuzhang: true,
|
||||
ketixian: data.ketixian || '0.00',
|
||||
guanshiCount: data.guanshiCount || 0,
|
||||
fenhongZonge: data.fenhongZonge || '0.00',
|
||||
isLoading: false
|
||||
ketixian: userData.ketixian || '0.00',
|
||||
guanshiCount: userData.guanshiCount || 0,
|
||||
fenhongZonge: userData.fenhongZonge || '0.00',
|
||||
})
|
||||
this.loadUserInfo()
|
||||
ensureRoleOnCenterPage(this, 'zuzhang')
|
||||
wx.showToast({ title: '注册成功!', icon: 'success', duration: 2000 })
|
||||
} else {
|
||||
throw new Error(res?.data?.msg || '注册失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error)
|
||||
this.setData({ isLoading: false })
|
||||
wx.showToast({ title: error.message || '注册失败', icon: 'error' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到提现页面
|
||||
@@ -222,4 +132,4 @@ Page({
|
||||
goToYaoqingGuanshi() {
|
||||
wx.navigateTo({ url: '/pages/invite-manager/invite-manager' })
|
||||
}
|
||||
})
|
||||
}, { roleConfig: { role: 'dashou', statusKey: 'zuzhangstatus', dataFlag: 'isZuzhang', pageId: 'zuzhangduan', apiUrl: '/yonghu/zuzhangxinxi' } }))
|
||||
@@ -1,124 +1,114 @@
|
||||
<!-- pages/guanshi-paihang/guanshi-paihang.wxml -->
|
||||
<view class="guanshi-paihang-page">
|
||||
<!-- pages/manager-rank/manager-rank.wxml -->
|
||||
<view class="rank-page">
|
||||
|
||||
<!-- 赛博朋克背景元素 -->
|
||||
<view class="cyber-bg">
|
||||
<view class="rank-cyber-bg">
|
||||
<!-- 网格线 -->
|
||||
<view class="cyber-grid"></view>
|
||||
<view class="rank-cyber-grid"></view>
|
||||
<!-- 扫描线 -->
|
||||
<view class="cyber-scanline"></view>
|
||||
<view class="rank-cyber-scanline"></view>
|
||||
<!-- 流动数据流 -->
|
||||
<view class="data-streams">
|
||||
<view class="stream stream-1"></view>
|
||||
<view class="stream stream-2"></view>
|
||||
<view class="stream stream-3"></view>
|
||||
<view class="rank-data-streams">
|
||||
<view class="rank-stream rank-stream--1"></view>
|
||||
<view class="rank-stream rank-stream--2"></view>
|
||||
<view class="rank-stream rank-stream--3"></view>
|
||||
</view>
|
||||
<!-- 浮动粒子 -->
|
||||
<view class="floating-particles">
|
||||
<view class="particle" wx:for="{{12}}" wx:key="index"></view>
|
||||
</view>
|
||||
<view class="rank-particle" wx:for="{{12}}" wx:key="index"></view>
|
||||
</view>
|
||||
|
||||
<!-- 页面头部 -->
|
||||
<view class="page-header">
|
||||
<view class="rank-page-header">
|
||||
<!-- 标题 -->
|
||||
<view class="title-container">
|
||||
<text class="main-title">管事排行榜</text>
|
||||
<view class="title-line"></view>
|
||||
<text class="sub-title">GUANSHI RANKING</text>
|
||||
<view class="rank-title-container">
|
||||
<text class="rank-main-title">管事排行榜</text>
|
||||
<view class="rank-title-line"></view>
|
||||
<text class="rank-sub-title">GUANSHI RANKING</text>
|
||||
</view>
|
||||
|
||||
<!-- 切换按钮 -->
|
||||
<view class="toggle-container">
|
||||
<view class="toggle-bg" style="transform: translateX({{paihangLeixing == 1 ? '0' : '100%'}});"></view>
|
||||
<view class="rank-toggle-container">
|
||||
<view class="rank-toggle-bg" style="transform: translateX({{paihangLeixing == 1 ? '0' : '100%'}});"></view>
|
||||
<view
|
||||
class="toggle-btn {{paihangLeixing == 1 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
class="rank-toggle-btn {{paihangLeixing == 1 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
data-leixing="1"
|
||||
bindtap="qiehuangPaihangLeixing"
|
||||
>
|
||||
<text class="toggle-text">今日充值人数</text>
|
||||
<view class="toggle-line {{paihangLeixing == 1 ? 'active' : ''}}"></view>
|
||||
<text class="rank-toggle-text">今日充值人数</text>
|
||||
<view class="rank-toggle-line {{paihangLeixing == 1 ? 'active' : ''}}"></view>
|
||||
</view>
|
||||
<view
|
||||
class="toggle-btn {{paihangLeixing == 2 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
class="rank-toggle-btn {{paihangLeixing == 2 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
data-leixing="2"
|
||||
bindtap="qiehuangPaihangLeixing"
|
||||
>
|
||||
<text class="toggle-text">今月充值人数</text>
|
||||
<view class="toggle-line {{paihangLeixing == 2 ? 'active' : ''}}"></view>
|
||||
<text class="rank-toggle-text">今月充值人数</text>
|
||||
<view class="rank-toggle-line {{paihangLeixing == 2 ? 'active' : ''}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view wx:if="{{jiazaiZhuangtai}}" class="loading-state">
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner">
|
||||
<view class="spinner-outer"></view>
|
||||
<view class="spinner-inner"></view>
|
||||
<view class="spinner-core"></view>
|
||||
</view>
|
||||
<text class="loading-text">数据加载中</text>
|
||||
<text class="loading-sub">Connecting to server...</text>
|
||||
<view wx:if="{{jiazaiZhuangtai}}" class="rank-loading-state">
|
||||
<view class="rank-loading-spinner">
|
||||
<view class="rank-spinner-outer"></view>
|
||||
<view class="rank-spinner-inner"></view>
|
||||
<view class="rank-spinner-core"></view>
|
||||
</view>
|
||||
<text class="rank-loading-text">数据加载中</text>
|
||||
<text class="rank-loading-sub">Connecting to server...</text>
|
||||
</view>
|
||||
|
||||
<!-- 排行榜内容 -->
|
||||
<view wx:else class="ranking-content">
|
||||
<view wx:else class="rank-content">
|
||||
|
||||
<!-- 虚拟数据提示 -->
|
||||
<view wx:if="{{!xianshiZhenshiShuju}}" class="demo-notice">
|
||||
<view class="notice-icon">📡</view>
|
||||
<view class="notice-content">
|
||||
<text class="notice-title">排行榜数据准备中</text>
|
||||
<text class="notice-desc">当前显示为模拟数据,实时排名即将开放</text>
|
||||
<view wx:if="{{!xianshiZhenshiShuju}}" class="rank-demo-notice">
|
||||
<view class="rank-notice-icon">📡</view>
|
||||
<view class="rank-notice-content">
|
||||
<text class="rank-notice-title">排行榜数据准备中</text>
|
||||
<text class="rank-notice-desc">当前显示为模拟数据,实时排名即将开放</text>
|
||||
</view>
|
||||
<view class="notice-glow"></view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 前三名区域 ========== -->
|
||||
<view class="top-three-section" wx:if="{{qiansanMing.length > 0}}">
|
||||
<view class="rank-top-three" wx:if="{{qiansanMing.length > 0}}">
|
||||
|
||||
<!-- 第二名 -->
|
||||
<view class="rank-card rank-2 {{flashCardIndex == 1 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
<view class="rank-card rank-card--2 {{flashCardIndex == 1 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
animation="{{flashCardIndex == 1 && flashCardType == 'top3' ? flashAnimation : ''}}"
|
||||
data-index="1" data-type="top3" bindtap="handleCardTap">
|
||||
|
||||
<view class="podium-base podium-silver">
|
||||
<view class="podium-glow"></view>
|
||||
<text class="podium-number">2</text>
|
||||
<view class="rank-podium-base rank-podium-silver">
|
||||
<text class="rank-podium-number">2</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-content">
|
||||
<!-- 头像容器 -->
|
||||
<view class="avatar-frame frame-silver">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar-image" src="{{qiansanMing[1] ? qiansanMing[1].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="avatar-halo halo-silver"></view>
|
||||
<view class="avatar-sparkles">
|
||||
<view class="sparkle s1"></view>
|
||||
<view class="sparkle s2"></view>
|
||||
<view class="sparkle s3"></view>
|
||||
<view class="rank-avatar-frame rank-frame-silver">
|
||||
<view class="rank-avatar-container">
|
||||
<image class="rank-avatar-image" src="{{qiansanMing[1] ? qiansanMing[1].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo rank-halo-silver"></view>
|
||||
<view class="rank-sparkle" style="top:15%;left:15%;animation-delay:0s"></view>
|
||||
<view class="rank-sparkle" style="top:25%;right:25%;animation-delay:0.5s"></view>
|
||||
<view class="rank-sparkle" style="bottom:35%;left:35%;animation-delay:1s"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rank-badge badge-silver">2</view>
|
||||
<view class="rank-badge rank-badge-silver">2</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-card">
|
||||
<view class="info-header">
|
||||
<text class="user-name">{{qiansanMing[1] ? qiansanMing[1].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-user-info-card">
|
||||
<text class="rank-user-name">{{qiansanMing[1] ? qiansanMing[1].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-info-body">
|
||||
<view class="rank-uid-row">
|
||||
<text class="rank-uid-label">ID:</text>
|
||||
<text class="rank-uid-value">{{qiansanMing[1] ? qiansanMing[1].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="info-body">
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">ID:</text>
|
||||
<text class="uid-value">{{qiansanMing[1] ? qiansanMing[1].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="income-row">
|
||||
<text class="income-label">充值大手:</text>
|
||||
<view class="income-value">
|
||||
<text class="amount {{qiansanMing[1] ? qiansanMing[1].jineClass : ''}}">{{qiansanMing[1] ? qiansanMing[1].jine : '0'}}</text>
|
||||
<text class="unit">人</text>
|
||||
<view class="rank-income-row">
|
||||
<text class="rank-income-label">充值大手:</text>
|
||||
<view class="rank-income-value">
|
||||
<text class="rank-amount {{qiansanMing[1] ? qiansanMing[1].jineClass : ''}}">{{qiansanMing[1] ? qiansanMing[1].jine : '0'}}</text>
|
||||
<text class="rank-unit">人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -127,62 +117,47 @@
|
||||
</view>
|
||||
|
||||
<!-- 第一名 -->
|
||||
<view class="rank-card rank-1 {{flashCardIndex == 0 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
<view class="rank-card rank-card--1 {{flashCardIndex == 0 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
animation="{{flashCardIndex == 0 && flashCardType == 'top3' ? flashAnimation : ''}}"
|
||||
data-index="0" data-type="top3" bindtap="handleCardTap">
|
||||
|
||||
<!-- 皇冠 -->
|
||||
<view class="crown-container" animation="{{crownAnimation}}">
|
||||
<view class="crown">
|
||||
<text class="crown-icon">👑</text>
|
||||
<view class="crown-rays">
|
||||
<view class="ray ray-1"></view>
|
||||
<view class="ray ray-2"></view>
|
||||
<view class="ray ray-3"></view>
|
||||
<view class="ray ray-4"></view>
|
||||
<view class="ray ray-5"></view>
|
||||
<view class="ray ray-6"></view>
|
||||
</view>
|
||||
<view class="crown-glow"></view>
|
||||
</view>
|
||||
<view class="rank-crown-container" animation="{{crownAnimation}}">
|
||||
<text class="rank-crown-icon">👑</text>
|
||||
<view class="rank-crown-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="podium-base podium-gold">
|
||||
<view class="podium-glow"></view>
|
||||
<text class="podium-number">1</text>
|
||||
<view class="rank-podium-base rank-podium-gold">
|
||||
<text class="rank-podium-number">1</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-content">
|
||||
<!-- 头像容器 -->
|
||||
<view class="avatar-frame frame-gold">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar-image" src="{{qiansanMing[0] ? qiansanMing[0].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="avatar-halo halo-gold"></view>
|
||||
<view class="avatar-sparkles">
|
||||
<view class="sparkle s1"></view>
|
||||
<view class="sparkle s2"></view>
|
||||
<view class="sparkle s3"></view>
|
||||
<view class="sparkle s4"></view>
|
||||
<view class="rank-avatar-frame rank-frame-gold">
|
||||
<view class="rank-avatar-container">
|
||||
<image class="rank-avatar-image" src="{{qiansanMing[0] ? qiansanMing[0].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo rank-halo-gold"></view>
|
||||
<view class="rank-sparkle" style="top:15%;left:15%;animation-delay:0s"></view>
|
||||
<view class="rank-sparkle" style="top:25%;right:25%;animation-delay:0.5s"></view>
|
||||
<view class="rank-sparkle" style="bottom:35%;left:35%;animation-delay:1s"></view>
|
||||
<view class="rank-sparkle" style="bottom:15%;right:15%;animation-delay:1.5s"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rank-badge badge-gold">1</view>
|
||||
<view class="rank-badge rank-badge-gold">1</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-card">
|
||||
<view class="info-header">
|
||||
<text class="user-name">{{qiansanMing[0] ? qiansanMing[0].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-user-info-card">
|
||||
<text class="rank-user-name">{{qiansanMing[0] ? qiansanMing[0].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-info-body">
|
||||
<view class="rank-uid-row">
|
||||
<text class="rank-uid-label">ID:</text>
|
||||
<text class="rank-uid-value">{{qiansanMing[0] ? qiansanMing[0].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="info-body">
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">ID:</text>
|
||||
<text class="uid-value">{{qiansanMing[0] ? qiansanMing[0].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="income-row">
|
||||
<text class="income-label">充值大手:</text>
|
||||
<view class="income-value">
|
||||
<text class="amount {{qiansanMing[0] ? qiansanMing[0].jineClass : ''}}">{{qiansanMing[0] ? qiansanMing[0].jine : '0'}}</text>
|
||||
<text class="unit">人</text>
|
||||
<view class="rank-income-row">
|
||||
<text class="rank-income-label">充值大手:</text>
|
||||
<view class="rank-income-value">
|
||||
<text class="rank-amount {{qiansanMing[0] ? qiansanMing[0].jineClass : ''}}">{{qiansanMing[0] ? qiansanMing[0].jine : '0'}}</text>
|
||||
<text class="rank-unit">人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -191,45 +166,40 @@
|
||||
</view>
|
||||
|
||||
<!-- 第三名 -->
|
||||
<view class="rank-card rank-3 {{flashCardIndex == 2 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
<view class="rank-card rank-card--3 {{flashCardIndex == 2 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
animation="{{flashCardIndex == 2 && flashCardType == 'top3' ? flashAnimation : ''}}"
|
||||
data-index="2" data-type="top3" bindtap="handleCardTap">
|
||||
|
||||
<view class="podium-base podium-bronze">
|
||||
<view class="podium-glow"></view>
|
||||
<text class="podium-number">3</text>
|
||||
<view class="rank-podium-base rank-podium-bronze">
|
||||
<text class="rank-podium-number">3</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-content">
|
||||
<!-- 头像容器 -->
|
||||
<view class="avatar-frame frame-bronze">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar-image" src="{{qiansanMing[2] ? qiansanMing[2].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="avatar-halo halo-bronze"></view>
|
||||
<view class="avatar-sparkles">
|
||||
<view class="sparkle s1"></view>
|
||||
<view class="sparkle s2"></view>
|
||||
<view class="sparkle s3"></view>
|
||||
<view class="rank-avatar-frame rank-frame-bronze">
|
||||
<view class="rank-avatar-container">
|
||||
<image class="rank-avatar-image" src="{{qiansanMing[2] ? qiansanMing[2].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo rank-halo-bronze"></view>
|
||||
<view class="rank-sparkle" style="top:15%;left:15%;animation-delay:0s"></view>
|
||||
<view class="rank-sparkle" style="top:25%;right:25%;animation-delay:0.5s"></view>
|
||||
<view class="rank-sparkle" style="bottom:35%;left:35%;animation-delay:1s"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rank-badge badge-bronze">3</view>
|
||||
<view class="rank-badge rank-badge-bronze">3</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-card">
|
||||
<view class="info-header">
|
||||
<text class="user-name">{{qiansanMing[2] ? qiansanMing[2].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-user-info-card">
|
||||
<text class="rank-user-name">{{qiansanMing[2] ? qiansanMing[2].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-info-body">
|
||||
<view class="rank-uid-row">
|
||||
<text class="rank-uid-label">ID:</text>
|
||||
<text class="rank-uid-value">{{qiansanMing[2] ? qiansanMing[2].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="info-body">
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">ID:</text>
|
||||
<text class="uid-value">{{qiansanMing[2] ? qiansanMing[2].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="income-row">
|
||||
<text class="income-label">充值大手:</text>
|
||||
<view class="income-value">
|
||||
<text class="amount {{qiansanMing[2] ? qiansanMing[2].jineClass : ''}}">{{qiansanMing[2] ? qiansanMing[2].jine : '0'}}</text>
|
||||
<text class="unit">人</text>
|
||||
<view class="rank-income-row">
|
||||
<text class="rank-income-label">充值大手:</text>
|
||||
<view class="rank-income-value">
|
||||
<text class="rank-amount {{qiansanMing[2] ? qiansanMing[2].jineClass : ''}}">{{qiansanMing[2] ? qiansanMing[2].jine : '0'}}</text>
|
||||
<text class="rank-unit">人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -239,50 +209,46 @@
|
||||
</view>
|
||||
|
||||
<!-- ========== 其他排名列表 ========== -->
|
||||
<view class="other-ranks-section" wx:if="{{paihangShuju.length > 0}}">
|
||||
<view class="section-title">
|
||||
<view class="title-line"></view>
|
||||
<text class="title-text">其他排名</text>
|
||||
<view class="title-line"></view>
|
||||
<view wx:if="{{paihangShuju.length > 0}}">
|
||||
<view class="rank-section-title">
|
||||
<view class="rank-section-line"></view>
|
||||
<text class="rank-section-text">其他排名</text>
|
||||
<view class="rank-section-line"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-list">
|
||||
<view class="rank-item {{flashCardIndex == index && flashCardType == 'normal' ? 'flash' : ''}}"
|
||||
<view class="rank-list-item {{flashCardIndex == index && flashCardType == 'normal' ? 'flash' : ''}}"
|
||||
wx:for="{{paihangShuju}}" wx:key="index"
|
||||
animation="{{flashCardIndex == index && flashCardType == 'normal' ? flashAnimation : ''}}"
|
||||
data-index="{{index}}" data-type="normal" bindtap="handleCardTap">
|
||||
|
||||
<view class="rank-number">
|
||||
<text class="number-text">{{xianshiZhenshiShuju ? index + 4 : index + 1}}</text>
|
||||
<view class="number-glow"></view>
|
||||
<text class="rank-number-text">{{xianshiZhenshiShuju ? index + 4 : index + 1}}</text>
|
||||
<view class="rank-number-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-avatar">
|
||||
<image class="rank-avatar-img" src="{{item.full_touxiang}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo"></view>
|
||||
<view class="rank-list-avatar">
|
||||
<image class="rank-list-avatar-img" src="{{item.full_touxiang}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-list-avatar-halo"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-info">
|
||||
<text class="rank-name">{{item.nicheng}}</text>
|
||||
<text class="rank-uid">UID: {{item.uid}}</text>
|
||||
<view class="rank-list-info">
|
||||
<text class="rank-list-name">{{item.nicheng}}</text>
|
||||
<text class="rank-list-uid">UID: {{item.uid}}</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-income">
|
||||
<text class="income-amount {{item.jineClass}}">{{item.jine}}</text>
|
||||
<text class="income-unit">人</text>
|
||||
<view class="income-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-hover"></view>
|
||||
<view class="rank-list-income">
|
||||
<text class="rank-income-amount {{item.jineClass}}">{{item.jine}}</text>
|
||||
<text class="rank-income-unit">人</text>
|
||||
<view class="rank-income-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view wx:if="{{xianshiZhenshiShuju && paihangShuju.length === 0 && qiansanMing.length === 0}}" class="empty-state">
|
||||
<view class="empty-icon">🏆</view>
|
||||
<text class="empty-title">暂无排名数据</text>
|
||||
<text class="empty-desc">成为第一个登榜的管事吧</text>
|
||||
<view wx:if="{{xianshiZhenshiShuju && paihangShuju.length === 0 && qiansanMing.length === 0}}" class="rank-empty-state">
|
||||
<view class="rank-empty-icon">🏆</view>
|
||||
<text class="rank-empty-title">暂无排名数据</text>
|
||||
<text class="rank-empty-desc">成为第一个登榜的管事吧</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+81
-262
@@ -1,16 +1,17 @@
|
||||
// pages/guanshizhongxin/guanshizhongxin.js
|
||||
import request from '../../utils/request.js';
|
||||
import popupService from '../../services/popupService.js';
|
||||
import {
|
||||
createPage, request, isRoleStatusActive, syncRoleStatuses, syncProfileFields,
|
||||
syncGroupFields, ensureRoleOnCenterPage, refreshTabBar, parseSceneOptions,
|
||||
isApiSuccess, getApiMsg, extractUserData
|
||||
} from '../../utils/base-page.js';
|
||||
import { buildInviterPeerId } from '../../utils/im-user.js';
|
||||
import { resolveAvatarUrl } from '../../utils/avatar.js';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 图片路径对象
|
||||
imgUrls: {},
|
||||
|
||||
isGuanshi: false,
|
||||
isLoading: false,
|
||||
inviteCode: '',
|
||||
uid: '',
|
||||
nicheng: '',
|
||||
@@ -33,14 +34,9 @@ Page({
|
||||
|
||||
onLoad(options) {
|
||||
let url = '/pages/fighter/fighter?registerType=guanshi';
|
||||
if (options.scene) {
|
||||
try {
|
||||
url += '&inviteCode=' + encodeURIComponent(decodeURIComponent(options.scene));
|
||||
} catch (e) {
|
||||
console.error('scene解码失败', e);
|
||||
}
|
||||
} else if (options.inviteCode) {
|
||||
url += '&inviteCode=' + encodeURIComponent(options.inviteCode);
|
||||
const parsed = parseSceneOptions(options);
|
||||
if (parsed.inviteCode) {
|
||||
url += '&inviteCode=' + encodeURIComponent(parsed.inviteCode);
|
||||
}
|
||||
wx.redirectTo({ url });
|
||||
return;
|
||||
@@ -50,7 +46,7 @@ Page({
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
const imgBase = ossImageUrl + 'beijing/guanshiduan/';
|
||||
this.setData({
|
||||
imgUrls: {
|
||||
imgUrls: this.initImageUrls({
|
||||
pageBg: imgBase + 'page-bg.jpg',
|
||||
topDecor: imgBase + 'top-decor.png',
|
||||
cardBg1: imgBase + 'card-bg1.png',
|
||||
@@ -66,48 +62,30 @@ Page({
|
||||
iconWithdraw: imgBase + 'icon-withdraw.png',
|
||||
iconContact: imgBase + 'icon-contact.png',
|
||||
iconArrow: imgBase + 'icon-arrow.png',
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// 处理扫码 scene
|
||||
if (options.scene) {
|
||||
try {
|
||||
const scene = decodeURIComponent(options.scene);
|
||||
options.inviteCode = scene;
|
||||
} catch (e) {
|
||||
console.error('scene解码失败', e);
|
||||
}
|
||||
}
|
||||
this.checkRoleStatus();
|
||||
|
||||
this.checkGuanshiStatus();
|
||||
|
||||
const { inviteCode } = options || {};
|
||||
const inviteCode = parsed.inviteCode;
|
||||
if (inviteCode) {
|
||||
this.setData({ inviteCode });
|
||||
setTimeout(() => {
|
||||
this.handleInviteCodeWithLoginCheck(inviteCode);
|
||||
this.handleInviteCodeWithLoginCheck({
|
||||
inviteCode,
|
||||
role: 'dashou',
|
||||
statusKey: 'guanshistatus',
|
||||
alreadyMsg: '已是管事',
|
||||
onNeedLogin: (code) => this.loginAndRegisterWithInviteCode({
|
||||
inviteCode: code,
|
||||
onLoginSuccess: (wxCode, invCode) => this.callLoginRegisterApi(wxCode, invCode),
|
||||
}),
|
||||
onAlreadyRegistered: () => this._enterRegisteredGuanshiState('您已是管事'),
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
if (!app.globalData.hasShownPopupOnColdStart) {
|
||||
app.globalData.hasShownPopupOnColdStart = true;
|
||||
setTimeout(() => {
|
||||
popupService.checkAndShow(this, 'guanshiduan');
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
|
||||
onReady() {
|
||||
if (this._isGuanshiPageActive()) {
|
||||
this.ensureGuanshiRoleAndTabBar();
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
this.checkColdStartPopup('guanshiduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
@@ -120,7 +98,7 @@ Page({
|
||||
if (!this.data.isGuanshi) {
|
||||
this.setData({ isGuanshi: true });
|
||||
}
|
||||
this.ensureGuanshiRoleAndTabBar();
|
||||
ensureRoleOnCenterPage(this, 'guanshi');
|
||||
setTimeout(() => {
|
||||
this.getGuanshiInfo();
|
||||
this.fetchChenghaoList();
|
||||
@@ -132,23 +110,21 @@ Page({
|
||||
const options = currentPage.options || {};
|
||||
if (options.inviteCode && !this._isGuanshiPageActive()) {
|
||||
setTimeout(() => {
|
||||
this.handleInviteCodeWithLoginCheck(options.inviteCode);
|
||||
this.handleInviteCodeWithLoginCheck({
|
||||
inviteCode: options.inviteCode,
|
||||
role: 'dashou',
|
||||
statusKey: 'guanshistatus',
|
||||
alreadyMsg: '已是管事',
|
||||
onNeedLogin: (code) => this.loginAndRegisterWithInviteCode({
|
||||
inviteCode: code,
|
||||
onLoginSuccess: (wxCode, invCode) => this.callLoginRegisterApi(wxCode, invCode),
|
||||
}),
|
||||
onAlreadyRegistered: () => this._enterRegisteredGuanshiState('您已是管事'),
|
||||
});
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
|
||||
// 全局通知注册
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const comp = this.selectComponent('#global-notification');
|
||||
if (comp && comp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => comp.showNotification(data),
|
||||
hide: () => comp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 称号列表
|
||||
async fetchChenghaoList() {
|
||||
try {
|
||||
@@ -166,7 +142,7 @@ Page({
|
||||
},
|
||||
|
||||
// 联系组长
|
||||
async contactInviter() {
|
||||
async contactInviter() {
|
||||
wx.showLoading({ title: '获取组长信息...', mask: true });
|
||||
try {
|
||||
const res = await request({
|
||||
@@ -207,35 +183,9 @@ async contactInviter() {
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// 管事状态辅助
|
||||
_isApiSuccess(body) {
|
||||
if (!body) return false;
|
||||
const code = Number(body.code);
|
||||
return code === 200 || code === 0;
|
||||
},
|
||||
|
||||
_getApiMsg(body) {
|
||||
if (!body) return '';
|
||||
return body.msg || body.message || '';
|
||||
},
|
||||
|
||||
_extractUserData(body) {
|
||||
if (!body) return {};
|
||||
if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) {
|
||||
return body.data;
|
||||
}
|
||||
const userData = { ...body };
|
||||
delete userData.code;
|
||||
delete userData.msg;
|
||||
delete userData.message;
|
||||
return userData;
|
||||
},
|
||||
|
||||
_isGuanshiStatusActive(status) {
|
||||
return Number(status) === 1;
|
||||
},
|
||||
|
||||
_isAlreadyGuanshiMsg(msg) {
|
||||
if (!msg || typeof msg !== 'string') return false;
|
||||
return msg.includes('已是管事') || msg.includes('已经是管事');
|
||||
@@ -247,21 +197,6 @@ async contactInviter() {
|
||||
app.globalData.guanshistatus = 1;
|
||||
},
|
||||
|
||||
// 仅同步后端返回的身份 status 字段
|
||||
_syncRoleStatuses(data) {
|
||||
if (!data || typeof data !== 'object') return;
|
||||
const app = getApp();
|
||||
const roleFields = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus', 'kaoheguanstatus'];
|
||||
roleFields.forEach(field => {
|
||||
if (Object.prototype.hasOwnProperty.call(data, field)) {
|
||||
const val = Number(data[field]);
|
||||
const normalized = Number.isNaN(val) ? 0 : val;
|
||||
wx.setStorageSync(field, normalized);
|
||||
app.globalData[field] = normalized;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 后端若返回打手详情字段则写入 globalData
|
||||
_syncDashouGlobalData(data) {
|
||||
const app = getApp();
|
||||
@@ -286,31 +221,7 @@ async contactInviter() {
|
||||
},
|
||||
|
||||
_isGuanshiPageActive() {
|
||||
return this.data.isGuanshi || this._isGuanshiStatusActive(wx.getStorageSync('guanshistatus'));
|
||||
},
|
||||
|
||||
// 管事页必须切到 guanshi 角色
|
||||
ensureGuanshiRoleAndTabBar() {
|
||||
const app = getApp();
|
||||
if (app.setCurrentRole) {
|
||||
app.setCurrentRole('guanshi');
|
||||
} else {
|
||||
app.globalData.currentRole = 'guanshi';
|
||||
wx.setStorageSync('currentRole', 'guanshi');
|
||||
app.emitEvent('currentRoleChanged', { role: 'guanshi' });
|
||||
}
|
||||
this._refreshTabBar();
|
||||
},
|
||||
|
||||
_refreshTabBar() {
|
||||
const tryRefresh = () => {
|
||||
const tabBar = this.getTabBar && this.getTabBar();
|
||||
if (tabBar && tabBar.refresh) {
|
||||
tabBar.refresh();
|
||||
}
|
||||
};
|
||||
tryRefresh();
|
||||
[50, 150, 300, 500, 800].forEach((delay) => setTimeout(tryRefresh, delay));
|
||||
return this.data.isGuanshi || isRoleStatusActive(wx.getStorageSync('guanshistatus'));
|
||||
},
|
||||
|
||||
_applyRegisterSuccess(userData, toastMsg) {
|
||||
@@ -319,31 +230,16 @@ async contactInviter() {
|
||||
if (data.token) wx.setStorageSync('token', data.token);
|
||||
if (data.token) app.globalData.token = data.token;
|
||||
|
||||
const profileFields = ['nicheng', 'uid', 'touxiang'];
|
||||
profileFields.forEach(field => {
|
||||
if (data[field] !== undefined && data[field] !== null) {
|
||||
wx.setStorageSync(field, data[field]);
|
||||
if (field === 'nicheng') {
|
||||
app.globalData.nicheng = data[field];
|
||||
}
|
||||
}
|
||||
});
|
||||
syncProfileFields(data);
|
||||
syncRoleStatuses(data);
|
||||
|
||||
// 身份列表以后端返回的 *status 字段为准
|
||||
this._syncRoleStatuses(data);
|
||||
if (!Object.prototype.hasOwnProperty.call(data, 'guanshistatus') &&
|
||||
!this._isGuanshiStatusActive(wx.getStorageSync('guanshistatus'))) {
|
||||
!isRoleStatusActive(wx.getStorageSync('guanshistatus'))) {
|
||||
this._syncGuanshiCache();
|
||||
}
|
||||
this._syncDashouGlobalData(data);
|
||||
|
||||
const groupFields = ['dashouqun', 'dashouqunid', 'guanshiqun', 'guanshiqunid'];
|
||||
groupFields.forEach(field => {
|
||||
if (data[field] !== undefined) {
|
||||
wx.setStorageSync(field, data[field]);
|
||||
app.globalData[field] = data[field];
|
||||
}
|
||||
});
|
||||
syncGroupFields(data);
|
||||
|
||||
const guanshiData = {
|
||||
gszhstatus: data.gszhstatus ?? '',
|
||||
@@ -392,41 +288,6 @@ async contactInviter() {
|
||||
}
|
||||
},
|
||||
|
||||
// 邀请码处理
|
||||
handleInviteCodeWithLoginCheck(inviteCode) {
|
||||
const token = wx.getStorageSync('token');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (!token || !uid) {
|
||||
this.loginAndRegisterWithInviteCode(inviteCode);
|
||||
} else {
|
||||
if (!this.data.isGuanshi) {
|
||||
wx.showLoading({ title: '自动注册中...', mask: true });
|
||||
this.callRegisterApi(inviteCode);
|
||||
} else {
|
||||
this._enterRegisteredGuanshiState('您已是管事');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
loginAndRegisterWithInviteCode(inviteCode) {
|
||||
const that = this;
|
||||
wx.showLoading({ title: '登录注册中...', mask: true });
|
||||
wx.login({
|
||||
success: (loginRes) => {
|
||||
if (loginRes.code) {
|
||||
that.callLoginRegisterApi(loginRes.code, inviteCode);
|
||||
} else {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '微信登录失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading();
|
||||
wx.reLaunch({ url: '/pages/gerenzhongxin/gerenzhongxin' });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
callLoginRegisterApi(code, inviteCode) {
|
||||
const that = this;
|
||||
const app = getApp();
|
||||
@@ -443,18 +304,18 @@ async contactInviter() {
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
const body = res.data || {};
|
||||
if (res.statusCode === 200 && that._isApiSuccess(body)) {
|
||||
const userData = that._extractUserData(body);
|
||||
const toastMsg = that._isAlreadyGuanshiMsg(that._getApiMsg(body))
|
||||
? that._getApiMsg(body)
|
||||
if (res.statusCode === 200 && isApiSuccess(body)) {
|
||||
const userData = extractUserData(body);
|
||||
const toastMsg = that._isAlreadyGuanshiMsg(getApiMsg(body))
|
||||
? getApiMsg(body)
|
||||
: '注册成功!';
|
||||
that._applyRegisterSuccess(userData, toastMsg);
|
||||
} else if (res.statusCode === 200 && that._isAlreadyGuanshiMsg(that._getApiMsg(body))) {
|
||||
that._enterRegisteredGuanshiState(that._getApiMsg(body) || '您已是管事', that._extractUserData(body));
|
||||
} else if (res.statusCode === 200 && that._isAlreadyGuanshiMsg(getApiMsg(body))) {
|
||||
that._enterRegisteredGuanshiState(getApiMsg(body) || '您已是管事', extractUserData(body));
|
||||
} else {
|
||||
wx.hideLoading();
|
||||
that.setData({ isLoading: false });
|
||||
wx.showToast({ title: that._getApiMsg(body) || '注册失败', icon: 'none' });
|
||||
wx.showToast({ title: getApiMsg(body) || '注册失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
@@ -472,18 +333,18 @@ async contactInviter() {
|
||||
data: { inviteCode }
|
||||
});
|
||||
const body = res?.data || {};
|
||||
if (res && this._isApiSuccess(body)) {
|
||||
const userData = this._extractUserData(body);
|
||||
const toastMsg = this._isAlreadyGuanshiMsg(this._getApiMsg(body))
|
||||
? this._getApiMsg(body)
|
||||
if (res && isApiSuccess(body)) {
|
||||
const userData = extractUserData(body);
|
||||
const toastMsg = this._isAlreadyGuanshiMsg(getApiMsg(body))
|
||||
? getApiMsg(body)
|
||||
: '注册成功!';
|
||||
this._applyRegisterSuccess(userData, toastMsg);
|
||||
} else if (this._isAlreadyGuanshiMsg(this._getApiMsg(body))) {
|
||||
this._enterRegisteredGuanshiState(this._getApiMsg(body) || '您已是管事', this._extractUserData(body));
|
||||
} else if (this._isAlreadyGuanshiMsg(getApiMsg(body))) {
|
||||
this._enterRegisteredGuanshiState(getApiMsg(body) || '您已是管事', extractUserData(body));
|
||||
} else {
|
||||
wx.hideLoading();
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: this._getApiMsg(body) || '注册失败', icon: 'none' });
|
||||
wx.showToast({ title: getApiMsg(body) || '注册失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('注册失败:', e);
|
||||
@@ -494,50 +355,7 @@ async contactInviter() {
|
||||
},
|
||||
|
||||
switchToGuanshiRole() {
|
||||
const app = getApp();
|
||||
this.ensureGuanshiRoleAndTabBar();
|
||||
if (app.switchRoleAndReconnect) {
|
||||
app.switchRoleAndReconnect('dashou').catch(() => {});
|
||||
} else if (app.connectForCurrentRole) {
|
||||
app.connectForCurrentRole();
|
||||
} else if (app.ensureConnection) {
|
||||
app.ensureConnection();
|
||||
}
|
||||
},
|
||||
|
||||
checkGuanshiStatus() {
|
||||
try {
|
||||
const status = wx.getStorageSync('guanshistatus');
|
||||
if (this._isGuanshiStatusActive(status)) {
|
||||
this.setData({ isGuanshi: true });
|
||||
this.loadUserInfo();
|
||||
this.checkGlobalData();
|
||||
this.ensureGuanshiRoleAndTabBar();
|
||||
} else {
|
||||
this.setData({ isGuanshi: false });
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ isGuanshi: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadUserInfo() {
|
||||
const app = getApp();
|
||||
const globalData = app.globalData || {};
|
||||
const touxiang = wx.getStorageSync('touxiang');
|
||||
const ossImageUrl = globalData.ossImageUrl || '';
|
||||
const morentouxiang = 'avatar/default.jpg';
|
||||
let avatarUrl = '';
|
||||
if (touxiang) {
|
||||
avatarUrl = ossImageUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang);
|
||||
} else {
|
||||
avatarUrl = ossImageUrl + (morentouxiang.startsWith('/') ? morentouxiang : '/' + morentouxiang);
|
||||
}
|
||||
this.setData({
|
||||
avatarUrl,
|
||||
uid: wx.getStorageSync('uid') || '',
|
||||
nicheng: globalData.nicheng || wx.getStorageSync('nicheng') || ''
|
||||
});
|
||||
ensureRoleOnCenterPage(this, 'guanshi');
|
||||
},
|
||||
|
||||
checkGlobalData() {
|
||||
@@ -574,8 +392,8 @@ async contactInviter() {
|
||||
url: '/yonghu/guanshixinxi',
|
||||
method: 'POST'
|
||||
});
|
||||
if (res && this._isApiSuccess(res.data)) {
|
||||
const data = this._extractUserData(res.data);
|
||||
if (res && isApiSuccess(res.data)) {
|
||||
const data = extractUserData(res.data);
|
||||
const app = getApp();
|
||||
this._syncGuanshiCache();
|
||||
if (!app.globalData.guanshi) app.globalData.guanshi = {};
|
||||
@@ -602,7 +420,7 @@ async contactInviter() {
|
||||
} else {
|
||||
this.setData({ isLoading: false });
|
||||
if (showToast) {
|
||||
wx.showToast({ title: this._getApiMsg(res?.data) || '获取失败', icon: 'none' });
|
||||
wx.showToast({ title: getApiMsg(res?.data) || '获取失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -614,8 +432,10 @@ async contactInviter() {
|
||||
},
|
||||
|
||||
refreshGuanshiInfo() {
|
||||
this.throttledRefresh(() => {
|
||||
this.getGuanshiInfo();
|
||||
this.fetchChenghaoList();
|
||||
});
|
||||
},
|
||||
|
||||
onInviteCodeInput(e) {
|
||||
@@ -640,17 +460,17 @@ async contactInviter() {
|
||||
data: { inviteCode }
|
||||
});
|
||||
const body = res?.data || {};
|
||||
if (res && this._isApiSuccess(body)) {
|
||||
const userData = this._extractUserData(body);
|
||||
const toastMsg = this._isAlreadyGuanshiMsg(this._getApiMsg(body))
|
||||
? this._getApiMsg(body)
|
||||
if (res && isApiSuccess(body)) {
|
||||
const userData = extractUserData(body);
|
||||
const toastMsg = this._isAlreadyGuanshiMsg(getApiMsg(body))
|
||||
? getApiMsg(body)
|
||||
: '注册成功!';
|
||||
this._applyRegisterSuccess(userData, toastMsg);
|
||||
} else if (this._isAlreadyGuanshiMsg(this._getApiMsg(body))) {
|
||||
this._enterRegisteredGuanshiState(this._getApiMsg(body) || '您已是管事', this._extractUserData(body));
|
||||
} else if (this._isAlreadyGuanshiMsg(getApiMsg(body))) {
|
||||
this._enterRegisteredGuanshiState(getApiMsg(body) || '您已是管事', extractUserData(body));
|
||||
} else {
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: this._getApiMsg(body) || '注册失败', icon: 'none' });
|
||||
wx.showToast({ title: getApiMsg(body) || '注册失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ isLoading: false });
|
||||
@@ -658,15 +478,6 @@ async contactInviter() {
|
||||
}
|
||||
},
|
||||
|
||||
copyUid() {
|
||||
const { uid } = this.data;
|
||||
if (!uid) return;
|
||||
wx.setClipboardData({
|
||||
data: uid,
|
||||
success: () => wx.showToast({ title: '已复制', icon: 'success' })
|
||||
});
|
||||
},
|
||||
|
||||
// 页面跳转
|
||||
goToWithdraw() { wx.navigateTo({ url: '/pages/withdraw/withdraw' }); },
|
||||
goToInviteUser() { wx.navigateTo({ url: '/pages/invite-fighter/invite-fighter' }); },
|
||||
@@ -674,4 +485,12 @@ async contactInviter() {
|
||||
goToRechargeRecord() { wx.navigateTo({ url: '/pages/recharge-log/recharge-log' }); },
|
||||
goToGuanshiRanking() { wx.navigateTo({ url: '/pages/manager-rank/manager-rank' }); },
|
||||
goToPromoPoster() { wx.navigateTo({ url: '/pages/poster/poster' }); }
|
||||
});
|
||||
}, {
|
||||
roleConfig: {
|
||||
role: 'dashou',
|
||||
statusKey: 'guanshistatus',
|
||||
dataFlag: 'isGuanshi',
|
||||
pageId: 'guanshiduan',
|
||||
apiUrl: '/yonghu/guanshixinxi'
|
||||
}
|
||||
}));
|
||||
@@ -1,8 +1,8 @@
|
||||
// pages/merchant-dispatch/merchant-dispatch.js
|
||||
import request from '../../utils/request.js'
|
||||
import { createPage, request } from '../../utils/base-page.js'
|
||||
import PopupService from '../../services/popupService.js' // 引入弹窗服务
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 商家余额 (从全局变量获取)
|
||||
sjyue: '0.00',
|
||||
@@ -143,18 +143,6 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
// 注册全局通知组件(用于顶部提示)
|
||||
registerNotificationComponent() {
|
||||
const app = getApp()
|
||||
const notificationComp = this.selectComponent('#global-notification')
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 教程按钮控制 ====================
|
||||
// 启动定时器:5秒后让按钮缩进
|
||||
startTutorialBtnTimer() {
|
||||
@@ -415,4 +403,4 @@ Page({
|
||||
const { id, item } = e.currentTarget.dataset
|
||||
this.setSelectedType(item)
|
||||
}
|
||||
})
|
||||
}))
|
||||
@@ -1,9 +1,10 @@
|
||||
// pages/merchant-orders/merchant-orders.js
|
||||
const app = getApp();
|
||||
import request from '../../utils/request.js';
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import { reconnectForRole } from '../../utils/role-tab-bar.js';
|
||||
import { getOrderStatusText } from '../../utils/api-helper.js';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 商品类型(和打手端用同一个接口)
|
||||
shangpinleixing: [],
|
||||
@@ -65,17 +66,6 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
// 注册全局通知组件
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 🆕 加载商品类型(和打手端用同一个接口 /dingdan/dsqdhqddlx)
|
||||
async loadShangpinLeixing() {
|
||||
try {
|
||||
@@ -193,7 +183,7 @@ Page({
|
||||
// 处理数据:拼接图片、转换状态中文
|
||||
const processed = newList.map(item => ({
|
||||
...item,
|
||||
zhuangtaiZh: this.getZhuangtaiZh(item.zhuangtai),
|
||||
zhuangtaiZh: getOrderStatusText(item.zhuangtai),
|
||||
zhuangtaiColor: statusItem.color,
|
||||
// 如果有商品图片则用商品图片,否则用默认图
|
||||
tupian: item.tupian
|
||||
@@ -302,16 +292,6 @@ Page({
|
||||
this.loadCurrentStatusOrders(false);
|
||||
},
|
||||
|
||||
// 状态码转中文
|
||||
getZhuangtaiZh(zhuangtai) {
|
||||
const map = {
|
||||
1: '已下单', 2: '进行中', 3: '已完成',
|
||||
4: '退款中', 5: '已退款', 6: '退款失败',
|
||||
7: '指定中', 8: '结算中'
|
||||
};
|
||||
return map[zhuangtai] || '未知状态';
|
||||
},
|
||||
|
||||
// 跳转订单详情
|
||||
goToSjDingdanXiangqing(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
@@ -324,4 +304,4 @@ Page({
|
||||
|
||||
// 图片加载失败
|
||||
onImageError() {}
|
||||
});
|
||||
}))
|
||||
@@ -1,124 +1,114 @@
|
||||
<!-- pages/shangjia-paihang/shangjia-paihang.wxml -->
|
||||
<view class="shangjia-paihang-page">
|
||||
<!-- pages/merchant-rank/merchant-rank.wxml -->
|
||||
<view class="rank-page">
|
||||
|
||||
<!-- 赛博朋克背景元素 -->
|
||||
<view class="cyber-bg">
|
||||
<view class="rank-cyber-bg">
|
||||
<!-- 网格线 -->
|
||||
<view class="cyber-grid"></view>
|
||||
<view class="rank-cyber-grid"></view>
|
||||
<!-- 扫描线 -->
|
||||
<view class="cyber-scanline"></view>
|
||||
<view class="rank-cyber-scanline"></view>
|
||||
<!-- 流动数据流 -->
|
||||
<view class="data-streams">
|
||||
<view class="stream stream-1"></view>
|
||||
<view class="stream stream-2"></view>
|
||||
<view class="stream stream-3"></view>
|
||||
<view class="rank-data-streams">
|
||||
<view class="rank-stream rank-stream--1"></view>
|
||||
<view class="rank-stream rank-stream--2"></view>
|
||||
<view class="rank-stream rank-stream--3"></view>
|
||||
</view>
|
||||
<!-- 浮动粒子 -->
|
||||
<view class="floating-particles">
|
||||
<view class="particle" wx:for="{{12}}" wx:key="index"></view>
|
||||
</view>
|
||||
<view class="rank-particle" wx:for="{{12}}" wx:key="index"></view>
|
||||
</view>
|
||||
|
||||
<!-- 页面头部 -->
|
||||
<view class="page-header">
|
||||
<view class="rank-page-header">
|
||||
<!-- 标题 -->
|
||||
<view class="title-container">
|
||||
<text class="main-title">商家排行榜</text>
|
||||
<view class="title-line"></view>
|
||||
<text class="sub-title">SHANGJIA RANKING</text>
|
||||
<view class="rank-title-container">
|
||||
<text class="rank-main-title">商家排行榜</text>
|
||||
<view class="rank-title-line"></view>
|
||||
<text class="rank-sub-title">SHANGJIA RANKING</text>
|
||||
</view>
|
||||
|
||||
<!-- 切换按钮 -->
|
||||
<view class="toggle-container">
|
||||
<view class="toggle-bg" style="transform: translateX({{paihangLeixing == 1 ? '0' : '100%'}});"></view>
|
||||
<view class="rank-toggle-container">
|
||||
<view class="rank-toggle-bg" style="transform: translateX({{paihangLeixing == 1 ? '0' : '100%'}});"></view>
|
||||
<view
|
||||
class="toggle-btn {{paihangLeixing == 1 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
class="rank-toggle-btn {{paihangLeixing == 1 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
data-leixing="1"
|
||||
bindtap="qiehuangPaihangLeixing"
|
||||
>
|
||||
<text class="toggle-text">今日流水</text>
|
||||
<view class="toggle-line {{paihangLeixing == 1 ? 'active' : ''}}"></view>
|
||||
<text class="rank-toggle-text">今日流水</text>
|
||||
<view class="rank-toggle-line {{paihangLeixing == 1 ? 'active' : ''}}"></view>
|
||||
</view>
|
||||
<view
|
||||
class="toggle-btn {{paihangLeixing == 2 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
class="rank-toggle-btn {{paihangLeixing == 2 ? 'active' : ''}} {{btnFangdou ? 'disabled' : ''}}"
|
||||
data-leixing="2"
|
||||
bindtap="qiehuangPaihangLeixing"
|
||||
>
|
||||
<text class="toggle-text">今月流水</text>
|
||||
<view class="toggle-line {{paihangLeixing == 2 ? 'active' : ''}}"></view>
|
||||
<text class="rank-toggle-text">今月流水</text>
|
||||
<view class="rank-toggle-line {{paihangLeixing == 2 ? 'active' : ''}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view wx:if="{{jiazaiZhuangtai}}" class="loading-state">
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner">
|
||||
<view class="spinner-outer"></view>
|
||||
<view class="spinner-inner"></view>
|
||||
<view class="spinner-core"></view>
|
||||
</view>
|
||||
<text class="loading-text">数据加载中</text>
|
||||
<text class="loading-sub">Connecting to server...</text>
|
||||
<view wx:if="{{jiazaiZhuangtai}}" class="rank-loading-state">
|
||||
<view class="rank-loading-spinner">
|
||||
<view class="rank-spinner-outer"></view>
|
||||
<view class="rank-spinner-inner"></view>
|
||||
<view class="rank-spinner-core"></view>
|
||||
</view>
|
||||
<text class="rank-loading-text">数据加载中</text>
|
||||
<text class="rank-loading-sub">Connecting to server...</text>
|
||||
</view>
|
||||
|
||||
<!-- 排行榜内容 -->
|
||||
<view wx:else class="ranking-content">
|
||||
<view wx:else class="rank-content">
|
||||
|
||||
<!-- 虚拟数据提示 -->
|
||||
<view wx:if="{{!xianshiZhenshiShuju}}" class="demo-notice">
|
||||
<view class="notice-icon">📡</view>
|
||||
<view class="notice-content">
|
||||
<text class="notice-title">排行榜数据准备中</text>
|
||||
<text class="notice-desc">当前显示为模拟数据,实时排名即将开放</text>
|
||||
<view wx:if="{{!xianshiZhenshiShuju}}" class="rank-demo-notice">
|
||||
<view class="rank-notice-icon">📡</view>
|
||||
<view class="rank-notice-content">
|
||||
<text class="rank-notice-title">排行榜数据准备中</text>
|
||||
<text class="rank-notice-desc">当前显示为模拟数据,实时排名即将开放</text>
|
||||
</view>
|
||||
<view class="notice-glow"></view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 前三名区域 ========== -->
|
||||
<view class="top-three-section" wx:if="{{qiansanMing.length > 0}}">
|
||||
<view class="rank-top-three" wx:if="{{qiansanMing.length > 0}}">
|
||||
|
||||
<!-- 第二名 -->
|
||||
<view class="rank-card rank-2 {{flashCardIndex == 1 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
<view class="rank-card rank-card--2 {{flashCardIndex == 1 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
animation="{{flashCardIndex == 1 && flashCardType == 'top3' ? flashAnimation : ''}}"
|
||||
data-index="1" data-type="top3" bindtap="handleCardTap">
|
||||
|
||||
<view class="podium-base podium-silver">
|
||||
<view class="podium-glow"></view>
|
||||
<text class="podium-number">2</text>
|
||||
<view class="rank-podium-base rank-podium-silver">
|
||||
<text class="rank-podium-number">2</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-content">
|
||||
<!-- 头像容器 -->
|
||||
<view class="avatar-frame frame-silver">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar-image" src="{{qiansanMing[1] ? qiansanMing[1].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="avatar-halo halo-silver"></view>
|
||||
<view class="avatar-sparkles">
|
||||
<view class="sparkle s1"></view>
|
||||
<view class="sparkle s2"></view>
|
||||
<view class="sparkle s3"></view>
|
||||
<view class="rank-avatar-frame rank-frame-silver">
|
||||
<view class="rank-avatar-container">
|
||||
<image class="rank-avatar-image" src="{{qiansanMing[1] ? qiansanMing[1].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo rank-halo-silver"></view>
|
||||
<view class="rank-sparkle" style="top:15%;left:15%;animation-delay:0s"></view>
|
||||
<view class="rank-sparkle" style="top:25%;right:25%;animation-delay:0.5s"></view>
|
||||
<view class="rank-sparkle" style="bottom:35%;left:35%;animation-delay:1s"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rank-badge badge-silver">2</view>
|
||||
<view class="rank-badge rank-badge-silver">2</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-card">
|
||||
<view class="info-header">
|
||||
<text class="user-name">{{qiansanMing[1] ? qiansanMing[1].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-user-info-card">
|
||||
<text class="rank-user-name">{{qiansanMing[1] ? qiansanMing[1].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-info-body">
|
||||
<view class="rank-uid-row">
|
||||
<text class="rank-uid-label">ID:</text>
|
||||
<text class="rank-uid-value">{{qiansanMing[1] ? qiansanMing[1].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="info-body">
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">ID:</text>
|
||||
<text class="uid-value">{{qiansanMing[1] ? qiansanMing[1].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="income-row">
|
||||
<text class="income-label">流水:</text>
|
||||
<view class="income-value">
|
||||
<text class="currency">¥</text>
|
||||
<text class="amount {{qiansanMing[1] ? qiansanMing[1].jineClass : ''}}">{{qiansanMing[1] ? qiansanMing[1].jine : '0.00'}}</text>
|
||||
<view class="rank-income-row">
|
||||
<text class="rank-income-label">流水:</text>
|
||||
<view class="rank-income-value">
|
||||
<text class="rank-currency">¥</text>
|
||||
<text class="rank-amount {{qiansanMing[1] ? qiansanMing[1].jineClass : ''}}">{{qiansanMing[1] ? qiansanMing[1].jine : '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -127,62 +117,47 @@
|
||||
</view>
|
||||
|
||||
<!-- 第一名 -->
|
||||
<view class="rank-card rank-1 {{flashCardIndex == 0 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
<view class="rank-card rank-card--1 {{flashCardIndex == 0 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
animation="{{flashCardIndex == 0 && flashCardType == 'top3' ? flashAnimation : ''}}"
|
||||
data-index="0" data-type="top3" bindtap="handleCardTap">
|
||||
|
||||
<!-- 皇冠 -->
|
||||
<view class="crown-container" animation="{{crownAnimation}}">
|
||||
<view class="crown">
|
||||
<text class="crown-icon">👑</text>
|
||||
<view class="crown-rays">
|
||||
<view class="ray ray-1"></view>
|
||||
<view class="ray ray-2"></view>
|
||||
<view class="ray ray-3"></view>
|
||||
<view class="ray ray-4"></view>
|
||||
<view class="ray ray-5"></view>
|
||||
<view class="ray ray-6"></view>
|
||||
</view>
|
||||
<view class="crown-glow"></view>
|
||||
</view>
|
||||
<view class="rank-crown-container" animation="{{crownAnimation}}">
|
||||
<text class="rank-crown-icon">👑</text>
|
||||
<view class="rank-crown-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="podium-base podium-gold">
|
||||
<view class="podium-glow"></view>
|
||||
<text class="podium-number">1</text>
|
||||
<view class="rank-podium-base rank-podium-gold">
|
||||
<text class="rank-podium-number">1</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-content">
|
||||
<!-- 头像容器 -->
|
||||
<view class="avatar-frame frame-gold">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar-image" src="{{qiansanMing[0] ? qiansanMing[0].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="avatar-halo halo-gold"></view>
|
||||
<view class="avatar-sparkles">
|
||||
<view class="sparkle s1"></view>
|
||||
<view class="sparkle s2"></view>
|
||||
<view class="sparkle s3"></view>
|
||||
<view class="sparkle s4"></view>
|
||||
<view class="rank-avatar-frame rank-frame-gold">
|
||||
<view class="rank-avatar-container">
|
||||
<image class="rank-avatar-image" src="{{qiansanMing[0] ? qiansanMing[0].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo rank-halo-gold"></view>
|
||||
<view class="rank-sparkle" style="top:15%;left:15%;animation-delay:0s"></view>
|
||||
<view class="rank-sparkle" style="top:25%;right:25%;animation-delay:0.5s"></view>
|
||||
<view class="rank-sparkle" style="bottom:35%;left:35%;animation-delay:1s"></view>
|
||||
<view class="rank-sparkle" style="bottom:15%;right:15%;animation-delay:1.5s"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rank-badge badge-gold">1</view>
|
||||
<view class="rank-badge rank-badge-gold">1</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-card">
|
||||
<view class="info-header">
|
||||
<text class="user-name">{{qiansanMing[0] ? qiansanMing[0].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-user-info-card">
|
||||
<text class="rank-user-name">{{qiansanMing[0] ? qiansanMing[0].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-info-body">
|
||||
<view class="rank-uid-row">
|
||||
<text class="rank-uid-label">ID:</text>
|
||||
<text class="rank-uid-value">{{qiansanMing[0] ? qiansanMing[0].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="info-body">
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">ID:</text>
|
||||
<text class="uid-value">{{qiansanMing[0] ? qiansanMing[0].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="income-row">
|
||||
<text class="income-label">流水:</text>
|
||||
<view class="income-value">
|
||||
<text class="currency">¥</text>
|
||||
<text class="amount {{qiansanMing[0] ? qiansanMing[0].jineClass : ''}}">{{qiansanMing[0] ? qiansanMing[0].jine : '0.00'}}</text>
|
||||
<view class="rank-income-row">
|
||||
<text class="rank-income-label">流水:</text>
|
||||
<view class="rank-income-value">
|
||||
<text class="rank-currency">¥</text>
|
||||
<text class="rank-amount {{qiansanMing[0] ? qiansanMing[0].jineClass : ''}}">{{qiansanMing[0] ? qiansanMing[0].jine : '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -191,45 +166,40 @@
|
||||
</view>
|
||||
|
||||
<!-- 第三名 -->
|
||||
<view class="rank-card rank-3 {{flashCardIndex == 2 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
<view class="rank-card rank-card--3 {{flashCardIndex == 2 && flashCardType == 'top3' ? 'flash' : ''}}"
|
||||
animation="{{flashCardIndex == 2 && flashCardType == 'top3' ? flashAnimation : ''}}"
|
||||
data-index="2" data-type="top3" bindtap="handleCardTap">
|
||||
|
||||
<view class="podium-base podium-bronze">
|
||||
<view class="podium-glow"></view>
|
||||
<text class="podium-number">3</text>
|
||||
<view class="rank-podium-base rank-podium-bronze">
|
||||
<text class="rank-podium-number">3</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-content">
|
||||
<!-- 头像容器 -->
|
||||
<view class="avatar-frame frame-bronze">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar-image" src="{{qiansanMing[2] ? qiansanMing[2].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="avatar-halo halo-bronze"></view>
|
||||
<view class="avatar-sparkles">
|
||||
<view class="sparkle s1"></view>
|
||||
<view class="sparkle s2"></view>
|
||||
<view class="sparkle s3"></view>
|
||||
<view class="rank-avatar-frame rank-frame-bronze">
|
||||
<view class="rank-avatar-container">
|
||||
<image class="rank-avatar-image" src="{{qiansanMing[2] ? qiansanMing[2].full_touxiang : ''}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo rank-halo-bronze"></view>
|
||||
<view class="rank-sparkle" style="top:15%;left:15%;animation-delay:0s"></view>
|
||||
<view class="rank-sparkle" style="top:25%;right:25%;animation-delay:0.5s"></view>
|
||||
<view class="rank-sparkle" style="bottom:35%;left:35%;animation-delay:1s"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rank-badge badge-bronze">3</view>
|
||||
<view class="rank-badge rank-badge-bronze">3</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-card">
|
||||
<view class="info-header">
|
||||
<text class="user-name">{{qiansanMing[2] ? qiansanMing[2].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-user-info-card">
|
||||
<text class="rank-user-name">{{qiansanMing[2] ? qiansanMing[2].nicheng : '等待上榜'}}</text>
|
||||
<view class="rank-info-body">
|
||||
<view class="rank-uid-row">
|
||||
<text class="rank-uid-label">ID:</text>
|
||||
<text class="rank-uid-value">{{qiansanMing[2] ? qiansanMing[2].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="info-body">
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">ID:</text>
|
||||
<text class="uid-value">{{qiansanMing[2] ? qiansanMing[2].uid : '----'}}</text>
|
||||
</view>
|
||||
<view class="income-row">
|
||||
<text class="income-label">流水:</text>
|
||||
<view class="income-value">
|
||||
<text class="currency">¥</text>
|
||||
<text class="amount {{qiansanMing[2] ? qiansanMing[2].jineClass : ''}}">{{qiansanMing[2] ? qiansanMing[2].jine : '0.00'}}</text>
|
||||
<view class="rank-income-row">
|
||||
<text class="rank-income-label">流水:</text>
|
||||
<view class="rank-income-value">
|
||||
<text class="rank-currency">¥</text>
|
||||
<text class="rank-amount {{qiansanMing[2] ? qiansanMing[2].jineClass : ''}}">{{qiansanMing[2] ? qiansanMing[2].jine : '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -239,50 +209,46 @@
|
||||
</view>
|
||||
|
||||
<!-- ========== 其他排名列表 ========== -->
|
||||
<view class="other-ranks-section" wx:if="{{paihangShuju.length > 0}}">
|
||||
<view class="section-title">
|
||||
<view class="title-line"></view>
|
||||
<text class="title-text">其他排名</text>
|
||||
<view class="title-line"></view>
|
||||
<view wx:if="{{paihangShuju.length > 0}}">
|
||||
<view class="rank-section-title">
|
||||
<view class="rank-section-line"></view>
|
||||
<text class="rank-section-text">其他排名</text>
|
||||
<view class="rank-section-line"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-list">
|
||||
<view class="rank-item {{flashCardIndex == index && flashCardType == 'normal' ? 'flash' : ''}}"
|
||||
<view class="rank-list-item {{flashCardIndex == index && flashCardType == 'normal' ? 'flash' : ''}}"
|
||||
wx:for="{{paihangShuju}}" wx:key="index"
|
||||
animation="{{flashCardIndex == index && flashCardType == 'normal' ? flashAnimation : ''}}"
|
||||
data-index="{{index}}" data-type="normal" bindtap="handleCardTap">
|
||||
|
||||
<view class="rank-number">
|
||||
<text class="number-text">{{xianshiZhenshiShuju ? index + 4 : index + 1}}</text>
|
||||
<view class="number-glow"></view>
|
||||
<text class="rank-number-text">{{xianshiZhenshiShuju ? index + 4 : index + 1}}</text>
|
||||
<view class="rank-number-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-avatar">
|
||||
<image class="rank-avatar-img" src="{{item.full_touxiang}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-avatar-halo"></view>
|
||||
<view class="rank-list-avatar">
|
||||
<image class="rank-list-avatar-img" src="{{item.full_touxiang}}" mode="aspectFill" binderror="tupianJiazaiShibai"></image>
|
||||
<view class="rank-list-avatar-halo"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-info">
|
||||
<text class="rank-name">{{item.nicheng}}</text>
|
||||
<text class="rank-uid">UID: {{item.uid}}</text>
|
||||
<view class="rank-list-info">
|
||||
<text class="rank-list-name">{{item.nicheng}}</text>
|
||||
<text class="rank-list-uid">UID: {{item.uid}}</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-income">
|
||||
<text class="income-currency">¥</text>
|
||||
<text class="income-amount {{item.jineClass}}">{{item.jine}}</text>
|
||||
<view class="income-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="rank-hover"></view>
|
||||
<view class="rank-list-income">
|
||||
<text class="rank-income-currency">¥</text>
|
||||
<text class="rank-income-amount {{item.jineClass}}">{{item.jine}}</text>
|
||||
<view class="rank-income-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view wx:if="{{xianshiZhenshiShuju && paihangShuju.length === 0 && qiansanMing.length === 0}}" class="empty-state">
|
||||
<view class="empty-icon">🏆</view>
|
||||
<text class="empty-title">暂无排名数据</text>
|
||||
<text class="empty-desc">成为第一个登榜的商家吧</text>
|
||||
<view wx:if="{{xianshiZhenshiShuju && paihangShuju.length === 0 && qiansanMing.length === 0}}" class="rank-empty-state">
|
||||
<view class="rank-empty-icon">🏆</view>
|
||||
<text class="rank-empty-title">暂无排名数据</text>
|
||||
<text class="rank-empty-desc">成为第一个登榜的商家吧</text>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+68
-194
@@ -1,21 +1,11 @@
|
||||
// pages/shangjiazhongxin/shangjiazhongxin.js
|
||||
// pages/shangjiazhongxin/shangjiiazhongxin.js
|
||||
|
||||
import request from '../../utils/request.js';
|
||||
import popupService from '../../services/popupService.js';
|
||||
import {
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
ensureRoleOnCenterPage,
|
||||
clearCacheAndEnterNormal,
|
||||
} from '../../utils/role-tab-bar.js';
|
||||
import { lockPrimaryRole } from '../../utils/primary-role.js';
|
||||
import { createPage, ensureRoleOnCenterPage, lockPrimaryRole, parseSceneOptions } from '../../utils/base-page.js';
|
||||
import { ensureLogin } from '../../utils/login.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
// 图片资源
|
||||
imgUrls: {
|
||||
@@ -42,7 +32,6 @@ Page({
|
||||
|
||||
// 商家状态
|
||||
isShangjia: false,
|
||||
isLoading: false,
|
||||
isAutoRegistering: false,
|
||||
|
||||
// 用户基础信息
|
||||
@@ -62,52 +51,57 @@ Page({
|
||||
|
||||
// 交互控制
|
||||
inviteCode: '',
|
||||
lastRefreshTime: 0,
|
||||
canRefresh: true,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.initImageUrls();
|
||||
this.checkShangjiaStatus();
|
||||
const parsed = parseSceneOptions(options);
|
||||
this.setupImageUrls();
|
||||
this.checkRoleStatus();
|
||||
|
||||
const { inviteCode } = options || {};
|
||||
const inviteCode = parsed.inviteCode;
|
||||
if (inviteCode) {
|
||||
this.setData({ inviteCode });
|
||||
setTimeout(() => this.handleInviteCodeWithLoginCheck(inviteCode), 500);
|
||||
}
|
||||
|
||||
if (!app.globalData.hasShownPopupOnColdStart) {
|
||||
app.globalData.hasShownPopupOnColdStart = true;
|
||||
setTimeout(() => popupService.checkAndShow(this, 'shangjiaduan'), 300);
|
||||
setTimeout(() => this.handleInviteCodeWithLoginCheck({
|
||||
inviteCode,
|
||||
role: 'shangjia',
|
||||
statusKey: 'shangjiastatus',
|
||||
alreadyMsg: '您已是商家',
|
||||
onNeedLogin: (code) => {
|
||||
this.setData({ inviteCode: code });
|
||||
ensureLogin({ silent: false, page: this })
|
||||
.then(() => {
|
||||
if (!this.data.isShangjia) {
|
||||
wx.showLoading({ title: '商家注册中...', mask: true });
|
||||
this.setData({ isAutoRegistering: true });
|
||||
this.onRegister();
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
onReady() {
|
||||
if (isCenterPageActive(this, 'isShangjia', 'shangjiastatus')) {
|
||||
onAlreadyRegistered: () => {
|
||||
wx.showToast({ title: '您已是商家', icon: 'success' });
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
},
|
||||
}), 500);
|
||||
}
|
||||
|
||||
this.checkColdStartPopup('shangjiaduan');
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.registerNotificationComponent();
|
||||
if (isCenterPageActive(this, 'isShangjia', 'shangjiastatus')) {
|
||||
if (!this.data.isShangjia) this.setData({ isShangjia: true });
|
||||
if (this.data.isShangjia) {
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
this.refreshShangjiaInfo();
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
popupComp?.cleanup?.();
|
||||
},
|
||||
|
||||
// 图片路径初始化
|
||||
initImageUrls() {
|
||||
setupImageUrls() {
|
||||
const ossBase = app.globalData.ossImageUrl || '';
|
||||
const imgDir = `${ossBase}beijing/shangjiaduan/`;
|
||||
this.setData({
|
||||
imgUrls: {
|
||||
imgUrls: this.initImageUrls({
|
||||
pageBg: `${imgDir}page_bg.png`,
|
||||
avatarCardBg: `${imgDir}avatar_card_bg.png`,
|
||||
assetCardBg: `${imgDir}asset_card_bg.png`,
|
||||
@@ -128,16 +122,16 @@ Page({
|
||||
avatarFrame: `${imgDir}avatar_frame.png`,
|
||||
iconClear: `${ossBase}beijing/tubiao/grzx_qingchu.jpg`,
|
||||
iconSwitch: '/images/_exit.png',
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
// 商家状态检查
|
||||
checkShangjiaStatus() {
|
||||
// 商家状态检查(覆盖基类 checkRoleStatus,增加 getShangjiaInfo 调用)
|
||||
checkRoleStatus() {
|
||||
try {
|
||||
const shangjiastatus = wx.getStorageSync('shangjiastatus');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (isRoleStatusActive(shangjiastatus)) {
|
||||
if (shangjiastatus) {
|
||||
this.setData({ isShangjia: true, uid: uid || '' });
|
||||
this.loadAvatar();
|
||||
this.getShangjiaInfo();
|
||||
@@ -151,32 +145,20 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
loadAvatar() {
|
||||
const { ossImageUrl = '', morentouxiang = 'avatar/default.jpg' } = app.globalData || {};
|
||||
const touxiang = wx.getStorageSync('touxiang') || '';
|
||||
let avatarUrl = '';
|
||||
if (touxiang) {
|
||||
avatarUrl = ossImageUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang);
|
||||
} else {
|
||||
avatarUrl = ossImageUrl + (morentouxiang.startsWith('/') ? morentouxiang : '/' + morentouxiang);
|
||||
}
|
||||
this.setData({ avatarUrl });
|
||||
},
|
||||
|
||||
// 获取商家信息
|
||||
async getShangjiaInfo() {
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/shangjiaxinxi', method: 'POST' });
|
||||
if (res?.data?.code === 200) {
|
||||
const data = res.data.data;
|
||||
await this.loadData({
|
||||
url: '/yonghu/shangjiaxinxi',
|
||||
method: 'POST',
|
||||
successCode: 200,
|
||||
errorMsg: '获取商家信息失败',
|
||||
onSuccess: (data) => {
|
||||
// 更新全局缓存
|
||||
app.globalData.shangjia = data;
|
||||
wx.setStorageSync('shangjiastatus', 1);
|
||||
|
||||
const chenghaoList = data.chenghao_list || [];
|
||||
|
||||
// 字段映射
|
||||
this.setData({
|
||||
nicheng: data.nicheng || '',
|
||||
sjyue: data.sjyue || '0.00',
|
||||
@@ -194,128 +176,41 @@ Page({
|
||||
this.setData({ isAutoRegistering: false });
|
||||
wx.showToast({ title: '商家注册成功', icon: 'success' });
|
||||
}
|
||||
} else {
|
||||
throw new Error(res?.data?.msg || '获取商家信息失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取商家信息失败', error);
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: error.message || '加载失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
refreshShangjiaInfo() {
|
||||
const now = Date.now();
|
||||
if (!this.data.canRefresh || now - this.data.lastRefreshTime < 3000) {
|
||||
wx.showToast({ title: '操作过快', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ lastRefreshTime: now, canRefresh: false });
|
||||
this.getShangjiaInfo();
|
||||
setTimeout(() => this.setData({ canRefresh: true }), 3000);
|
||||
},
|
||||
|
||||
// 邀请码注册
|
||||
handleInviteCodeWithLoginCheck(inviteCode) {
|
||||
const token = wx.getStorageSync('token');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (!token || !uid) {
|
||||
this.setData({ inviteCode });
|
||||
ensureLogin({ silent: false, page: this })
|
||||
.then(() => {
|
||||
if (!this.data.isShangjia) {
|
||||
wx.showLoading({ title: '商家注册中...', mask: true });
|
||||
this.setData({ isAutoRegistering: true });
|
||||
this.onRegister();
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
if (!this.data.isShangjia) {
|
||||
wx.showLoading({ title: '商家注册中...', mask: true });
|
||||
this.setData({ isAutoRegistering: true });
|
||||
this.onRegister();
|
||||
} else {
|
||||
wx.showToast({ title: '您已是商家', icon: 'success' });
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
}
|
||||
}
|
||||
this.throttledRefresh(() => this.getShangjiaInfo());
|
||||
},
|
||||
|
||||
async onRegister() {
|
||||
const { inviteCode } = this.data;
|
||||
if (!inviteCode) {
|
||||
wx.showToast({ title: '请输入邀请码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (inviteCode.length > 100) {
|
||||
wx.showToast({ title: '邀请码不能超过100字符', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/shangjiahuce',
|
||||
method: 'POST',
|
||||
data: { inviteCode }
|
||||
});
|
||||
if (res?.data?.code === 200) {
|
||||
const data = res.data.data || {};
|
||||
syncRoleStatuses(data);
|
||||
syncProfileFields(data);
|
||||
if (!Object.prototype.hasOwnProperty.call(data, 'shangjiastatus')) {
|
||||
wx.setStorageSync('shangjiastatus', 1);
|
||||
app.globalData.shangjiastatus = 1;
|
||||
}
|
||||
app.globalData.shangjia = data;
|
||||
const chenghaoList = data.chenghao_list || [];
|
||||
await this.registerWithInviteCode({
|
||||
inviteCode,
|
||||
apiUrl: '/yonghu/shangjiahuce',
|
||||
role: 'shangjia',
|
||||
statusKey: 'shangjiastatus',
|
||||
successMsg: '商家注册成功',
|
||||
onSuccess: (userData) => {
|
||||
app.globalData.shangjia = userData;
|
||||
const chenghaoList = userData.chenghao_list || [];
|
||||
this.setData({
|
||||
isShangjia: true,
|
||||
nicheng: data.nicheng || '',
|
||||
sjyue: data.sjyue || '0.00',
|
||||
fabu: data.fabu || 0,
|
||||
tuikuan: data.tuikuan || 0,
|
||||
jinriliushui: data.jinriliushui || '0.00',
|
||||
jinyueliushui: data.jinyueliushui || '0.00',
|
||||
jinridingdan: data.jinridingdan || 0,
|
||||
nicheng: userData.nicheng || '',
|
||||
sjyue: userData.sjyue || '0.00',
|
||||
fabu: userData.fabu || 0,
|
||||
tuikuan: userData.tuikuan || 0,
|
||||
jinriliushui: userData.jinriliushui || '0.00',
|
||||
jinyueliushui: userData.jinyueliushui || '0.00',
|
||||
jinridingdan: userData.jinridingdan || 0,
|
||||
chenghaoList: chenghaoList,
|
||||
isLoading: false,
|
||||
isAutoRegistering: false,
|
||||
});
|
||||
this.loadAvatar();
|
||||
ensureRoleOnCenterPage(this, 'shangjia');
|
||||
lockPrimaryRole('shangjia');
|
||||
wx.showToast({ title: '商家注册成功', icon: 'success' });
|
||||
} else {
|
||||
throw new Error(res?.data?.msg || '注册失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('商家注册失败', error);
|
||||
this.setData({ isLoading: false, isAutoRegistering: false });
|
||||
wx.showToast({ title: error.message, icon: 'error' });
|
||||
}
|
||||
},
|
||||
|
||||
// 辅助功能
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp?.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
copyUid() {
|
||||
if (!this.data.uid) {
|
||||
wx.showToast({ title: 'UID不存在', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.setClipboardData({
|
||||
data: this.data.uid,
|
||||
success: () => wx.showToast({ title: 'UID已复制', icon: 'success' }),
|
||||
fail: () => wx.showToast({ title: '复制失败', icon: 'error' })
|
||||
});
|
||||
},
|
||||
|
||||
@@ -336,33 +231,12 @@ Page({
|
||||
goToWithdraw() { wx.navigateTo({ url: '/pages/withdraw/withdraw' }); },
|
||||
goToRanking() { wx.navigateTo({ url: '/pages/fighter-rank/fighter-rank?type=shangjia' }); },
|
||||
goToMessages() { wx.navigateTo({ url: '/pages/merchant-msg/merchant-msg' }); },
|
||||
goToKefu() {
|
||||
const { kefuConfig } = app.globalData;
|
||||
if (kefuConfig?.link && kefuConfig?.enterpriseId) {
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: { url: kefuConfig.link },
|
||||
corpId: kefuConfig.enterpriseId,
|
||||
fail: () => wx.showToast({ title: '客服暂不可用', icon: 'none' })
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: '客服配置未加载', icon: 'none' });
|
||||
}
|
||||
}, {
|
||||
roleConfig: {
|
||||
role: 'shangjia',
|
||||
statusKey: 'shangjiastatus',
|
||||
dataFlag: 'isShangjia',
|
||||
pageId: 'shangjiaduan',
|
||||
apiUrl: '/yonghu/shangjiaxinxi',
|
||||
},
|
||||
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '清除缓存',
|
||||
content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (r) => {
|
||||
if (r.confirm) clearCacheAndEnterNormal(this);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
switchToNormal() {
|
||||
lockPrimaryRole('normal');
|
||||
wx.reLaunch({ url: '/pages/mine/mine' });
|
||||
},
|
||||
});
|
||||
}));
|
||||
@@ -6,9 +6,10 @@ import { getLocalImUserId } from '../../utils/im-user.js';
|
||||
import { normalizeConversationsList } from '../../utils/conversation-display.js';
|
||||
import { reconnectForRole } from '../../utils/role-tab-bar.js';
|
||||
import { getPrimaryRole } from '../../utils/primary-role.js';
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
const { jianquanxian } = require('../../utils/imAuth/jianquanxian');
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
allConversations: [],
|
||||
filteredConversations: [],
|
||||
@@ -88,16 +89,6 @@ Page({
|
||||
return true;
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification(),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
autoConnect() {
|
||||
const targetUserId = getLocalImUserId(app);
|
||||
if (!targetUserId) return;
|
||||
@@ -400,4 +391,4 @@ Page({
|
||||
const key = `filteredConversations[${index}].data.avatar`;
|
||||
this.setData({ [key]: def });
|
||||
}
|
||||
});
|
||||
}))
|
||||
+247
-31
@@ -1,38 +1,254 @@
|
||||
.msg-page { background: #f5f5f5; }
|
||||
/* pages/messages/messages.wxss */
|
||||
|
||||
.header { display: flex; align-items: center; padding: 20rpx 24rpx; background: #fff; border-bottom: 1rpx solid #e8e8e8; }
|
||||
.search-bar { flex: 1; }
|
||||
.search-input { height: 68rpx; }
|
||||
.search-icon { font-size: 32rpx; margin-left: 10rpx; color: #999; }
|
||||
.notify-switch { margin-left: 20rpx; }
|
||||
.notify-text { font-size: 26rpx; color: #666; padding: 10rpx 16rpx; background: #f0f0f0; border-radius: 30rpx; }
|
||||
.msg-page {
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.tab-row { border-bottom: 1rpx solid #e8e8e8; }
|
||||
/* .tab-item 已由全局 .tab-item 覆盖 */
|
||||
.tab-item.active { color: #07c160; font-weight: 600; }
|
||||
.tab-item.active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 60rpx; height: 6rpx; background: #07c160; border-radius: 3rpx; }
|
||||
.tab-badge { border-radius: 20rpx; padding: 2rpx 12rpx; margin-left: 8rpx; }
|
||||
/* 顶部搜索 + 通知 */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 24rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.conversation-list { background: #fff; }
|
||||
.conversation-item { display: flex; align-items: center; padding: 20rpx 24rpx; border-bottom: 1rpx solid #f0f0f0; }
|
||||
.has-unread { background: #fafafa; }
|
||||
.avatar { width: 96rpx; height: 96rpx; border-radius: 12rpx; margin-right: 20rpx; background: #eee; }
|
||||
.info { flex: 1; }
|
||||
.top-line { display: flex; justify-content: space-between; margin-bottom: 8rpx; }
|
||||
.name { font-size: 30rpx; color: #1a1a1a; font-weight: 500; }
|
||||
.time { font-size: 22rpx; color: #999; }
|
||||
.bottom-line { display: flex; justify-content: space-between; align-items: center; }
|
||||
.last-msg { font-size: 26rpx; color: #888; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.unread-dot { background: #fa5151; color: #fff; font-size: 20rpx; border-radius: 50%; min-width: 36rpx; height: 36rpx; line-height: 36rpx; text-align: center; margin-left: 12rpx; }
|
||||
.load-tip { text-align: center; padding: 24rpx; color: #999; font-size: 24rpx; }
|
||||
.empty { text-align: center; padding: 200rpx 0; color: #bbb; font-size: 28rpx; }
|
||||
.search-bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f5f5f5;
|
||||
border-radius: 36rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 68rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
height: 68rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
font-size: 28rpx;
|
||||
margin-left: 10rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.notify-switch {
|
||||
margin-left: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.notify-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
padding: 10rpx 20rpx;
|
||||
background: #f0f0f0;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
/* TAB 栏 */
|
||||
.tab-row {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #e8e8e8;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 88rpx;
|
||||
position: relative;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: #07c160;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
background: #07c160;
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
|
||||
.tab-label {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tab-badge {
|
||||
background: #fa5151;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
margin-left: 8rpx;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 会话列表 */
|
||||
.conversation-list {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.conversation-item:active {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.has-unread {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
background: #eee;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.top-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 30rpx;
|
||||
color: #1a1a1a;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.bottom-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.last-msg {
|
||||
font-size: 26rpx;
|
||||
color: #888;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.unread-dot {
|
||||
background: #fa5151;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
border-radius: 50%;
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
text-align: center;
|
||||
margin-left: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.load-tip {
|
||||
text-align: center;
|
||||
padding: 24rpx;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
padding: 200rpx 0;
|
||||
color: #bbb;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 客服入口 */
|
||||
.kefu-entry { text-align: center; padding: 100rpx 0; }
|
||||
.kefu-btn { width: 300rpx; background: #07c160; color: #fff; border-radius: 40rpx; font-size: 28rpx; }
|
||||
.kefu-entry {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.kefu-btn {
|
||||
width: 300rpx;
|
||||
background: #07c160;
|
||||
color: #fff;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 操作弹窗 */
|
||||
.action-mask { background: rgba(0,0,0,0.4); z-index: 10000; }
|
||||
.action-sheet { position: absolute; bottom: 0; left: 0; right: 0; background: #fff; border-radius: 24rpx 24rpx 0 0; padding: 20rpx 20rpx 150rpx 20rpx; }
|
||||
.action-btn { text-align: center; padding: 28rpx 0; font-size: 32rpx; border-bottom: 1rpx solid #eee; }
|
||||
.cancel { color: #999; margin-top: 12rpx; border-bottom: none; }
|
||||
.action-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.action-sheet {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
padding: 20rpx 20rpx 60rpx;
|
||||
animation: slideInUp 0.25s ease-out;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
text-align: center;
|
||||
padding: 28rpx 0;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
color: #999;
|
||||
margin-top: 12rpx;
|
||||
border-bottom: none;
|
||||
}
|
||||
+4
-33
@@ -4,13 +4,13 @@ import { ensureLogin } from '../../utils/login';
|
||||
import {
|
||||
backfillUserProfileCache,
|
||||
getUserProfileForDisplay,
|
||||
clearCacheAndEnterNormal,
|
||||
isRoleStatusActive,
|
||||
ensureRoleOnCenterPage,
|
||||
} from '../../utils/role-tab-bar';
|
||||
import { enterLockedRole } from '../../utils/primary-role.js';
|
||||
import { createPage } from '../../utils/base-page.js';
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
avatarUrl: '',
|
||||
userNicheng: '',
|
||||
@@ -34,7 +34,7 @@ Page({
|
||||
|
||||
onShow() {
|
||||
this.initIconUrls();
|
||||
this.registerNotification();
|
||||
this.registerNotificationComponent();
|
||||
this.checkAvatarPrompt();
|
||||
backfillUserProfileCache(app);
|
||||
this.loadUserData();
|
||||
@@ -50,16 +50,6 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
registerNotification() {
|
||||
const comp = this.selectComponent('#global-notification')
|
||||
if (comp?.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: d => comp.showNotification(d),
|
||||
hide: () => comp.hideNotification()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
checkAvatarPrompt() {
|
||||
const ds = wx.getStorageSync('dashoustatus')
|
||||
const tx = wx.getStorageSync('touxiang') || ''
|
||||
@@ -145,25 +135,6 @@ Page({
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
goToKefu() {
|
||||
const { link, enterpriseId } = app.globalData.kefuConfig || {}
|
||||
if (!link || !enterpriseId) { wx.showToast({ title: '客服配置未加载', icon: 'none' }); return }
|
||||
wx.openCustomerServiceChat({ extInfo: { url: link }, corpId: enterpriseId })
|
||||
},
|
||||
goToCustomService() { this.goToKefu() },
|
||||
goToGuanzhuA() { wx.navigateTo({ url: '/pages/manager-assign/manager-assign' }) },
|
||||
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '清除缓存',
|
||||
content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: r => {
|
||||
if (r.confirm) {
|
||||
clearCacheAndEnterNormal(this);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
})
|
||||
}))
|
||||
+149
-126
@@ -1,190 +1,213 @@
|
||||
/* pages/orders/orders.wxss */
|
||||
|
||||
.dingdan-page {
|
||||
background-color: #f8f9fa;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
background: #f2f2f2;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.dingdan-tabs-scroll {
|
||||
/* TAB栏 */
|
||||
.dingdan-tabs-scroll {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
background: #fff;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.dingdan-tabs-container {
|
||||
.dingdan-tabs-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
height: 88rpx;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.dingdan-tab-item {
|
||||
.dingdan-tab-item {
|
||||
position: relative;
|
||||
}
|
||||
padding: 0 28rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dingdan-tab-item:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* .dingdan-tab-item.tab-active 已由全局 .tab-capsule--active 覆盖 */
|
||||
|
||||
.tab-active-line {
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background-color: #4caf50;
|
||||
border-radius: 2rpx;
|
||||
margin-top: 6rpx;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: scale(0.8); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.dingdan-list-container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
.dingdan-tab-item.tab-active .tab-text {
|
||||
color: #e02e24;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
.tab-active-line {
|
||||
width: 40rpx;
|
||||
height: 6rpx;
|
||||
background: #e02e24;
|
||||
border-radius: 3rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
/* 列表区 */
|
||||
.dingdan-list-container {
|
||||
padding: 20rpx 24rpx;
|
||||
}
|
||||
|
||||
/* 加载 */
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 400rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
border: 6rpx solid #e0e0e0;
|
||||
border-top-color: #4caf50;
|
||||
.loading-spinner {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border: 4rpx solid #f0f0f0;
|
||||
border-top-color: #e02e24;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
animation: spin 0.7s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
/* 空状态 */
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 500rpx;
|
||||
padding-top: 80rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.empty-image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
opacity: 0.5;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: #999999;
|
||||
}
|
||||
.empty-text {
|
||||
font-size: 30rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.dingdan-card {
|
||||
.empty-subtext {
|
||||
font-size: 24rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* 订单卡片 */
|
||||
.dingdan-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.dingdan-card:active {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.dingdan-card-left {
|
||||
.dingdan-card-left {
|
||||
flex-shrink: 0;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.dingdan-image {
|
||||
.dingdan-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
border-radius: 8rpx;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.dingdan-card-center {
|
||||
.dingdan-card-center {
|
||||
flex: 1;
|
||||
height: 160rpx;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.dingdan-jieshao {
|
||||
.dingdan-jieshao {
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
color: #333333;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.dingdan-card-right {
|
||||
.dingdan-card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
height: 160rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 价格样式 */
|
||||
.dingdan-jine {
|
||||
display: flex;
|
||||
align-items: center; /* 垂直居中 */
|
||||
}
|
||||
|
||||
.jine-symbol {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
margin-right: 4rpx; /* 添加右边距,让符号和数字有间隔 */
|
||||
}
|
||||
|
||||
.jine-number {
|
||||
font-size: 32rpx; /* 整数和小数一样大 */
|
||||
color: #333333;
|
||||
.dingdan-zhuangtai {
|
||||
font-size: 24rpx;
|
||||
color: #e02e24;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.load-more-line {
|
||||
.dingdan-jine {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.jine-symbol {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.jine-number {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 加载更多 */
|
||||
.load-more-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.load-more-line {
|
||||
width: 80rpx;
|
||||
height: 1rpx;
|
||||
background-color: #e0e0e0;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.load-more-text {
|
||||
.load-more-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
.dingdan-image {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
.no-more-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.dingdan-card-center {
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
.dingdan-card-right {
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
.jine-number {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
.no-more-text {
|
||||
font-size: 24rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
@@ -62,9 +62,9 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 && !detailItem.bohuiliyou }}" class="btn primary" bindtap="openShensu">申诉</view>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3 }}" class="btn primary" bindtap="startPay">立即缴纳</view>
|
||||
<view class="btn default" bindtap="closeDetail">关闭</view>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 && !detailItem.bohuiliyou }}" class="pbtn primary" bindtap="openShensu">申诉</view>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3 }}" class="pbtn primary" bindtap="startPay">立即缴纳</view>
|
||||
<view class="pbtn default" bindtap="closeDetail">关闭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -100,8 +100,8 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="btn default" bindtap="closeShensu">取消</view>
|
||||
<view class="btn primary" bindtap="submitShensu">提交申诉</view>
|
||||
<view class="pbtn default" bindtap="closeShensu">取消</view>
|
||||
<view class="pbtn primary" bindtap="submitShensu">提交申诉</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -50,10 +50,10 @@ page { background: #f5f5f5; }
|
||||
.progress-text { font-size: 22rpx; color: #2e7d32; text-align: right; margin-top: 4rpx; }
|
||||
|
||||
.modal-foot { width: 100%; box-sizing: border-box; display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee; }
|
||||
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.pbtn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
.pbtn:active { opacity: 0.8; }
|
||||
|
||||
|
||||
/* ===== 获取更多按钮 ===== */
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
<text class="confirm-tip">确认后将从您的佣金/分红中扣除</text>
|
||||
</view>
|
||||
<view class="pay-foot">
|
||||
<view class="btn default" bindtap="backToMethod">取消</view>
|
||||
<view class="btn primary" bindtap="confirmBalance">确认</view>
|
||||
<view class="pbtn default" bindtap="backToMethod">取消</view>
|
||||
<view class="pbtn primary" bindtap="confirmBalance">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.loading-text { color: #fff; font-size: 26rpx; }
|
||||
|
||||
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.pbtn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
.pbtn:active { opacity: 0.8; }
|
||||
@@ -60,8 +60,8 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view wx:if="{{ xuanzhongChufa.sqzhuangtai === 0 }}" class="btn primary" bindtap="openShensuModal">申诉</view>
|
||||
<view class="btn default" bindtap="guanbiXiangqing">关闭</view>
|
||||
<view wx:if="{{ xuanzhongChufa.sqzhuangtai === 0 }}" class="pbtn primary" bindtap="openShensuModal">申诉</view>
|
||||
<view class="pbtn default" bindtap="guanbiXiangqing">关闭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -97,8 +97,8 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="btn default" bindtap="closeShensuModal">取消</view>
|
||||
<view class="btn primary" bindtap="submitShensu">提交申诉</view>
|
||||
<view class="pbtn default" bindtap="closeShensuModal">取消</view>
|
||||
<view class="pbtn primary" bindtap="submitShensu">提交申诉</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -59,10 +59,10 @@ page { background: #f5f5f5; }
|
||||
|
||||
/* 🔥 底部按钮区左右 padding 严格均等 */
|
||||
.modal-foot { width: 100%; box-sizing: border-box; display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee; }
|
||||
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.pbtn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
.pbtn:active { opacity: 0.8; }
|
||||
|
||||
|
||||
/* ===== 获取更多按钮 ===== */
|
||||
|
||||
+20
-20
@@ -25,38 +25,38 @@
|
||||
|
||||
<!-- 按钮区域(第一行:保存/刷新/生成海报) -->
|
||||
<view class="button-section">
|
||||
<view class="btn-group">
|
||||
<view class="btn save-btn" bindtap="saveToAlbum" wx:if="{{qrcodeUrl}}">
|
||||
<text class="btn-icon">📥</text>
|
||||
<text class="btn-text">保存二维码</text>
|
||||
<view class="pbtn-group">
|
||||
<view class="pbtn pbtn-save" bindtap="saveToAlbum" wx:if="{{qrcodeUrl}}">
|
||||
<text class="pbtn-icon">📥</text>
|
||||
<text class="pbtn-text">保存二维码</text>
|
||||
</view>
|
||||
<view class="btn refresh-btn" bindtap="refreshQRCode" wx:if="{{!isLoading}}">
|
||||
<text class="btn-icon">⟳</text>
|
||||
<text class="btn-text">{{qrcodeUrl ? '重新获取' : '获取二维码'}}</text>
|
||||
<view class="pbtn pbtn-refresh" bindtap="refreshQRCode" wx:if="{{!isLoading}}">
|
||||
<text class="pbtn-icon">⟳</text>
|
||||
<text class="pbtn-text">{{qrcodeUrl ? '重新获取' : '获取二维码'}}</text>
|
||||
</view>
|
||||
<view class="btn loading-btn" wx:else>
|
||||
<view class="pbtn pbtn-loading" wx:else>
|
||||
<view class="loading-spinner-small"></view>
|
||||
<text class="btn-text">生成中...</text>
|
||||
<text class="pbtn-text">生成中...</text>
|
||||
</view>
|
||||
<!-- 🔥【新增】生成我的专属海报按钮 -->
|
||||
<view class="btn poster-btn" bindtap="generateMyPoster" wx:if="{{qrcodeUrl && !showPosterCanvas}}">
|
||||
<text class="btn-icon">🎨</text>
|
||||
<text class="btn-text">生成我的专属海报</text>
|
||||
<!-- 生成我的专属海报按钮 -->
|
||||
<view class="pbtn pbtn-poster" bindtap="generateMyPoster" wx:if="{{qrcodeUrl && !showPosterCanvas}}">
|
||||
<text class="pbtn-icon">🎨</text>
|
||||
<text class="pbtn-text">生成我的专属海报</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 🔥【新增】专属海报展示区(当 showPosterCanvas 为 true 时显示) -->
|
||||
<!-- 专属海报展示区(当 showPosterCanvas 为 true 时显示) -->
|
||||
<view class="poster-section" wx:if="{{showPosterCanvas}}">
|
||||
<canvas canvas-id="posterCanvas" class="poster-canvas"></canvas>
|
||||
<view class="poster-actions">
|
||||
<view class="btn save-poster-btn" bindtap="savePoster">
|
||||
<text class="btn-icon">📥</text>
|
||||
<text class="btn-text">保存海报</text>
|
||||
<view class="pbtn pbtn-save-poster" bindtap="savePoster">
|
||||
<text class="pbtn-icon">📥</text>
|
||||
<text class="pbtn-text">保存海报</text>
|
||||
</view>
|
||||
<view class="btn back-btn" bindtap="hidePoster">
|
||||
<text class="btn-icon">↩️</text>
|
||||
<text class="btn-text">返回</text>
|
||||
<view class="pbtn pbtn-back" bindtap="hidePoster">
|
||||
<text class="pbtn-icon">↩️</text>
|
||||
<text class="pbtn-text">返回</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
+93
-125
@@ -1,57 +1,51 @@
|
||||
/* 机甲风格推广海报页面(含新增专属海报样式) */
|
||||
/* 推广海报页面 */
|
||||
|
||||
page {
|
||||
background: #0a0e17;
|
||||
min-height: 100vh;
|
||||
font-family: 'Avenir', 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 背景网格 */
|
||||
.grid-bg {
|
||||
/* 背景网格 */
|
||||
.grid-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 160, 255, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 160, 255, 0.05) 1px, transparent 1px);
|
||||
background-size: 50rpx 50rpx;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.page-container {
|
||||
.page-container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
min-height: 100vh;
|
||||
padding: 30rpx 25rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
/* 机械装饰线条 */
|
||||
.mech-line {
|
||||
/* 机械装饰线条 */
|
||||
.mech-line {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.line-h {
|
||||
top: 200rpx;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2rpx;
|
||||
}
|
||||
.line-h {
|
||||
top: 200rpx; left: 0;
|
||||
width: 100%; height: 2rpx;
|
||||
background: linear-gradient(90deg, transparent, #00a6ff, transparent);
|
||||
}
|
||||
.line-v {
|
||||
right: 50rpx;
|
||||
top: 100rpx;
|
||||
width: 2rpx;
|
||||
height: 300rpx;
|
||||
}
|
||||
.line-v {
|
||||
right: 50rpx; top: 100rpx;
|
||||
width: 2rpx; height: 300rpx;
|
||||
background: linear-gradient(180deg, transparent, #00a6ff, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
@@ -60,14 +54,14 @@ page {
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 标题区域 */
|
||||
.title-section {
|
||||
/* 标题区域 */
|
||||
.title-section {
|
||||
text-align: center;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
.title {
|
||||
}
|
||||
.title {
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
@@ -75,87 +69,80 @@ page {
|
||||
letter-spacing: 2rpx;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.subtitle {
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #9cf;
|
||||
opacity: 0.9;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 二维码区域(方形,无斜切) */
|
||||
.qrcode-section {
|
||||
/* 二维码区域 */
|
||||
.qrcode-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
.qrcode-wrapper {
|
||||
}
|
||||
.qrcode-wrapper {
|
||||
position: relative;
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
width: 400rpx; height: 400rpx;
|
||||
background: rgba(0, 20, 40, 0.8);
|
||||
border: 4rpx solid #00a6ff;
|
||||
/* 移除 clip-path,保持方形 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 80rpx rgba(0, 166, 255, 0.4);
|
||||
animation: qrcodePulse 2s infinite alternate;
|
||||
}
|
||||
.qrcode-img {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
}
|
||||
.qrcode-img {
|
||||
width: 90%; height: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.qrcode-glow {
|
||||
}
|
||||
.qrcode-glow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: radial-gradient(circle at 30% 30%, rgba(0, 166, 255, 0.2), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.empty-qrcode {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
}
|
||||
.empty-qrcode {
|
||||
width: 400rpx; height: 400rpx;
|
||||
background: rgba(0, 20, 40, 0.8);
|
||||
border: 4rpx dashed #00a6ff;
|
||||
/* 方形 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.empty-icon {
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
color: #00a6ff;
|
||||
margin-bottom: 20rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.empty-text {
|
||||
}
|
||||
.empty-text {
|
||||
font-size: 26rpx;
|
||||
color: #9cf;
|
||||
text-align: center;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮区域(机甲斜切风格) */
|
||||
.button-section {
|
||||
/* 按钮区域 */
|
||||
.button-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
.btn-group {
|
||||
}
|
||||
.pbtn-group {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.btn {
|
||||
}
|
||||
.pbtn {
|
||||
width: 220rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(0, 30, 60, 0.8);
|
||||
@@ -168,102 +155,83 @@ page {
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn:active {
|
||||
}
|
||||
.pbtn:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 0 40rpx #00a6ff;
|
||||
}
|
||||
.save-btn {
|
||||
border-color: #00a6ff;
|
||||
}
|
||||
.refresh-btn {
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
.refresh-btn .btn-icon {
|
||||
color: #ffaa00;
|
||||
}
|
||||
/* 🔥【新增】生成海报按钮样式 */
|
||||
.poster-btn {
|
||||
border-color: #ffaa00;
|
||||
}
|
||||
.poster-btn .btn-icon {
|
||||
color: #ffaa00;
|
||||
}
|
||||
.loading-btn {
|
||||
}
|
||||
.pbtn-save { border-color: #00a6ff; }
|
||||
.pbtn-refresh { border-color: #ffaa00; }
|
||||
.pbtn-refresh .pbtn-icon { color: #ffaa00; }
|
||||
.pbtn-poster { border-color: #ffaa00; }
|
||||
.pbtn-poster .pbtn-icon { color: #ffaa00; }
|
||||
.pbtn-loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.btn-icon {
|
||||
}
|
||||
.pbtn-icon {
|
||||
font-size: 36rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.btn-text {
|
||||
}
|
||||
.pbtn-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
}
|
||||
.loading-spinner-small {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
}
|
||||
.loading-spinner-small {
|
||||
width: 30rpx; height: 30rpx;
|
||||
border: 4rpx solid rgba(255,255,255,0.2);
|
||||
border-top: 4rpx solid #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 🔥【新增】专属海报展示区 */
|
||||
.poster-section {
|
||||
/* 专属海报展示区 */
|
||||
.poster-section {
|
||||
margin-top: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.poster-canvas {
|
||||
width: 600rpx;
|
||||
height: 900rpx;
|
||||
background: #1a1f2e; /* 占位色 */
|
||||
}
|
||||
.poster-canvas {
|
||||
width: 600rpx; height: 900rpx;
|
||||
background: #1a1f2e;
|
||||
border: 4rpx solid #00a6ff;
|
||||
box-shadow: 0 0 60rpx #00a6ff;
|
||||
/* 保持方形,无斜切 */
|
||||
}
|
||||
.poster-actions {
|
||||
}
|
||||
.poster-actions {
|
||||
display: flex;
|
||||
gap: 30rpx;
|
||||
margin-top: 30rpx;
|
||||
justify-content: center;
|
||||
}
|
||||
.save-poster-btn {
|
||||
}
|
||||
.pbtn-save-poster {
|
||||
background: linear-gradient(45deg, #0066cc, #00a6ff);
|
||||
border-color: #00a6ff;
|
||||
}
|
||||
.back-btn {
|
||||
}
|
||||
.pbtn-back {
|
||||
background: rgba(0, 30, 60, 0.8);
|
||||
border-color: #00a6ff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 使用说明(保留) */
|
||||
.instruction {
|
||||
/* 使用说明 */
|
||||
.instruction {
|
||||
margin-top: 30rpx;
|
||||
padding: 20rpx 40rpx;
|
||||
background: rgba(0, 30, 50, 0.5);
|
||||
border-left: 4rpx solid #00a6ff;
|
||||
clip-path: polygon(0 0, 100% 0, 98% 100%, 0 100%);
|
||||
}
|
||||
.instruction-text {
|
||||
}
|
||||
.instruction-text {
|
||||
font-size: 24rpx;
|
||||
color: #9cf;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes qrcodePulse {
|
||||
/* 动画 */
|
||||
@keyframes qrcodePulse {
|
||||
0% { box-shadow: 0 0 40rpx rgba(0, 166, 255, 0.3); }
|
||||
100% { box-shadow: 0 0 100rpx rgba(0, 166, 255, 0.6); }
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
}
|
||||
@@ -140,8 +140,7 @@ Page({
|
||||
that.handleLoadError(res.data?.msg || '加载失败')
|
||||
}
|
||||
},
|
||||
fail(error) {
|
||||
console.error('商品详情加载失败:', error)
|
||||
fail() {
|
||||
that.handleLoadError('网络错误,请重试')
|
||||
}
|
||||
})
|
||||
@@ -234,6 +233,10 @@ Page({
|
||||
this.loadShangpinDetail()
|
||||
},
|
||||
|
||||
goToKefu() {
|
||||
wx.navigateTo({ url: '/pages/cs-chat/cs-chat' })
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const { shangpinData, shangpinId } = this.data
|
||||
|
||||
|
||||
@@ -1,112 +1,123 @@
|
||||
<!--pages/product-detail/product-detail.wxml-->
|
||||
<view class="detail-page page-container">
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-container loading-mask loading-mask--light" wx:if="{{isLoading}}">
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner">
|
||||
<view class="spinner-ring"></view>
|
||||
<view class="spinner-dot"></view>
|
||||
</view>
|
||||
<view class="page">
|
||||
<!-- 加载 -->
|
||||
<view class="loading-box" wx:if="{{isLoading}}">
|
||||
<view class="spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<view class="error-container empty-state" wx:if="{{!isLoading && loadError}}">
|
||||
<image src="/images/error.png" class="error-icon" />
|
||||
<!-- 错误 -->
|
||||
<view class="error-box" wx:if="{{!isLoading && loadError}}">
|
||||
<text class="error-text">加载失败</text>
|
||||
<button class="error-btn btn btn-sm btn-sm--green" bindtap="onRefresh">重新加载</button>
|
||||
<view class="error-btn" bindtap="onRefresh">重新加载</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品详情内容 -->
|
||||
<view class="detail-content" wx:if="{{!isLoading && !loadError && shangpinData}}">
|
||||
<!-- 商品图片区域(占满屏幕) -->
|
||||
<view class="image-section">
|
||||
<view class="image-container" bindtap="previewImage">
|
||||
<image
|
||||
src="{{shangpinData.tupian}}"
|
||||
mode="aspectFill"
|
||||
class="product-image"
|
||||
/>
|
||||
<!-- 库存标签 -->
|
||||
<view class="kucun-tag {{shangpinData.kucunClass || 'kucun-normal'}}">
|
||||
<text class="kucun-text">{{shangpinData.kucunText || '库存充足'}}</text>
|
||||
<!-- 商品详情 -->
|
||||
<block wx:if="{{!isLoading && !loadError && shangpinData}}">
|
||||
<!-- 商品大图 -->
|
||||
<view class="hero" bindtap="previewImage">
|
||||
<image src="{{shangpinData.tupian}}" mode="aspectFill" class="hero-img" />
|
||||
<!-- 图片角标 -->
|
||||
<view class="hero-badge">
|
||||
<text class="hero-badge-text">{{shangpinData.kucun <= 0 ? '已售罄' : '现货'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格区 - 红底渐变 -->
|
||||
<view class="price-block">
|
||||
<view class="price-left">
|
||||
<text class="price-sym">¥</text>
|
||||
<text class="price-int">{{shangpinData.priceInteger || '0'}}</text>
|
||||
<text class="price-dec">.{{shangpinData.priceDecimal || '00'}}</text>
|
||||
</view>
|
||||
<view class="price-right">
|
||||
<text class="sold-text">已拼{{shangpinData.xiaoliangText || '0'}}件</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 标题区 -->
|
||||
<view class="title-block">
|
||||
<view class="title-tags">
|
||||
<text class="tag-hot">热卖</text>
|
||||
</view>
|
||||
<text class="title-text">{{shangpinData.biaoti || ''}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务保障条 -->
|
||||
<view class="service-strip">
|
||||
<view class="service-item">
|
||||
<text class="service-check">✓</text>
|
||||
<text class="service-label">极速接单</text>
|
||||
</view>
|
||||
<view class="service-item">
|
||||
<text class="service-check">✓</text>
|
||||
<text class="service-label">资金担保</text>
|
||||
</view>
|
||||
<view class="service-item">
|
||||
<text class="service-check">✓</text>
|
||||
<text class="service-label">售后保障</text>
|
||||
</view>
|
||||
<view class="service-item">
|
||||
<text class="service-check">✓</text>
|
||||
<text class="service-label">隐私保护</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择SKU入口 -->
|
||||
<view class="sku-trigger" bindtap="onOrderClick">
|
||||
<view class="sku-trigger-left">
|
||||
<text class="sku-label">选择</text>
|
||||
<text class="sku-value">服务规格、购买数量</text>
|
||||
</view>
|
||||
<text class="sku-arrow">></text>
|
||||
</view>
|
||||
|
||||
<!-- 商品介绍 -->
|
||||
<view class="detail-card" wx:if="{{shangpinData.xiangqing}}">
|
||||
<view class="detail-card-head">
|
||||
<text class="detail-card-title">商品介绍</text>
|
||||
</view>
|
||||
<text class="detail-card-body">{{shangpinData.xiangqing}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 购买须知 -->
|
||||
<view class="detail-card" wx:if="{{shangpinData.xiadanxuzhi}}">
|
||||
<view class="detail-card-head">
|
||||
<text class="detail-card-title">购买须知</text>
|
||||
</view>
|
||||
<text class="detail-card-body">{{shangpinData.xiadanxuzhi}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 规则图片 -->
|
||||
<view class="detail-card" wx:if="{{shangpinData.guize}}">
|
||||
<view class="detail-card-head">
|
||||
<text class="detail-card-title">服务规则</text>
|
||||
</view>
|
||||
<image src="{{shangpinData.guize}}" mode="widthFix" class="rule-img" binderror="onImageError" />
|
||||
</view>
|
||||
|
||||
<!-- 底部留白 -->
|
||||
<view class="bottom-space"></view>
|
||||
</block>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar" wx:if="{{!isLoading && !loadError && shangpinData}}">
|
||||
<view class="bottom-bar-inner">
|
||||
<!-- 客服 -->
|
||||
<view class="bar-icon-btn" bindtap="goToKefu">
|
||||
<text class="bar-icon-text">客服</text>
|
||||
</view>
|
||||
<!-- 单独购买 -->
|
||||
<view
|
||||
class="bar-buy-btn {{shangpinData.kucun <= 0 ? 'bar-buy-btn--disabled' : ''}}"
|
||||
bindtap="onOrderClick"
|
||||
hover-class="bar-buy-btn--hover"
|
||||
>
|
||||
<text class="bar-buy-text">{{shangpinData.kucun <= 0 ? '已售罄' : '立即下单'}}</text>
|
||||
<text class="bar-buy-price">¥{{shangpinData.priceInteger || '0'}}.{{shangpinData.priceDecimal || '00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 下方内容区域(顶部有圆角) -->
|
||||
<view class="content-area">
|
||||
<!-- 商品标题和价格区域 -->
|
||||
<view class="title-price-section">
|
||||
<view class="title-row">
|
||||
<text class="product-title">{{shangpinData.biaoti || ''}}</text>
|
||||
</view>
|
||||
|
||||
<view class="price-row">
|
||||
<!-- 价格 -->
|
||||
<view class="price-section">
|
||||
<text class="price-icon">¥</text>
|
||||
<text class="price-integer">{{shangpinData.priceInteger || '0'}}</text>
|
||||
<text class="price-decimal">.{{shangpinData.priceDecimal || '00'}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 销量 -->
|
||||
<view class="sales-section">
|
||||
<image src="/images/xiaoliang.png" class="sales-icon" />
|
||||
<text class="sales-text">{{shangpinData.xiaoliangText || '0'}}次购买</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品介绍区域 -->
|
||||
<view class="info-section" wx:if="{{shangpinData.xiangqing}}">
|
||||
<view class="section-header">
|
||||
<image src="/images/jieshao.png" class="section-icon" />
|
||||
<text class="section-title">商品介绍:</text>
|
||||
</view>
|
||||
<view class="section-content">
|
||||
<text class="content-text">{{shangpinData.xiangqing}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 购买须知区域 -->
|
||||
<view class="info-section" wx:if="{{shangpinData.xiadanxuzhi}}">
|
||||
<view class="section-header">
|
||||
<image src="/images/jieshao.png" class="section-icon" />
|
||||
<text class="section-title">购买须知:</text>
|
||||
</view>
|
||||
<view class="section-content">
|
||||
<text class="content-text">{{shangpinData.xiadanxuzhi}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 规则图片区域(只有有图片时才显示) -->
|
||||
<view class="rule-section" wx:if="{{shangpinData.guize}}">
|
||||
<image
|
||||
src="{{shangpinData.guize}}"
|
||||
mode="widthFix"
|
||||
class="rule-image"
|
||||
binderror="onImageError"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 占位区域(给底部按钮留空间) -->
|
||||
<view class="placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 立即下单按钮(固定在底部,无价格标签) -->
|
||||
<view class="order-btn-container fixed-bottom" wx:if="{{!isLoading && !loadError && shangpinData}}">
|
||||
<view class="order-btn btn btn-primary btn-primary--green" bindtap="onOrderClick">
|
||||
<!-- 按钮发光特效 -->
|
||||
<view class="btn-glow"></view>
|
||||
<!-- 按钮流光特效 -->
|
||||
<view class="btn-streamer"></view>
|
||||
<!-- 按钮文字 -->
|
||||
<text class="btn-text">立即预约</text>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
</view>
|
||||
@@ -1,349 +1,347 @@
|
||||
/* pages/product-detail/product-detail.wxss */
|
||||
.detail-page {
|
||||
width: 100%;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-container {
|
||||
position: absolute;
|
||||
background: #f5f5f5;
|
||||
z-index: 100;
|
||||
}
|
||||
page {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 140rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 加载 */
|
||||
.loading-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
justify-content: center;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
position: relative;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.spinner-ring {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 4rpx solid rgba(82, 196, 26, 0.2);
|
||||
.spinner {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border: 4rpx solid #f0f0f0;
|
||||
border-top-color: #e02e24;
|
||||
border-radius: 50%;
|
||||
}
|
||||
animation: spin 0.7s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.spinner-dot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: #52c41a;
|
||||
border-radius: 50%;
|
||||
transform: translateX(-50%);
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
/* 错误 */
|
||||
.error-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
/* 错误状态 */
|
||||
.error-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #f5f5f5;
|
||||
z-index: 100;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
opacity: 0.3;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
.error-text {
|
||||
font-size: 30rpx;
|
||||
color: #999;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.error-btn {
|
||||
font-weight: 500;
|
||||
padding: 20rpx 50rpx;
|
||||
border-radius: 50rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
.error-btn {
|
||||
padding: 16rpx 48rpx;
|
||||
background: #e02e24;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
/* 商品详情内容 */
|
||||
.detail-content {
|
||||
padding-bottom: 140rpx; /* 给底部按钮留出空间 */
|
||||
}
|
||||
|
||||
/* 商品图片区域(占满屏幕) */
|
||||
.image-section {
|
||||
width: 100%;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
width: 100%;
|
||||
height: 750rpx; /* 正方形,占满屏幕宽度 */
|
||||
/* 商品大图 */
|
||||
.hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
width: 100%;
|
||||
height: 750rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
.hero-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 库存标签 */
|
||||
.kucun-tag {
|
||||
.hero-badge {
|
||||
position: absolute;
|
||||
bottom: 30rpx;
|
||||
right: 30rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 30rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.kucun-normal {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.kucun-none {
|
||||
background: rgba(255, 77, 79, 0.8);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.kucun-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 下方内容区域(顶部有圆角,与图片重叠一部分) */
|
||||
.content-area {
|
||||
position: relative;
|
||||
background: white;
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
margin-top: -40rpx; /* 与图片重叠一部分,实现平滑过渡 */
|
||||
padding: 40rpx 30rpx 0;
|
||||
min-height: 400rpx;
|
||||
}
|
||||
|
||||
/* 标题和价格区域 */
|
||||
.title-price-section {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.title-row {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 价格和销量行 */
|
||||
.price-row {
|
||||
top: 24rpx;
|
||||
left: 24rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 6rpx;
|
||||
background: rgba(224, 46, 36, 0.85);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 价格部分 */
|
||||
.price-section {
|
||||
.hero-badge-text {
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 价格区 - 拼多多红底 */
|
||||
.price-block {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 28rpx;
|
||||
background: linear-gradient(135deg, #e02e24, #f05545);
|
||||
}
|
||||
|
||||
.price-icon {
|
||||
font-size: 34rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.price-integer {
|
||||
font-size: 58rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 700;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.price-decimal {
|
||||
font-size: 34rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 销量部分 */
|
||||
.sales-section {
|
||||
.price-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.sales-icon {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
margin-right: 12rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
.price-sym {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 信息区域(商品介绍、购买须知) */
|
||||
.info-section {
|
||||
margin-bottom: 40rpx;
|
||||
padding-top: 30rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.price-int {
|
||||
font-size: 60rpx;
|
||||
color: #fff;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
.price-dec {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.price-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.sold-text {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
/* 标题区 */
|
||||
.title-block {
|
||||
background: #fff;
|
||||
padding: 24rpx 28rpx 20rpx;
|
||||
}
|
||||
|
||||
.title-tags {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.tag-hot {
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
background: #e02e24;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 4rpx;
|
||||
font-weight: 600;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #52c41a; /* 绿色 */
|
||||
}
|
||||
color: #222;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
line-height: 1.8;
|
||||
padding-left: 52rpx; /* 与图标对齐 */
|
||||
}
|
||||
/* 服务保障条 */
|
||||
.service-strip {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0;
|
||||
padding: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 28rpx;
|
||||
.service-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 24rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.service-check {
|
||||
font-size: 20rpx;
|
||||
color: #e02e24;
|
||||
font-weight: 700;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.service-label {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 选择SKU入口 */
|
||||
.sku-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
padding: 24rpx 28rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.sku-trigger-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sku-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.sku-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 400;
|
||||
white-space: pre-line;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* 规则图片区域 */
|
||||
.rule-section {
|
||||
margin: 40rpx 0;
|
||||
padding-top: 30rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.sku-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #ccc;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.rule-image {
|
||||
width: 100%;
|
||||
border-radius: 16rpx;
|
||||
display: block;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
/* 详情卡片 */
|
||||
.detail-card {
|
||||
background: #fff;
|
||||
margin-bottom: 16rpx;
|
||||
padding: 28rpx;
|
||||
}
|
||||
|
||||
/* 占位区域 */
|
||||
.placeholder {
|
||||
height: 140rpx;
|
||||
}
|
||||
.detail-card-head {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
/* 立即下单按钮容器(固定底部) */
|
||||
.order-btn-container {
|
||||
background: linear-gradient(transparent, rgba(255, 255, 255, 0.9) 70%);
|
||||
}
|
||||
.detail-card-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
position: relative;
|
||||
padding-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 立即下单按钮 */
|
||||
.order-btn {
|
||||
border-radius: 50rpx;
|
||||
height: 100rpx;
|
||||
box-shadow:
|
||||
0 12rpx 36rpx rgba(82, 196, 26, 0.4),
|
||||
0 4rpx 12rpx rgba(82, 196, 26, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.order-btn:active {
|
||||
box-shadow:
|
||||
0 6rpx 24rpx rgba(82, 196, 26, 0.5),
|
||||
0 2rpx 8rpx rgba(82, 196, 26, 0.4);
|
||||
}
|
||||
|
||||
/* 按钮发光特效 */
|
||||
.btn-glow {
|
||||
.detail-card-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
top: 4rpx;
|
||||
width: 6rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 3rpx;
|
||||
background: #e02e24;
|
||||
}
|
||||
|
||||
.detail-card-body {
|
||||
font-size: 26rpx;
|
||||
color: #555;
|
||||
line-height: 1.8;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.rule-img {
|
||||
width: 100%;
|
||||
border-radius: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 底部留白 */
|
||||
.bottom-space {
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
animation: glowPulse 2s infinite;
|
||||
}
|
||||
z-index: 9999;
|
||||
background: #fff;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 按钮流光特效 */
|
||||
.btn-streamer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100rpx;
|
||||
.bottom-bar-inner {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.bar-icon-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 28rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.bar-icon-text {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.bar-buy-btn {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
transform: translateX(-100%);
|
||||
animation: streamerFlow 3s infinite;
|
||||
}
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #e02e24;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 按钮文字 */
|
||||
.btn-text {
|
||||
font-size: 36rpx;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.bar-buy-btn--disabled {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
/* 动画定义 */
|
||||
@keyframes glowPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.bar-buy-btn--hover {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
@keyframes streamerFlow {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
.bar-buy-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.bar-buy-price {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
+95
-310
@@ -1,31 +1,29 @@
|
||||
// pages/submit/submit.js
|
||||
const app = getApp()
|
||||
import request from '../../utils/request.js'
|
||||
// 引入弹窗服务(路径请根据项目实际调整)
|
||||
import PopupService from '../../services/popupService.js'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 全局变量
|
||||
ossImageUrl: '',
|
||||
apiBaseUrl: '',
|
||||
|
||||
// 从商品详情页传入的数据
|
||||
shangpinData: null,
|
||||
shangpinId: '',
|
||||
|
||||
// 表单数据
|
||||
nicheng: '', // 游戏昵称/ID
|
||||
beizhu: '', // 备注信息
|
||||
zhiding: '', // 指定打手ID(新增)
|
||||
jine: 0, // 总金额
|
||||
nicheng: '',
|
||||
beizhu: '',
|
||||
zhiding: '',
|
||||
quantity: 1,
|
||||
jine: 0,
|
||||
|
||||
totalPriceInteger: '0',
|
||||
totalPriceDecimal: '00',
|
||||
|
||||
// 页面状态
|
||||
isSubmitting: false,
|
||||
canSubmit: false,
|
||||
isPaying: false,
|
||||
|
||||
// 支付相关
|
||||
orderId: '',
|
||||
payParams: null,
|
||||
checkCount: 0,
|
||||
@@ -33,48 +31,38 @@ Page({
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
// 获取全局变量
|
||||
this.setData({
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
apiBaseUrl: app.globalData.apiBaseUrl || ''
|
||||
})
|
||||
|
||||
// 设置页面标题
|
||||
wx.setNavigationBarTitle({
|
||||
title: '提交订单'
|
||||
})
|
||||
wx.setNavigationBarTitle({ title: '提交订单' })
|
||||
|
||||
// 解析传递的商品数据
|
||||
try {
|
||||
if (options.data) {
|
||||
const shangpinData = JSON.parse(decodeURIComponent(options.data))
|
||||
|
||||
// 处理图片URL(拼接完整路径)
|
||||
if (shangpinData.image && !shangpinData.image.startsWith('http')) {
|
||||
shangpinData.image = this.data.ossImageUrl + shangpinData.image
|
||||
}
|
||||
|
||||
// 处理价格显示
|
||||
if (shangpinData.price) {
|
||||
const price = parseFloat(shangpinData.price) || 0
|
||||
const priceInteger = Math.floor(price)
|
||||
let priceDecimal = '00'
|
||||
|
||||
const decimalPart = (price - priceInteger).toFixed(2)
|
||||
if (decimalPart > 0) {
|
||||
priceDecimal = decimalPart.toString().split('.')[1]
|
||||
}
|
||||
|
||||
shangpinData.priceInteger = priceInteger
|
||||
shangpinData.priceDecimal = priceDecimal
|
||||
}
|
||||
|
||||
// 设置商品数据
|
||||
this.setData({
|
||||
shangpinData: shangpinData,
|
||||
shangpinId: shangpinData.id || '',
|
||||
jine: parseFloat(shangpinData.price) || 0,
|
||||
})
|
||||
}, () => this.updateTotalPrice())
|
||||
} else {
|
||||
this.handleDataError('商品数据不完整')
|
||||
}
|
||||
@@ -84,147 +72,101 @@ Page({
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 原有代码:注册通知组件
|
||||
this.registerNotificationComponent();
|
||||
|
||||
// 🆕 进入页面时触发弹窗检查(PopupService 会自动判断是否展示)
|
||||
PopupService.checkAndShow(this, 'tijiao');
|
||||
this.registerNotificationComponent()
|
||||
PopupService.checkAndShow(this, 'tijiao')
|
||||
},
|
||||
|
||||
// pages/submit/submit.js 中添加 onHide 方法(如果已有则合并内容)
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice')
|
||||
if (popupComp && popupComp.cleanup) popupComp.cleanup()
|
||||
},
|
||||
|
||||
// 新增:注册通知组件
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
|
||||
const app = getApp()
|
||||
const notificationComp = this.selectComponent('#global-notification')
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
//console.log('🏪 商城页面注册通知组件');
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理数据错误
|
||||
*/
|
||||
updateTotalPrice() {
|
||||
const total = this.data.jine * this.data.quantity
|
||||
const intPart = Math.floor(total)
|
||||
const decPart = (total - intPart).toFixed(2).split('.')[1]
|
||||
this.setData({
|
||||
totalPriceInteger: intPart,
|
||||
totalPriceDecimal: decPart
|
||||
})
|
||||
},
|
||||
|
||||
onQtyMinus() {
|
||||
if (this.data.quantity <= 1) return
|
||||
this.setData({ quantity: this.data.quantity - 1 }, () => this.updateTotalPrice())
|
||||
},
|
||||
|
||||
onQtyPlus() {
|
||||
if (this.data.quantity >= 99) return
|
||||
this.setData({ quantity: this.data.quantity + 1 }, () => this.updateTotalPrice())
|
||||
},
|
||||
|
||||
handleDataError(errorMsg) {
|
||||
wx.showToast({
|
||||
title: errorMsg || '数据错误',
|
||||
icon: 'none'
|
||||
})
|
||||
wx.showToast({ title: errorMsg || '数据错误', icon: 'none' })
|
||||
setTimeout(() => wx.navigateBack(), 1500)
|
||||
},
|
||||
|
||||
setTimeout(() => {
|
||||
goBack() {
|
||||
wx.navigateBack()
|
||||
}, 1500)
|
||||
},
|
||||
|
||||
/**
|
||||
* 游戏昵称输入事件
|
||||
*/
|
||||
onNicknameInput(e) {
|
||||
const value = e.detail.value.trim()
|
||||
this.setData({
|
||||
nicheng: value
|
||||
}, () => {
|
||||
this.checkFormValid()
|
||||
})
|
||||
this.setData({ nicheng: e.detail.value.trim() }, () => this.checkFormValid())
|
||||
},
|
||||
|
||||
/**
|
||||
* 备注信息输入事件
|
||||
*/
|
||||
onRemarkInput(e) {
|
||||
const value = e.detail.value.trim()
|
||||
this.setData({
|
||||
beizhu: value
|
||||
})
|
||||
this.setData({ beizhu: e.detail.value.trim() })
|
||||
},
|
||||
|
||||
/**
|
||||
* 指定打手ID输入事件
|
||||
*/
|
||||
onZhidingInput(e) {
|
||||
const value = e.detail.value.trim()
|
||||
this.setData({
|
||||
zhiding: value
|
||||
})
|
||||
this.setData({ zhiding: e.detail.value.trim() })
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查表单是否有效
|
||||
*/
|
||||
checkFormValid() {
|
||||
const { nicheng } = this.data
|
||||
|
||||
const isValid = nicheng && nicheng.length > 0 && nicheng.length <= 15
|
||||
|
||||
this.setData({
|
||||
canSubmit: isValid
|
||||
})
|
||||
this.setData({ canSubmit: nicheng && nicheng.length > 0 && nicheng.length <= 15 })
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交订单
|
||||
*/
|
||||
onSubmitOrder() {
|
||||
const { nicheng, beizhu, zhiding, jine, shangpinId, canSubmit, isSubmitting } = this.data
|
||||
const { nicheng, beizhu, zhiding, jine, quantity, shangpinId, canSubmit, isSubmitting } = this.data
|
||||
|
||||
// 验证表单
|
||||
if (!canSubmit) {
|
||||
wx.showToast({
|
||||
title: '请填写游戏昵称',
|
||||
icon: 'none'
|
||||
})
|
||||
wx.showToast({ title: '请填写游戏昵称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 防止重复提交
|
||||
if (isSubmitting) {
|
||||
return
|
||||
}
|
||||
if (isSubmitting) return
|
||||
|
||||
this.setData({
|
||||
isSubmitting: true,
|
||||
isPaying: false
|
||||
})
|
||||
this.setData({ isSubmitting: true, isPaying: false })
|
||||
|
||||
// 显示loading(超时设为10秒)
|
||||
wx.showLoading({
|
||||
title: '创建订单中...',
|
||||
mask: true
|
||||
})
|
||||
wx.showLoading({ title: '创建订单中...', mask: true })
|
||||
|
||||
const timeoutTimer = setTimeout(() => {
|
||||
wx.hideLoading()
|
||||
wx.showToast({
|
||||
title: '网络超时,请检查您是否已登录',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: '网络超时,请检查您是否已登录', icon: 'none' })
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
}, 10000)
|
||||
|
||||
// 构建请求数据(添加zhiding字段)
|
||||
const orderData = {
|
||||
shangpin_id: shangpinId, // 商品ID
|
||||
nicheng: nicheng, // 游戏昵称
|
||||
beizhu: beizhu || '', // 备注(可为空)
|
||||
zhiding: zhiding || '', // 指定打手ID(可为空)
|
||||
jine: jine // 金额
|
||||
shangpin_id: shangpinId,
|
||||
nicheng: nicheng,
|
||||
beizhu: beizhu || '',
|
||||
zhiding: zhiding || '',
|
||||
jine: jine * quantity,
|
||||
shuliang: quantity
|
||||
}
|
||||
|
||||
// 调用后端创建订单接口
|
||||
request({
|
||||
url: '/dingdan/xiadan',
|
||||
method: 'POST',
|
||||
@@ -236,12 +178,9 @@ onHide() {
|
||||
|
||||
if (res.statusCode === 200 && res.data) {
|
||||
const response = res.data
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
// 保存订单ID和支付参数
|
||||
const orderId = response.data.dingdanid
|
||||
const payParams = response.data.payParams || {}
|
||||
|
||||
this.setData({
|
||||
orderId: orderId,
|
||||
payParams: payParams,
|
||||
@@ -249,59 +188,29 @@ onHide() {
|
||||
isPaying: true,
|
||||
checkCount: 0
|
||||
})
|
||||
|
||||
// 调起微信支付
|
||||
this.triggerWxPayment(payParams)
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: response.msg || '创建订单失败',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: response.msg || '创建订单失败', icon: 'none' })
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
}
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: '网络错误,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: '网络错误,请重试', icon: 'none' })
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch(() => {
|
||||
clearTimeout(timeoutTimer)
|
||||
wx.hideLoading()
|
||||
wx.showToast({
|
||||
title: '创建订单失败',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: '创建订单失败', icon: 'none' })
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 调起微信支付
|
||||
*/
|
||||
triggerWxPayment(payParams) {
|
||||
// 验证支付参数是否完整
|
||||
if (!payParams || !payParams.timeStamp || !payParams.nonceStr ||
|
||||
!payParams.package || !payParams.signType || !payParams.paySign) {
|
||||
wx.showToast({
|
||||
title: '支付参数不完整',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: '支付参数不完整', icon: 'none' })
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -311,237 +220,113 @@ onHide() {
|
||||
package: payParams.package,
|
||||
signType: payParams.signType,
|
||||
paySign: payParams.paySign,
|
||||
|
||||
success: (res) => {
|
||||
// 支付成功,开始双重确认
|
||||
this.onPaymentSuccess()
|
||||
},
|
||||
|
||||
success: () => this.onPaymentSuccess(),
|
||||
fail: (err) => {
|
||||
// 根据错误类型处理
|
||||
if (err.errMsg === 'requestPayment:fail cancel') {
|
||||
// 用户取消支付
|
||||
this.handleUserCancel()
|
||||
} else {
|
||||
// 其他支付错误
|
||||
this.handlePaymentFail(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理用户取消支付
|
||||
*/
|
||||
handleUserCancel() {
|
||||
const { orderId } = this.data
|
||||
|
||||
wx.showToast({
|
||||
title: '已取消支付',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 通知后端用户取消(静默请求,不阻塞用户)
|
||||
wx.showToast({ title: '已取消支付', icon: 'none', duration: 1500 })
|
||||
if (orderId) {
|
||||
request({
|
||||
url: '/dingdan/shibai',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdanid: orderId,
|
||||
yuanyin: 'user_cancel'
|
||||
data: { dingdanid: orderId, yuanyin: 'user_cancel' }
|
||||
}).catch(() => {})
|
||||
}
|
||||
}).catch(() => {
|
||||
// 静默失败
|
||||
})
|
||||
}
|
||||
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理支付失败
|
||||
*/
|
||||
handlePaymentFail(err) {
|
||||
const { orderId } = this.data
|
||||
|
||||
wx.showToast({
|
||||
title: '支付失败',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 通知后端支付失败(简化流程,直接请求,不显示loading)
|
||||
wx.showToast({ title: '支付失败', icon: 'none', duration: 1500 })
|
||||
if (orderId) {
|
||||
request({
|
||||
url: '/dingdan/shibai',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdanid: orderId,
|
||||
yuanyin: 'payment_fail',
|
||||
cuowu: err.errMsg || ''
|
||||
}
|
||||
}).catch(() => {
|
||||
// 静默失败
|
||||
}).finally(() => {
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
data: { dingdanid: orderId, yuanyin: 'payment_fail', cuowu: err.errMsg || '' }
|
||||
}).catch(() => {}).finally(() => {
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
})
|
||||
} else {
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 支付成功处理(双重确认)
|
||||
*/
|
||||
onPaymentSuccess() {
|
||||
const { orderId } = this.data
|
||||
|
||||
if (!orderId) {
|
||||
wx.showToast({
|
||||
title: '订单ID不存在',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: '订单ID不存在', icon: 'none' })
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
return
|
||||
}
|
||||
|
||||
// 显示支付成功提示(时间缩短)
|
||||
wx.showToast({
|
||||
title: '支付成功,确认中...',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 开始双重确认流程
|
||||
wx.showToast({ title: '支付成功,确认中...', icon: 'none', duration: 1500 })
|
||||
this.checkOrderStatus()
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查订单状态(双重确认)
|
||||
*/
|
||||
checkOrderStatus() {
|
||||
const { orderId, checkCount, maxCheckCount } = this.data
|
||||
wx.showLoading({ title: `确认支付状态${checkCount > 0 ? `(${checkCount + 1})` : ''}`, mask: true })
|
||||
|
||||
// 显示加载(时间缩短)
|
||||
wx.showLoading({
|
||||
title: `确认支付状态${checkCount > 0 ? `(${checkCount + 1})` : ''}`,
|
||||
mask: true
|
||||
})
|
||||
|
||||
// 超时设为5秒
|
||||
const timeoutTimer = setTimeout(() => {
|
||||
wx.hideLoading()
|
||||
this.handleConfirmError('网络超时')
|
||||
}, 5000)
|
||||
|
||||
// 向后端发起支付复查请求
|
||||
request({
|
||||
url: '/dingdan/fucha',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdanid: orderId
|
||||
}
|
||||
data: { dingdanid: orderId }
|
||||
})
|
||||
.then(res => {
|
||||
clearTimeout(timeoutTimer)
|
||||
wx.hideLoading()
|
||||
|
||||
if (res.statusCode === 200 && res.data) {
|
||||
const response = res.data
|
||||
|
||||
if (response.code === 0) {
|
||||
// 确认成功,订单状态已更新
|
||||
if (res.data.code === 0) {
|
||||
this.handleConfirmSuccess()
|
||||
} else {
|
||||
// 后端返回错误
|
||||
this.handleConfirmError(response.msg || '订单状态异常')
|
||||
this.handleConfirmError(res.data.msg || '订单状态异常')
|
||||
}
|
||||
} else {
|
||||
// 网络错误
|
||||
this.handleConfirmError('网络错误')
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch(() => {
|
||||
clearTimeout(timeoutTimer)
|
||||
wx.hideLoading()
|
||||
this.handleConfirmError('检查失败')
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理确认成功
|
||||
*/
|
||||
handleConfirmSuccess() {
|
||||
// 显示最终成功提示
|
||||
wx.showToast({
|
||||
title: '购买成功!',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 1.5秒后跳转到首页
|
||||
setTimeout(() => {
|
||||
wx.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}, 1500)
|
||||
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
wx.showToast({ title: '购买成功!', icon: 'success', duration: 1500 })
|
||||
setTimeout(() => wx.switchTab({ url: '/pages/index/index' }), 1500)
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理确认错误
|
||||
*/
|
||||
handleConfirmError(errorMsg) {
|
||||
const { checkCount, maxCheckCount } = this.data
|
||||
const newCheckCount = checkCount + 1
|
||||
|
||||
if (newCheckCount < maxCheckCount) {
|
||||
// 还可以重试
|
||||
this.setData({
|
||||
checkCount: newCheckCount
|
||||
})
|
||||
|
||||
// 1秒后重试
|
||||
setTimeout(() => {
|
||||
this.checkOrderStatus()
|
||||
}, 1000)
|
||||
const newCheckCount = this.data.checkCount + 1
|
||||
if (newCheckCount < this.data.maxCheckCount) {
|
||||
this.setData({ checkCount: newCheckCount })
|
||||
setTimeout(() => this.checkOrderStatus(), 1000)
|
||||
} else {
|
||||
// 达到最大重试次数
|
||||
wx.showModal({
|
||||
title: '支付确认中',
|
||||
content: '支付已成功,但订单状态确认异常。请稍后在订单列表中查看状态,如有问题请联系客服。',
|
||||
showCancel: false,
|
||||
confirmText: '我知道了',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
if (res.confirm) wx.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
|
||||
this.setData({
|
||||
isSubmitting: false,
|
||||
isPaying: false
|
||||
})
|
||||
this.setData({ isSubmitting: false, isPaying: false })
|
||||
}
|
||||
}
|
||||
})
|
||||
+81
-73
@@ -1,124 +1,132 @@
|
||||
<!--pages/submit/submit.wxml-->
|
||||
<view class="submit-page">
|
||||
<!-- 商品信息区域 -->
|
||||
<view class="product-section">
|
||||
<view class="product-card">
|
||||
<!-- 商品图片 -->
|
||||
<view class="product-image-box">
|
||||
<image
|
||||
src="{{shangpinData && shangpinData.image ? shangpinData.image : ''}}"
|
||||
mode="aspectFill"
|
||||
class="product-image"
|
||||
/>
|
||||
<view class="page">
|
||||
<!-- 商品摘要 - 拼多多SKU弹窗顶部风格 -->
|
||||
<view class="sku-header">
|
||||
<image class="sku-thumb" src="{{shangpinData.image || ''}}" mode="aspectFill" />
|
||||
<view class="sku-header-info">
|
||||
<view class="sku-price-row">
|
||||
<text class="sku-price-sym">¥</text>
|
||||
<text class="sku-price-int">{{totalPriceInteger}}</text>
|
||||
<text class="sku-price-dec">.{{totalPriceDecimal}}</text>
|
||||
</view>
|
||||
<text class="sku-stock">库存{{shangpinData.kucun || 0}}件</text>
|
||||
<text class="sku-selected">已选:{{quantity}}份</text>
|
||||
</view>
|
||||
<view class="sku-close" bindtap="goBack">×</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="product-info">
|
||||
<view class="product-title">
|
||||
<text class="title-text">{{shangpinData && shangpinData.title ? shangpinData.title : '商品标题'}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 价格信息 -->
|
||||
<view class="price-info">
|
||||
<text class="price-label">价格:</text>
|
||||
<view class="price-value">
|
||||
<text class="price-icon">¥</text>
|
||||
<text class="price-integer">{{shangpinData && shangpinData.priceInteger ? shangpinData.priceInteger : '0'}}</text>
|
||||
<text class="price-decimal">.{{shangpinData && shangpinData.priceDecimal ? shangpinData.priceDecimal : '00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 购买数量 -->
|
||||
<view class="sku-section">
|
||||
<text class="sku-section-title">购买数量</text>
|
||||
<view class="qty-row">
|
||||
<view class="qty-btn {{quantity <= 1 ? 'qty-btn--disabled' : ''}}" bindtap="onQtyMinus">-</view>
|
||||
<text class="qty-val">{{quantity}}</text>
|
||||
<view class="qty-btn {{quantity >= 99 ? 'qty-btn--disabled' : ''}}" bindtap="onQtyPlus">+</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域(左右布局) -->
|
||||
<view class="form-section">
|
||||
<view class="form-card">
|
||||
<!-- 游戏昵称 - 左右布局 -->
|
||||
<view class="form-item-row">
|
||||
<view class="item-label-row">
|
||||
<text class="label-text-bold">昵称</text>
|
||||
<text class="label-required">*</text>
|
||||
<!-- 服务信息 -->
|
||||
<view class="sku-section">
|
||||
<text class="sku-section-title">服务信息</text>
|
||||
|
||||
<view class="form-field">
|
||||
<view class="field-top">
|
||||
<text class="field-name">游戏昵称</text>
|
||||
<text class="field-required">必填</text>
|
||||
</view>
|
||||
<input
|
||||
class="item-input-row"
|
||||
class="field-input"
|
||||
type="text"
|
||||
placeholder="请输入您的昵称"
|
||||
placeholder-class="input-placeholder-gray"
|
||||
placeholder="请输入您的游戏昵称"
|
||||
placeholder-class="ph"
|
||||
maxlength="15"
|
||||
value="{{nicheng}}"
|
||||
bindinput="onNicknameInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 备注信息 - 左右布局 -->
|
||||
<view class="form-item-row">
|
||||
<view class="item-label-row">
|
||||
<text class="label-text-bold">备注</text>
|
||||
<view class="form-field">
|
||||
<view class="field-top">
|
||||
<text class="field-name">备注</text>
|
||||
<text class="field-optional">选填</text>
|
||||
</view>
|
||||
<input
|
||||
class="item-input-row"
|
||||
class="field-input"
|
||||
type="text"
|
||||
placeholder="请补充您要补充的其他联系信息"
|
||||
placeholder-class="input-placeholder-gray"
|
||||
placeholder="补充其他联系信息"
|
||||
placeholder-class="ph"
|
||||
maxlength="30"
|
||||
value="{{beizhu}}"
|
||||
bindinput="onRemarkInput"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 指定打手ID - 左右布局 -->
|
||||
<view class="form-item-row">
|
||||
<view class="item-label-row">
|
||||
<text class="label-text-bold">指定服务者ID(选填)</text>
|
||||
<view class="form-field">
|
||||
<view class="field-top">
|
||||
<text class="field-name">指定服务者</text>
|
||||
<text class="field-optional">选填</text>
|
||||
</view>
|
||||
<input
|
||||
class="item-input-row"
|
||||
class="field-input"
|
||||
type="text"
|
||||
placeholder="填写您需要指定的服务者"
|
||||
placeholder-class="input-placeholder-gray"
|
||||
placeholder="填写指定服务者ID"
|
||||
placeholder-class="ph"
|
||||
maxlength="20"
|
||||
value="{{zhiding}}"
|
||||
bindinput="onZhidingInput"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<view class="payment-item">
|
||||
<view class="payment-method">
|
||||
<image src="/images/wechat-pay.png" class="payment-icon" />
|
||||
<text class="payment-text">微信支付</text>
|
||||
<view class="payment-checkbox">
|
||||
<text class="checkbox-text">✓</text>
|
||||
<!-- 服务保障 -->
|
||||
<view class="sku-section">
|
||||
<text class="sku-section-title">服务保障</text>
|
||||
<view class="guarantee-row">
|
||||
<view class="guarantee-item">
|
||||
<text class="g-check">✓</text>
|
||||
<text class="g-text">极速接单</text>
|
||||
</view>
|
||||
<view class="guarantee-item">
|
||||
<text class="g-check">✓</text>
|
||||
<text class="g-text">资金担保</text>
|
||||
</view>
|
||||
<view class="guarantee-item">
|
||||
<text class="g-check">✓</text>
|
||||
<text class="g-text">售后保障</text>
|
||||
</view>
|
||||
<view class="guarantee-item">
|
||||
<text class="g-check">✓</text>
|
||||
<text class="g-text">隐私保护</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="footer-section">
|
||||
<view class="total-price">
|
||||
<text class="total-label">总价:</text>
|
||||
<view class="total-value">
|
||||
<text class="price-icon">¥</text>
|
||||
<text class="price-integer">{{shangpinData && shangpinData.priceInteger ? shangpinData.priceInteger : '0'}}</text>
|
||||
<text class="price-decimal">.{{shangpinData && shangpinData.priceDecimal ? shangpinData.priceDecimal : '00'}}</text>
|
||||
<!-- 支付方式 -->
|
||||
<view class="sku-section">
|
||||
<text class="sku-section-title">支付方式</text>
|
||||
<view class="pay-row">
|
||||
<image src="/images/wechat-pay.png" class="pay-icon" />
|
||||
<text class="pay-label">微信支付</text>
|
||||
<view class="pay-check">
|
||||
<text class="pay-check-icon">✓</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部留白 -->
|
||||
<view class="bottom-space"></view>
|
||||
|
||||
<!-- 底部确认按钮 -->
|
||||
<view class="confirm-bar">
|
||||
<view
|
||||
class="submit-btn {{canSubmit && !isSubmitting ? 'submit-btn-active' : 'submit-btn-disabled'}}"
|
||||
class="confirm-btn {{canSubmit && !isSubmitting ? 'confirm-btn--active' : 'confirm-btn--disabled'}}"
|
||||
bindtap="onSubmitOrder"
|
||||
hover-class="confirm-btn--hover"
|
||||
>
|
||||
<text class="btn-text">{{isSubmitting ? '提交中...' : '提交订单'}}</text>
|
||||
<view class="btn-glow"></view>
|
||||
{{isSubmitting ? '提交中...' : '确定'}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 全局通知组件 -->
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
<!-- 弹窗公告组件(供 PopupService 调用) -->
|
||||
<popup-notice id="popupNotice" />
|
||||
</view>
|
||||
+249
-233
@@ -1,280 +1,296 @@
|
||||
/* pages/submit/submit.wxss */
|
||||
|
||||
.submit-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
page {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f3ff 100%);
|
||||
padding-bottom: 140rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
/* 商品信息区域 */
|
||||
.product-section {
|
||||
padding: 30rpx 30rpx 20rpx 30rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
/* SKU头部 - 拼多多弹窗风格 */
|
||||
.sku-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
position: relative;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.product-image-box {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
background: #f5f5f5;
|
||||
.sku-thumb {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
margin-right: 20rpx;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
.sku-header-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 160rpx;
|
||||
}
|
||||
padding: 4rpx 0;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.price-info {
|
||||
.sku-price-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.price-label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.price-icon {
|
||||
.sku-price-sym {
|
||||
font-size: 28rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.price-integer {
|
||||
font-size: 38rpx;
|
||||
color: #ff4d4f;
|
||||
color: #e02e24;
|
||||
font-weight: 700;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.price-decimal {
|
||||
.sku-price-int {
|
||||
font-size: 48rpx;
|
||||
color: #e02e24;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
|
||||
.sku-price-dec {
|
||||
font-size: 28rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 600;
|
||||
}
|
||||
color: #e02e24;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
padding: 0 30rpx 20rpx 30rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.form-item-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 25rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.form-item-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-label-row {
|
||||
width: 220rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label-text-bold {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.label-required {
|
||||
font-size: 30rpx;
|
||||
color: #ff4d4f;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.item-input-row {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
padding: 0 20rpx;
|
||||
text-align: right;
|
||||
background: #fafafa;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.input-placeholder-gray {
|
||||
.sku-stock {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 支付方式 */
|
||||
.payment-item {
|
||||
margin-top: 30rpx;
|
||||
padding-top: 30rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.sku-selected {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.payment-method {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.payment-icon {
|
||||
.sku-close {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #999;
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.payment-text {
|
||||
/* SKU区块 */
|
||||
.sku-section {
|
||||
background: #fff;
|
||||
padding: 24rpx 28rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.sku-section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 数量选择器 */
|
||||
.qty-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.qty-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
border: 1rpx solid #e8e8e8;
|
||||
}
|
||||
|
||||
.qty-btn--disabled {
|
||||
color: #ccc;
|
||||
border-color: #f0f0f0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.qty-val {
|
||||
min-width: 80rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
/* 表单字段 */
|
||||
.form-field {
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.form-field:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.field-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.field-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.field-required {
|
||||
font-size: 20rpx;
|
||||
color: #e02e24;
|
||||
background: #fff1f0;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 4rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.field-optional {
|
||||
font-size: 20rpx;
|
||||
color: #bbb;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 100%;
|
||||
height: 72rpx;
|
||||
background: #f7f7f7;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
border: 1rpx solid #eee;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ph {
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 服务保障 */
|
||||
.guarantee-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx 20rpx;
|
||||
}
|
||||
|
||||
.guarantee-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.g-check {
|
||||
font-size: 20rpx;
|
||||
color: #e02e24;
|
||||
font-weight: 700;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.g-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 支付方式 */
|
||||
.pay-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pay-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.pay-label {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-checkbox {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
.pay-check {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
background: #52c41a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-text {
|
||||
color: white;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.pay-check-icon {
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.footer-section {
|
||||
/* 底部留白 */
|
||||
.bottom-space {
|
||||
height: 20rpx;
|
||||
}
|
||||
|
||||
/* 底部确认按钮 */
|
||||
.confirm-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
height: 100rpx;
|
||||
background: #fff;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background: white;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
margin-top: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.total-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.total-label {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.total-value {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 240rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.submit-btn-active {
|
||||
background: linear-gradient(135deg, #52c41a, #389e0d);
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 77, 79, 0.4);
|
||||
}
|
||||
|
||||
.submit-btn-disabled {
|
||||
background: #ccc;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.submit-btn-active:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 20rpx rgba(255, 77, 79, 0.5);
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 32rpx;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
.btn-glow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transform: translateX(-100%);
|
||||
animation: btnGlow 3s infinite;
|
||||
}
|
||||
.confirm-btn--active {
|
||||
background: #e02e24;
|
||||
}
|
||||
|
||||
@keyframes btnGlow {
|
||||
0% { transform: translateX(-100%); }
|
||||
50% { transform: translateX(100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
.confirm-btn--disabled {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.confirm-btn--hover {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
+3
-13
@@ -1,5 +1,5 @@
|
||||
// pages/verify/verify.js
|
||||
import request from '../../utils/request.js'
|
||||
import { createPage, request } from '../../utils/base-page.js'
|
||||
import { enterLockedRole } from '../../utils/primary-role.js'
|
||||
|
||||
const API_MAP = {
|
||||
@@ -26,7 +26,7 @@ const NAME_MAP = {
|
||||
kaoheguan: '考核官'
|
||||
}
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
authType: '',
|
||||
authName: '',
|
||||
@@ -156,14 +156,4 @@ Page({
|
||||
wx.showToast({ title: err.message || '注册失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
goToKefu() {
|
||||
const app = getApp()
|
||||
const { link, enterpriseId } = app.globalData.kefuConfig || {}
|
||||
if (!link || !enterpriseId) {
|
||||
wx.showToast({ title: '客服配置未加载', icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.openCustomerServiceChat({ extInfo: { url: link }, corpId: enterpriseId })
|
||||
}
|
||||
})
|
||||
}))
|
||||
@@ -1,10 +1,10 @@
|
||||
// pages/withdraw/withdraw.js
|
||||
import request from '../../utils/request.js';
|
||||
import { createPage, request } from '../../utils/base-page.js';
|
||||
import PopupService from '../../services/popupService.js';
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
Page(createPage({
|
||||
data: {
|
||||
pageMode: 0,
|
||||
pageOptions: {},
|
||||
@@ -23,24 +23,6 @@ Page({
|
||||
PopupService.checkAndShow(this, 'tixian');
|
||||
},
|
||||
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
},
|
||||
|
||||
// 注册通知组件
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification');
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
async fetchPageMode() {
|
||||
try {
|
||||
const res = await request({
|
||||
@@ -69,4 +51,4 @@ Page({
|
||||
},
|
||||
|
||||
stopPropagation() {}
|
||||
});
|
||||
}))
|
||||
+8
-7
@@ -34,6 +34,7 @@
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.8);
|
||||
padding: 0 20rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab-capsule::before {
|
||||
@@ -46,19 +47,19 @@
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.25) 20%,
|
||||
rgba(255, 255, 255, 0.35) 50%,
|
||||
rgba(255, 255, 255, 0.25) 80%,
|
||||
rgba(255, 255, 255, 0.15) 20%,
|
||||
rgba(255, 255, 255, 0.22) 50%,
|
||||
rgba(255, 255, 255, 0.15) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
animation: shine 3s infinite;
|
||||
animation: shine 8s infinite;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
25% { left: 150%; }
|
||||
100% { left: 150%; }
|
||||
0% { left: -60%; }
|
||||
15% { left: 100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// utils/api-helper.js - API 响应解析
|
||||
|
||||
export function isApiSuccess(body) {
|
||||
if (!body) return false;
|
||||
const code = Number(body.code);
|
||||
return code === 200 || code === 0;
|
||||
}
|
||||
|
||||
export function getApiMsg(body) {
|
||||
if (!body) return '';
|
||||
return body.msg || body.message || '';
|
||||
}
|
||||
|
||||
export function extractUserData(body) {
|
||||
if (!body) return {};
|
||||
if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) {
|
||||
return body.data;
|
||||
}
|
||||
const userData = { ...body };
|
||||
delete userData.code;
|
||||
delete userData.msg;
|
||||
delete userData.message;
|
||||
return userData;
|
||||
}
|
||||
|
||||
/** 解析扫码场景参数 */
|
||||
export function parseSceneOptions(options) {
|
||||
if (!options || !options.scene) return options || {};
|
||||
try {
|
||||
const scene = decodeURIComponent(options.scene);
|
||||
return { ...options, inviteCode: scene };
|
||||
} catch (e) {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/** 订单状态中文映射 */
|
||||
const ORDER_STATUS_MAP = {
|
||||
1: '已下单', 2: '进行中', 3: '已完成',
|
||||
4: '退款中', 5: '已退款', 6: '退款失败',
|
||||
7: '指定中', 8: '结算中',
|
||||
};
|
||||
|
||||
export function getOrderStatusText(status) {
|
||||
return ORDER_STATUS_MAP[status] || '未知状态';
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
// utils/base-page.js - 页面公共框架
|
||||
import request from './request.js';
|
||||
import popupService from '../services/popupService.js';
|
||||
import {
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
syncGroupFields,
|
||||
ensureRoleOnCenterPage,
|
||||
clearCacheAndEnterNormal,
|
||||
refreshTabBar,
|
||||
} from './role-tab-bar.js';
|
||||
import { lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole } from './primary-role.js';
|
||||
import { resolveAvatarFromStorage } from './avatar.js';
|
||||
import { isApiSuccess, getApiMsg, extractUserData, parseSceneOptions } from './api-helper.js';
|
||||
|
||||
// ===== 基类数据 =====
|
||||
const BASE_DATA = {
|
||||
isLoading: false,
|
||||
lastRefreshTime: 0,
|
||||
canRefresh: true,
|
||||
};
|
||||
|
||||
// ===== 基类方法 =====
|
||||
const BASE_METHODS = {
|
||||
|
||||
// ---------- 通知组件 ----------
|
||||
registerNotificationComponent() {
|
||||
const app = getApp();
|
||||
const comp = this.selectComponent('#global-notification');
|
||||
if (comp && comp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => comp.showNotification(data),
|
||||
hide: () => comp.hideNotification(),
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 弹窗清理 ----------
|
||||
cleanupPopup() {
|
||||
const comp = this.selectComponent('#popupNotice');
|
||||
if (comp && comp.cleanup) comp.cleanup();
|
||||
},
|
||||
|
||||
// ---------- 冷启动弹窗 ----------
|
||||
checkColdStartPopup(pageId) {
|
||||
const app = getApp();
|
||||
if (!app.globalData.hasShownPopupOnColdStart) {
|
||||
app.globalData.hasShownPopupOnColdStart = true;
|
||||
setTimeout(() => popupService.checkAndShow(this, pageId), 300);
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 头像加载 ----------
|
||||
loadAvatar() {
|
||||
this.setData({ avatarUrl: resolveAvatarFromStorage(getApp()) });
|
||||
},
|
||||
|
||||
// ---------- 通用用户信息加载 ----------
|
||||
loadUserInfo() {
|
||||
const app = getApp();
|
||||
const { nick, uid, avatarUrl } = (app.getUserProfileForDisplay && app.getUserProfileForDisplay()) || {};
|
||||
this.setData({
|
||||
avatarUrl: avatarUrl || resolveAvatarFromStorage(app),
|
||||
uid: uid || wx.getStorageSync('uid') || '',
|
||||
nicheng: nick || wx.getStorageSync('nicheng') || '',
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 通用数据加载 ----------
|
||||
async loadData(opts) {
|
||||
const {
|
||||
url, method = 'POST', data: reqData,
|
||||
fields, loadingKey = 'isLoading',
|
||||
successCode = 200, errorMsg = '加载失败',
|
||||
onSuccess, onError,
|
||||
} = opts;
|
||||
|
||||
this.setData({ [loadingKey]: true });
|
||||
try {
|
||||
const res = await request({ url, method, data: reqData });
|
||||
if (res && (res.data.code === successCode || (successCode === 0 && res.data.code === 0))) {
|
||||
const resData = res.data.data || {};
|
||||
if (fields) {
|
||||
const updateData = {};
|
||||
Object.keys(fields).forEach((pageKey) => {
|
||||
const apiField = fields[pageKey];
|
||||
updateData[pageKey] = apiField in resData ? resData[apiField] : (typeof apiField === 'object' ? apiField.default : '');
|
||||
});
|
||||
this.setData({ ...updateData, [loadingKey]: false });
|
||||
}
|
||||
if (onSuccess) onSuccess(resData, res);
|
||||
return resData;
|
||||
}
|
||||
throw new Error(res?.data?.msg || res?.data?.message || errorMsg);
|
||||
} catch (error) {
|
||||
this.setData({ [loadingKey]: false });
|
||||
if (onError) {
|
||||
onError(error);
|
||||
} else {
|
||||
wx.showToast({ title: error.message || errorMsg, icon: 'none' });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 节流刷新 ----------
|
||||
throttledRefresh(fn, interval = 3000) {
|
||||
const now = Date.now();
|
||||
if (!this.data.canRefresh || (now - this.data.lastRefreshTime < interval)) {
|
||||
wx.showToast({ title: '操作过快', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ lastRefreshTime: now, canRefresh: false });
|
||||
fn.call(this);
|
||||
setTimeout(() => this.setData({ canRefresh: true }), interval);
|
||||
},
|
||||
|
||||
// ---------- 复制 UID ----------
|
||||
copyUid() {
|
||||
const uid = this.data.uid;
|
||||
if (!uid) {
|
||||
wx.showToast({ title: 'UID不存在', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.setClipboardData({
|
||||
data: uid,
|
||||
success: () => wx.showToast({ title: '已复制UID', icon: 'success' }),
|
||||
fail: () => wx.showToast({ title: '复制失败', icon: 'none' }),
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 清除缓存 ----------
|
||||
clearCache() {
|
||||
wx.showModal({
|
||||
title: '清除缓存',
|
||||
content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (r) => { if (r.confirm) clearCacheAndEnterNormal(this); },
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 返回点单端 ----------
|
||||
switchToNormal() {
|
||||
lockPrimaryRole('normal');
|
||||
wx.reLaunch({ url: '/pages/mine/mine' });
|
||||
},
|
||||
|
||||
// ---------- 客服 ----------
|
||||
goToKefu() {
|
||||
const app = getApp();
|
||||
const { link, enterpriseId } = app.globalData.kefuConfig || {};
|
||||
if (!link || !enterpriseId) {
|
||||
wx.showToast({ title: '客服配置未加载', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.openCustomerServiceChat({ extInfo: { url: link }, corpId: enterpriseId });
|
||||
},
|
||||
|
||||
// ---------- API 辅助(快捷访问) ----------
|
||||
_isApiSuccess: isApiSuccess,
|
||||
_getApiMsg: getApiMsg,
|
||||
_extractUserData: extractUserData,
|
||||
|
||||
// ---------- 邀请码注册通用流程 ----------
|
||||
async registerWithInviteCode(opts) {
|
||||
const {
|
||||
inviteCode, apiUrl, role, statusKey,
|
||||
alreadyMsg, successMsg = '注册成功',
|
||||
onSuccess,
|
||||
} = opts;
|
||||
|
||||
if (!inviteCode) {
|
||||
wx.showToast({ title: '请输入邀请码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (inviteCode.length > 100) {
|
||||
wx.showToast({ title: '邀请码不能超过100字符', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
const res = await request({ url: apiUrl, method: 'POST', data: { inviteCode } });
|
||||
const body = res?.data || {};
|
||||
|
||||
if (isApiSuccess(body)) {
|
||||
const userData = extractUserData(body);
|
||||
syncRoleStatuses(userData);
|
||||
syncProfileFields(userData);
|
||||
syncGroupFields(userData);
|
||||
if (userData.token) {
|
||||
wx.setStorageSync('token', userData.token);
|
||||
getApp().globalData.token = userData.token;
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(userData, statusKey) &&
|
||||
!isRoleStatusActive(wx.getStorageSync(statusKey))) {
|
||||
wx.setStorageSync(statusKey, 1);
|
||||
getApp().globalData[statusKey] = 1;
|
||||
}
|
||||
if (onSuccess) {
|
||||
await onSuccess(userData, body);
|
||||
}
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: successMsg, icon: 'success' });
|
||||
} else {
|
||||
const msg = getApiMsg(body);
|
||||
if (alreadyMsg && msg && msg.includes(alreadyMsg)) {
|
||||
if (onSuccess) {
|
||||
await onSuccess(extractUserData(body), body);
|
||||
}
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: msg, icon: 'success' });
|
||||
} else {
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: msg || '注册失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.setData({ isLoading: false });
|
||||
wx.showToast({ title: error.message || '注册失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 邀请码 + 登录检查 ----------
|
||||
handleInviteCodeWithLoginCheck(opts) {
|
||||
const { inviteCode, role, statusKey, alreadyMsg, onNeedLogin, onAlreadyRegistered } = opts;
|
||||
const token = wx.getStorageSync('token');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
|
||||
if (!token || !uid) {
|
||||
if (onNeedLogin) onNeedLogin(inviteCode);
|
||||
return;
|
||||
}
|
||||
|
||||
const isActive = isRoleStatusActive(wx.getStorageSync(statusKey));
|
||||
if (!isActive) {
|
||||
wx.showLoading({ title: '注册中...', mask: true });
|
||||
this.registerWithInviteCode(opts);
|
||||
} else {
|
||||
if (onAlreadyRegistered) onAlreadyRegistered();
|
||||
else wx.showToast({ title: alreadyMsg || '您已注册', icon: 'success' });
|
||||
}
|
||||
},
|
||||
|
||||
// ---------- 微信登录 + 注册 ----------
|
||||
loginAndRegisterWithInviteCode(opts) {
|
||||
const { inviteCode, onLoginSuccess } = opts;
|
||||
wx.showLoading({ title: '登录注册中...', mask: true });
|
||||
wx.login({
|
||||
success: (loginRes) => {
|
||||
if (loginRes.code) {
|
||||
if (onLoginSuccess) onLoginSuccess(loginRes.code, inviteCode);
|
||||
} else {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '微信登录失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '微信登录失败', icon: 'none' });
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// ---------- 图片 URL 初始化 ----------
|
||||
initImageUrls(urlMap) {
|
||||
const ossImageUrl = getApp().globalData.ossImageUrl || '';
|
||||
const result = {};
|
||||
Object.keys(urlMap).forEach((key) => {
|
||||
const path = urlMap[key];
|
||||
result[key] = path.startsWith('http') || path.startsWith('/') ? path : ossImageUrl + path;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
// ===== 角色中心页增强 =====
|
||||
function createRoleCenterMixin(roleConfig) {
|
||||
const { role, statusKey, dataFlag, pageId, apiUrl } = roleConfig;
|
||||
|
||||
return {
|
||||
onReady() {
|
||||
if (isCenterPageActive(this, dataFlag, statusKey)) {
|
||||
ensureRoleOnCenterPage(this, role);
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.cleanupPopup();
|
||||
},
|
||||
|
||||
checkRoleStatus() {
|
||||
try {
|
||||
const status = wx.getStorageSync(statusKey);
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (isRoleStatusActive(status)) {
|
||||
this.setData({ [dataFlag]: true, uid: uid || '' });
|
||||
this.loadAvatar();
|
||||
if (this.checkGlobalData) this.checkGlobalData();
|
||||
ensureRoleOnCenterPage(this, role);
|
||||
} else {
|
||||
this.setData({ [dataFlag]: false });
|
||||
}
|
||||
} catch (error) {
|
||||
this.setData({ [dataFlag]: false });
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建页面配置(合并基类方法)
|
||||
* @param {Object} pageConfig - 页面特有配置
|
||||
* @param {Object} [options] - 可选配置
|
||||
* @param {Object} [options.roleConfig] - 角色中心页配置 { role, statusKey, dataFlag, pageId, apiUrl }
|
||||
* @returns {Object} 完整的 Page 配置
|
||||
*/
|
||||
export function createPage(pageConfig, options = {}) {
|
||||
const { roleConfig } = options;
|
||||
|
||||
// 合并 data
|
||||
const data = { ...BASE_DATA, ...(pageConfig.data || {}) };
|
||||
|
||||
// 合并方法
|
||||
const methods = { ...BASE_METHODS };
|
||||
|
||||
// 如果是角色中心页,混入角色相关方法
|
||||
if (roleConfig) {
|
||||
Object.assign(methods, createRoleCenterMixin(roleConfig));
|
||||
}
|
||||
|
||||
// 页面特有方法覆盖基类方法
|
||||
const allMethods = { ...methods, ...pageConfig };
|
||||
|
||||
// 确保 data 正确
|
||||
allMethods.data = data;
|
||||
|
||||
return allMethods;
|
||||
}
|
||||
|
||||
// 导出工具函数供页面直接使用
|
||||
export {
|
||||
request,
|
||||
isRoleStatusActive,
|
||||
isCenterPageActive,
|
||||
syncRoleStatuses,
|
||||
syncProfileFields,
|
||||
syncGroupFields,
|
||||
ensureRoleOnCenterPage,
|
||||
clearCacheAndEnterNormal,
|
||||
refreshTabBar,
|
||||
lockPrimaryRole,
|
||||
migrateLegacyCenterRole,
|
||||
enterLockedRole,
|
||||
isApiSuccess,
|
||||
getApiMsg,
|
||||
extractUserData,
|
||||
parseSceneOptions,
|
||||
};
|
||||
Reference in new issue
Block a user