统一排行榜对齐星雀UI,页面资源后台配置实时刷新
Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -310,6 +310,7 @@ App({
|
||||
enterpriseId: config.kefu.enterpriseId || ''
|
||||
};
|
||||
}
|
||||
this.emitEvent('configReady', config);
|
||||
},
|
||||
|
||||
initGoEasyWithConfig() {
|
||||
|
||||
107
miniprogram/app.json
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"pages": [
|
||||
"pages/index/index",
|
||||
"pages/fenlei/fenlei",
|
||||
"pages/xuanren/xuanren",
|
||||
"pages/xiaoxi/xiaoxi",
|
||||
"pages/wode/wode",
|
||||
"pages/shangpinxiangqing/shangpinxiangqing",
|
||||
"pages/tijiao/tijiao",
|
||||
"pages/xiugai/xiugai",
|
||||
"pages/dashouduan/dashouduan",
|
||||
"pages/shangjiaduan/shangjiaduan",
|
||||
"pages/guanshiduan/guanshiduan",
|
||||
"pages/dingdan/dingdan",
|
||||
"pages/dingdanxiangqing/dingdanxiangqing",
|
||||
"pages/dashouguize/dashouguize",
|
||||
"pages/dashouxiugai/dashouxiugai",
|
||||
"pages/dashoupaihang/dashoupaihang",
|
||||
"pages/dashouchongzhi/dashouchongzhi",
|
||||
"pages/dashouxiaoxi/dashouxiaoxi",
|
||||
"pages/dashoudingdan/dashoudingdan",
|
||||
"pages/jiedan/jiedan",
|
||||
"pages/tixian/tixian",
|
||||
"pages/yaoqingdashou/yaoqingdashou",
|
||||
"pages/wodedashou/wodedashou",
|
||||
"pages/czjilu/czjilu",
|
||||
"pages/guanshipaihang/guanshipaihang",
|
||||
"pages/sjpaidan/sjpaidan",
|
||||
"pages/sjdingdan/sjdingdan",
|
||||
"pages/sjxiaoxi/sjxiaoxi",
|
||||
"pages/sjpaihang/sjpaihang",
|
||||
"pages/sjchongzhi/sjchongzhi",
|
||||
"pages/sjddxq/sjddxq",
|
||||
"pages/dsddxq/dsddxq",
|
||||
"pages/liaotian/liaotian",
|
||||
"pages/jisufd/jisufd",
|
||||
"pages/jiedanchi/jiedanchi",
|
||||
"pages/haibao/haibao",
|
||||
"pages/jinpaids/jinpaids",
|
||||
"pages/zuzhangduan/zuzhangduan",
|
||||
"pages/guanzhual/guanzhual",
|
||||
"pages/zzfhjilu/zzfhjilu",
|
||||
"pages/yqguanshi/yqguanshi",
|
||||
"components/global-notification/global-notification",
|
||||
"components/popup-notice/popup-notice",
|
||||
"pages/tixian/components/mode1/mode1",
|
||||
"pages/tixian/components/mode2/mode2",
|
||||
|
||||
"pages/jiedanchi2/jiedanchi2",
|
||||
"pages/qunliaotian/qunliaotian",
|
||||
"pages/kefuliaotian/kefuliaotian",
|
||||
"pages/renzheng/renzheng",
|
||||
|
||||
"components/order-card/order-card",
|
||||
"components/order-sender/order-sender",
|
||||
"pages/cfss/cfss/cfss",
|
||||
"pages/cfss/components/fakuan-list/fakuan-list",
|
||||
"pages/cfss/components/jifen-list/jifen-list",
|
||||
"pages/cfss/components/fakuan-pay/fakuan-pay",
|
||||
"pages/phone-auth/phone-auth",
|
||||
"components/chenghao-tag/chenghao-tag",
|
||||
"pages/kaoheguan/kaoheguan",
|
||||
"pages/kaohe_dafen/kaohe_dafen",
|
||||
"pages/kaohe_jilu/kaohe_jilu",
|
||||
"pages/kaohe_zhongxin/kaohe_zhongxin",
|
||||
"pages/kaohe_jinpai/kaohe_jinpai"
|
||||
|
||||
],
|
||||
|
||||
"usingComponents": {
|
||||
"popup-notice": "/components/popup-notice/popup-notice",
|
||||
"custom-tab-bar": "/custom-tab-bar/index"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationStyle": "default",
|
||||
"backgroundTextStyle": "light",
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
"navigationBarTitleText": "二H电竞"
|
||||
},
|
||||
"tabBar": {
|
||||
"custom": true,
|
||||
"list": [
|
||||
|
||||
|
||||
{
|
||||
"pagePath": "pages/jiedanchi/jiedanchi",
|
||||
"text": "接单池",
|
||||
"iconPath": "/images/jiedanchi.png",
|
||||
"selectedIconPath": "/images/jiedanchi.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/jiedanchi2/jiedanchi2",
|
||||
"text": "接单池",
|
||||
"iconPath": "/images/jiedanchi.png",
|
||||
"selectedIconPath": "/images/jiedanchi.png"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
"style": "v2",
|
||||
"sitemapLocation": "sitemap.json",
|
||||
"lazyCodeLoading": "requiredComponents"
|
||||
}
|
||||
10
miniprogram/app.wxss
Normal file
@@ -0,0 +1,10 @@
|
||||
/**app.wxss**/
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 200rpx 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
73
miniprogram/components/chenghao-tag/chenghao-tag.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// components/chenghao-tag/chenghao-tag.js
|
||||
const app = getApp();
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
mingcheng: { type: String, value: '' },
|
||||
texiaoJson: { type: Object, value: {} }
|
||||
},
|
||||
data: {
|
||||
// 默认值:宽152,高52,六边形,无背景图,无动画,白色字
|
||||
width: 152,
|
||||
height: 52,
|
||||
shapeClass: 'liubianxing', // 保证至少不是矩形
|
||||
animationClass: '',
|
||||
bgStyle: '',
|
||||
textColor: '#FFFFFF',
|
||||
textSize: 22,
|
||||
imageUrl: ''
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const cfg = this.properties.texiaoJson || {};
|
||||
|
||||
// 1. 尺寸(稍大一些,一行能放3~4个)
|
||||
const width = cfg.width || 152;
|
||||
const height = cfg.height || 52;
|
||||
|
||||
// 2. 背景处理:优先背景图,否则用渐变/纯色
|
||||
let bgStyle = '';
|
||||
let imageUrl = '';
|
||||
if (cfg.image_url) {
|
||||
const ossImageUrl = app.globalData.ossImageUrl || '';
|
||||
imageUrl = cfg.image_url.startsWith('http')
|
||||
? cfg.image_url
|
||||
: ossImageUrl + cfg.image_url;
|
||||
} else if (cfg.bg_gradient) {
|
||||
bgStyle = `background: ${cfg.bg_gradient};`;
|
||||
} else if (cfg.bg_color) {
|
||||
bgStyle = `background: ${cfg.bg_color};`;
|
||||
} else {
|
||||
// 默认金橙渐变
|
||||
bgStyle = `background: linear-gradient(135deg, #FFD700, #FF8C00);`;
|
||||
}
|
||||
|
||||
// 3. 形状(后端传入 shape 字段,默认 liubianxing)
|
||||
const shapeClass = cfg.shape || 'liubianxing';
|
||||
|
||||
// 4. 动画
|
||||
const animationClass = cfg.animation || '';
|
||||
|
||||
// 5. 文字
|
||||
const textColor = cfg.text_color || '#FFFFFF';
|
||||
const textSize = cfg.text_size || 22;
|
||||
|
||||
this.setData({
|
||||
width, height,
|
||||
bgStyle, imageUrl,
|
||||
shapeClass, animationClass,
|
||||
textColor, textSize
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 背景图加载失败时回退为纯色背景
|
||||
onImageError() {
|
||||
this.setData({ imageUrl: '' });
|
||||
// 如果 bgStyle 为空,给个默认背景
|
||||
if (!this.data.bgStyle) {
|
||||
this.setData({ bgStyle: 'background: linear-gradient(135deg, #FFD700, #FF8C00);' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
4
miniprogram/components/chenghao-tag/chenghao-tag.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
20
miniprogram/components/chenghao-tag/chenghao-tag.wxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<!-- components/chenghao-tag/chenghao-tag.wxml -->
|
||||
<view
|
||||
class="tag-root {{shapeClass}} {{animationClass}}"
|
||||
style="width: {{width}}rpx; height: {{height}}rpx; {{bgStyle}}"
|
||||
>
|
||||
<!-- 背景图(如果有) -->
|
||||
<image
|
||||
wx:if="{{imageUrl}}"
|
||||
class="tag-bg-image"
|
||||
src="{{imageUrl}}"
|
||||
mode="aspectFill"
|
||||
binderror="onImageError"
|
||||
></image>
|
||||
|
||||
<!-- 文字层 -->
|
||||
<text
|
||||
class="tag-text"
|
||||
style="color: {{textColor}}; font-size: {{textSize}}rpx;"
|
||||
>{{mingcheng}}</text>
|
||||
</view>
|
||||
90
miniprogram/components/chenghao-tag/chenghao-tag.wxss
Normal file
@@ -0,0 +1,90 @@
|
||||
/* components/chenghao-tag/chenghao-tag.wxss */
|
||||
|
||||
/* ========== 根容器 ========== */
|
||||
.tag-root {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border-radius: 4rpx;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
margin: 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ========== 背景图 ========== */
|
||||
.tag-bg-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
/* ========== 文字层 ========== */
|
||||
.tag-text {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
/* ========== 多边形形状 ========== */
|
||||
.tag-root.liubianxing {
|
||||
clip-path: polygon(12% 0%, 88% 0%, 100% 50%, 88% 100%, 12% 100%, 0% 50%);
|
||||
}
|
||||
.tag-root.lingxing {
|
||||
clip-path: polygon(50% 0%, 85% 12%, 100% 50%, 85% 88%, 50% 100%, 15% 88%, 0% 50%, 15% 12%);
|
||||
}
|
||||
.tag-root.wujiaoxing {
|
||||
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
|
||||
}
|
||||
.tag-root.dunpai {
|
||||
clip-path: polygon(18% 0%, 82% 0%, 100% 18%, 95% 50%, 100% 82%, 82% 100%, 18% 100%, 0% 82%, 5% 50%, 0% 18%);
|
||||
}
|
||||
.tag-root.jiantou {
|
||||
clip-path: polygon(0% 0%, 85% 0%, 100% 50%, 85% 100%, 0% 100%, 12% 50%);
|
||||
}
|
||||
|
||||
/* ========== 动画 ========== */
|
||||
/* 流光扫描 */
|
||||
.tag-root.shine::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
|
||||
transform: skewX(-20deg);
|
||||
animation: shine-anim 2s infinite;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes shine-anim {
|
||||
0% { left: -100%; }
|
||||
100% { left: 200%; }
|
||||
}
|
||||
|
||||
/* 呼吸光晕 */
|
||||
.tag-root.pulse {
|
||||
animation: pulse-anim 1.5s ease-in-out infinite;
|
||||
}
|
||||
@keyframes pulse-anim {
|
||||
0%, 100% { filter: brightness(1); transform: scale(1); }
|
||||
50% { filter: brightness(1.2); transform: scale(1.04); }
|
||||
}
|
||||
|
||||
/* 外发光闪烁 */
|
||||
.tag-root.glow {
|
||||
animation: glow-anim 2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes glow-anim {
|
||||
0%, 100% { box-shadow: 0 0 8rpx rgba(255, 215, 0, 0.4); }
|
||||
50% { box-shadow: 0 0 20rpx rgba(255, 215, 0, 0.8), 0 0 40rpx rgba(255, 215, 0, 0.4); }
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
Component({
|
||||
properties: {
|
||||
position: {
|
||||
type: String,
|
||||
value: 'top'
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
value: 3000
|
||||
},
|
||||
showProgress: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
showMuteButton: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
showTime: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
swipeToClose: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
swipeThreshold: {
|
||||
type: Number,
|
||||
value: 50
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
show: false,
|
||||
title: '',
|
||||
message: '',
|
||||
avatar: '',
|
||||
notificationData: null,
|
||||
progress: 100,
|
||||
progressInterval: null,
|
||||
positionClass: 'top',
|
||||
|
||||
// 滑动相关
|
||||
touchStartY: 0,
|
||||
touchStartX: 0,
|
||||
currentY: 0,
|
||||
currentX: 0,
|
||||
isSwiping: false,
|
||||
swipingClass: '',
|
||||
swipeStyle: '',
|
||||
swipeDistance: 0,
|
||||
|
||||
// 静音状态
|
||||
isMuted: false,
|
||||
|
||||
// 自动隐藏计时器
|
||||
autoHideTimeout: null
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.loadMuteSetting();
|
||||
this.setData({
|
||||
positionClass: this.properties.position
|
||||
});
|
||||
this.setupDirectEventListeners();
|
||||
},
|
||||
|
||||
detached() {
|
||||
this.clearTimers();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setupDirectEventListeners() {
|
||||
const app = getApp();
|
||||
if (app) {
|
||||
const self = this;
|
||||
const globalNotification = {
|
||||
show: (data) => {
|
||||
if (self && self.showNotification) {
|
||||
self.showNotification(data);
|
||||
}
|
||||
},
|
||||
hide: () => {
|
||||
if (self && self.hideNotification) {
|
||||
self.hideNotification();
|
||||
}
|
||||
},
|
||||
isMuted: () => {
|
||||
return self.data.isMuted;
|
||||
}
|
||||
};
|
||||
app.globalData.globalNotification = globalNotification;
|
||||
}
|
||||
},
|
||||
|
||||
loadMuteSetting() {
|
||||
try {
|
||||
const isMuted = wx.getStorageSync('notificationMuted') || false;
|
||||
this.setData({ isMuted });
|
||||
} catch (error) {
|
||||
console.warn('加载静音设置失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
saveMuteSetting(isMuted) {
|
||||
try {
|
||||
wx.setStorageSync('notificationMuted', isMuted);
|
||||
} catch (error) {
|
||||
console.warn('保存静音设置失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
showNotification(data) {
|
||||
if (!data) return;
|
||||
if (this.data.isMuted) return;
|
||||
|
||||
this.clearTimers();
|
||||
|
||||
const app = getApp();
|
||||
let avatar = data.avatar;
|
||||
if (!avatar && app && app.globalData) {
|
||||
avatar = app.globalData.ossImageUrl + app.globalData.morentouxiang;
|
||||
}
|
||||
|
||||
this.setData({
|
||||
show: true,
|
||||
title: data.senderName || '新消息',
|
||||
message: data.content || '[消息内容]',
|
||||
avatar: avatar,
|
||||
notificationData: data,
|
||||
progress: 100,
|
||||
swipingClass: '',
|
||||
swipeStyle: ''
|
||||
});
|
||||
|
||||
if (this.properties.showProgress) {
|
||||
this.startProgressBar(this.properties.duration);
|
||||
}
|
||||
|
||||
this.data.autoHideTimeout = setTimeout(() => {
|
||||
this.hideNotification();
|
||||
}, this.properties.duration);
|
||||
},
|
||||
|
||||
hideNotification() {
|
||||
this.clearTimers();
|
||||
this.setData({
|
||||
show: false,
|
||||
progress: 100,
|
||||
swipingClass: '',
|
||||
swipeStyle: ''
|
||||
});
|
||||
this.triggerEvent('hide');
|
||||
},
|
||||
|
||||
startProgressBar(duration) {
|
||||
const interval = 100;
|
||||
const steps = duration / interval;
|
||||
const stepValue = 100 / steps;
|
||||
|
||||
let currentProgress = 100;
|
||||
this.data.progressInterval = setInterval(() => {
|
||||
currentProgress -= stepValue;
|
||||
if (currentProgress <= 0) {
|
||||
currentProgress = 0;
|
||||
this.setData({ progress: currentProgress });
|
||||
clearInterval(this.data.progressInterval);
|
||||
} else {
|
||||
this.setData({ progress: currentProgress });
|
||||
}
|
||||
}, interval);
|
||||
},
|
||||
|
||||
clearTimers() {
|
||||
if (this.data.autoHideTimeout) {
|
||||
clearTimeout(this.data.autoHideTimeout);
|
||||
this.data.autoHideTimeout = null;
|
||||
}
|
||||
if (this.data.progressInterval) {
|
||||
clearInterval(this.data.progressInterval);
|
||||
this.data.progressInterval = null;
|
||||
}
|
||||
},
|
||||
|
||||
onTouchStart(e) {
|
||||
if (!this.properties.swipeToClose) return;
|
||||
const touch = e.touches[0];
|
||||
this.setData({
|
||||
touchStartY: touch.clientY,
|
||||
touchStartX: touch.clientX,
|
||||
currentY: touch.clientY,
|
||||
currentX: touch.clientX,
|
||||
isSwiping: false
|
||||
});
|
||||
this.clearTimers();
|
||||
},
|
||||
|
||||
onTouchMove(e) {
|
||||
if (!this.properties.swipeToClose) return;
|
||||
const touch = e.touches[0];
|
||||
const deltaY = touch.clientY - this.data.touchStartY;
|
||||
const deltaX = touch.clientX - this.data.touchStartX;
|
||||
const absDeltaY = Math.abs(deltaY);
|
||||
const absDeltaX = Math.abs(deltaX);
|
||||
|
||||
if (absDeltaY > 10 && absDeltaY > absDeltaX) {
|
||||
this.setData({
|
||||
isSwiping: true,
|
||||
currentY: touch.clientY,
|
||||
swipingClass: deltaY < 0 ? 'swiping-up' : 'swiping',
|
||||
swipeDistance: Math.abs(deltaY),
|
||||
swipeStyle: `--swipe-distance: ${Math.abs(deltaY)}rpx;`
|
||||
});
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
onTouchEnd(e) {
|
||||
if (!this.properties.swipeToClose || !this.data.isSwiping) {
|
||||
this.restartAutoHide();
|
||||
return;
|
||||
}
|
||||
const deltaY = this.data.currentY - this.data.touchStartY;
|
||||
const absDeltaY = Math.abs(deltaY);
|
||||
if (absDeltaY > this.properties.swipeThreshold) {
|
||||
this.hideNotification();
|
||||
} else {
|
||||
this.setData({
|
||||
swipingClass: '',
|
||||
swipeStyle: 'transition: transform 0.3s ease; transform: translateY(0);'
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
swipingClass: '',
|
||||
swipeStyle: ''
|
||||
});
|
||||
}, 300);
|
||||
this.restartAutoHide();
|
||||
}
|
||||
this.setData({ isSwiping: false });
|
||||
},
|
||||
|
||||
restartAutoHide() {
|
||||
const remainingTime = (this.data.progress / 100) * this.properties.duration;
|
||||
if (remainingTime > 0) {
|
||||
this.data.autoHideTimeout = setTimeout(() => {
|
||||
this.hideNotification();
|
||||
}, remainingTime);
|
||||
}
|
||||
},
|
||||
|
||||
onTap() {
|
||||
const { notificationData } = this.data;
|
||||
if (!notificationData) return;
|
||||
this.triggerEvent('tap', notificationData);
|
||||
this.hideNotification();
|
||||
this.navigateToChatPage(notificationData);
|
||||
},
|
||||
|
||||
onClose(e) {
|
||||
e.stopPropagation();
|
||||
this.triggerEvent('close');
|
||||
this.hideNotification();
|
||||
},
|
||||
|
||||
onMute(e) {
|
||||
e.stopPropagation();
|
||||
const newMutedState = !this.data.isMuted;
|
||||
this.setData({ isMuted: newMutedState });
|
||||
this.saveMuteSetting(newMutedState);
|
||||
|
||||
const app = getApp();
|
||||
if (app && app.toggleDoNotDisturb) {
|
||||
app.toggleDoNotDisturb(newMutedState);
|
||||
}
|
||||
this.triggerEvent('mute', { muted: newMutedState });
|
||||
wx.showToast({
|
||||
title: newMutedState ? '已开启免打扰' : '已关闭免打扰',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
|
||||
if (newMutedState) {
|
||||
this.hideNotification();
|
||||
}
|
||||
},
|
||||
|
||||
formatTime(timestamp) {
|
||||
if (!timestamp) return '';
|
||||
const date = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diff = now - date;
|
||||
if (diff < 60 * 1000) return '刚刚';
|
||||
if (diff < 60 * 60 * 1000) return Math.floor(diff / (60 * 1000)) + '分钟前';
|
||||
if (date.toDateString() === now.toDateString()) {
|
||||
return date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');
|
||||
}
|
||||
const yesterday = new Date(now);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
if (date.toDateString() === yesterday.toDateString()) {
|
||||
return '昨天 ' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');
|
||||
}
|
||||
return (date.getMonth() + 1) + '月' + date.getDate() + '日';
|
||||
},
|
||||
|
||||
navigateToChatPage(data) {
|
||||
if (!data || !data.message) {
|
||||
console.warn('通知数据无效,无法跳转');
|
||||
return;
|
||||
}
|
||||
|
||||
const msg = data.message;
|
||||
let path = '';
|
||||
let queryParam = null;
|
||||
|
||||
// 根据 notificationType 或消息字段判断
|
||||
if (data.notificationType === 'cs' || msg.teamId) {
|
||||
// 客服消息
|
||||
const teamId = data.teamId || msg.teamId;
|
||||
path = '/pages/kefuliaotian/kefuliaotian';
|
||||
queryParam = { teamId: teamId };
|
||||
} else if (data.notificationType === 'group' || msg.groupId) {
|
||||
// 群聊消息
|
||||
const groupId = data.groupId || msg.groupId;
|
||||
const orderId = data.orderId || msg.orderId || '';
|
||||
const groupName = data.groupName || '订单群聊';
|
||||
const groupAvatar = data.groupAvatar || '';
|
||||
const isCross = data.isCross || msg.isCross || 0;
|
||||
path = '/pages/qunliaotian/qunliaotian';
|
||||
queryParam = {
|
||||
groupId,
|
||||
orderId,
|
||||
groupName,
|
||||
groupAvatar,
|
||||
isCross
|
||||
};
|
||||
} else {
|
||||
// 默认私聊
|
||||
const toUserId = data.senderId || msg.senderId;
|
||||
const toName = data.senderName || '用户';
|
||||
const toAvatar = data.avatar || '';
|
||||
path = '/pages/liaotian/liaotian';
|
||||
queryParam = {
|
||||
toUserId,
|
||||
toName,
|
||||
toAvatar
|
||||
};
|
||||
}
|
||||
|
||||
if (path && queryParam) {
|
||||
const encoded = encodeURIComponent(JSON.stringify(queryParam));
|
||||
wx.navigateTo({
|
||||
url: `${path}?data=${encoded}`,
|
||||
fail: (err) => {
|
||||
console.error('通知跳转失败:', err);
|
||||
wx.switchTab({ url: '/pages/xiaoxi/xiaoxi' });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
wx.switchTab({ url: '/pages/xiaoxi/xiaoxi' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<view
|
||||
class="global-notification {{show ? 'show' : ''}} {{positionClass}} {{swipingClass}}"
|
||||
bindtap="onTap"
|
||||
bindtouchstart="onTouchStart"
|
||||
bindtouchmove="onTouchMove"
|
||||
bindtouchend="onTouchEnd"
|
||||
style="{{swipeStyle}}"
|
||||
data-notification-id="{{notificationData && notificationData.id ? notificationData.id : ''}}">
|
||||
|
||||
<!-- 滑动关闭指示器 -->
|
||||
<view class="notification-swipe-indicator {{isSwiping ? 'active' : ''}}">
|
||||
<view class="swipe-line"></view>
|
||||
</view>
|
||||
|
||||
<!-- 通知内容 -->
|
||||
<view class="notification-content">
|
||||
<!-- 左侧头像 -->
|
||||
<image class="notification-avatar" src="{{avatar}}" mode="aspectFill"></image>
|
||||
|
||||
<!-- 中间内容区域 -->
|
||||
<view class="notification-body">
|
||||
<view class="notification-title">{{title}}</view>
|
||||
<view class="notification-message">{{message}}</view>
|
||||
<view class="notification-time" wx:if="{{showTime}}">
|
||||
{{formatTime(notificationData ? notificationData.timestamp : '')}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧操作区域 -->
|
||||
<view class="notification-actions">
|
||||
<!-- 关闭按钮 -->
|
||||
<view class="notification-close" catchtap="onClose">
|
||||
<text class="close-icon">×</text>
|
||||
</view>
|
||||
|
||||
<!-- 免打扰按钮 -->
|
||||
<view class="notification-mute {{isMuted ? 'muted' : ''}}" catchtap="onMute" wx:if="{{showMuteButton}}">
|
||||
<text class="mute-icon">
|
||||
{{isMuted ? '🔕' : '🔔'}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<view class="notification-progress" wx:if="{{showProgress}}">
|
||||
<view class="progress-bar {{progress < 30 ? 'warning' : ''}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,215 @@
|
||||
.global-notification {
|
||||
position: fixed;
|
||||
left: 20rpx;
|
||||
right: 20rpx;
|
||||
top: 0rpx;
|
||||
z-index: 99999;
|
||||
opacity: 0;
|
||||
transform: translateY(-120%);
|
||||
transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(41, 47, 61, 0.98) 0%,
|
||||
rgba(31, 36, 48, 0.98) 100%);
|
||||
backdrop-filter: blur(30rpx) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(30rpx) saturate(180%);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow:
|
||||
0 20rpx 60rpx rgba(0, 0, 0, 0.4),
|
||||
0 0 0 1rpx rgba(255, 255, 255, 0.03),
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.1);
|
||||
min-height: 140rpx;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
.global-notification.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.notification-swipe-indicator {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.swipe-line {
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 2rpx;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.notification-swipe-indicator.active .swipe-line {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
transform: scaleX(1.2);
|
||||
}
|
||||
|
||||
.notification-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx;
|
||||
min-height: 140rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notification-avatar {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
border: 3rpx solid rgba(212, 175, 55, 0.6);
|
||||
box-shadow:
|
||||
0 8rpx 24rpx rgba(212, 175, 55, 0.2),
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.3);
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.notification-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.notification-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background: linear-gradient(135deg, #D4AF37 0%, #FFD700 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.notification-message {
|
||||
font-size: 28rpx;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.notification-time {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
margin-top: 4rpx;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.notification-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 20rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.notification-close,
|
||||
.notification-mute {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 28rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
transition: all 0.3s;
|
||||
flex-shrink: 0;
|
||||
box-shadow:
|
||||
0 4rpx 12rpx rgba(0, 0, 0, 0.2),
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.notification-close:active,
|
||||
.notification-mute:active {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
font-size: 32rpx;
|
||||
font-weight: 300;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mute-icon {
|
||||
font-size: 28rpx;
|
||||
line-height: 0;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.notification-mute.muted .mute-icon {
|
||||
color: #D4AF37;
|
||||
}
|
||||
|
||||
.notification-progress {
|
||||
height: 6rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
#D4AF37 0%,
|
||||
#FFD700 50%,
|
||||
#D4AF37 100%);
|
||||
background-size: 200% 100%;
|
||||
transition: width 0.1s linear;
|
||||
box-shadow: 0 0 20rpx rgba(212, 175, 55, 0.4);
|
||||
animation: progressShine 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes progressShine {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
.progress-bar.warning {
|
||||
background: linear-gradient(90deg,
|
||||
#ff3b30 0%,
|
||||
#ff6b6b 50%,
|
||||
#ff3b30 100%);
|
||||
animation: progressShine 1s linear infinite;
|
||||
}
|
||||
|
||||
.global-notification.swiping {
|
||||
opacity: 0.7;
|
||||
transform: translateY(calc(var(--swipe-distance, 0) * -1));
|
||||
}
|
||||
|
||||
.global-notification.swiping-up {
|
||||
opacity: 0.3;
|
||||
transform: translateY(calc(var(--swipe-distance, 0) * -1));
|
||||
}
|
||||
|
||||
.global-notification:active:not(.swiping) {
|
||||
transform: scale(0.995) translateY(0);
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
102
miniprogram/components/navigation-bar/navigation-bar.js
Normal file
@@ -0,0 +1,102 @@
|
||||
Component({
|
||||
options: {
|
||||
multipleSlots: true // 在组件定义时的选项中启用多slot支持
|
||||
},
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
properties: {
|
||||
extClass: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
back: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
homeButton: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
animated: {
|
||||
// 显示隐藏的时候opacity动画效果
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
show: {
|
||||
// 显示隐藏导航,隐藏的时候navigation-bar的高度占位还在
|
||||
type: Boolean,
|
||||
value: true,
|
||||
observer: '_showChange'
|
||||
},
|
||||
// back为true的时候,返回的页面深度
|
||||
delta: {
|
||||
type: Number,
|
||||
value: 1
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
displayStyle: ''
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const rect = wx.getMenuButtonBoundingClientRect()
|
||||
const platform = (wx.getDeviceInfo() || wx.getSystemInfoSync()).platform
|
||||
const isAndroid = platform === 'android'
|
||||
const isDevtools = platform === 'devtools'
|
||||
const { windowWidth, safeArea: { top = 0, bottom = 0 } = {} } = wx.getWindowInfo() || wx.getSystemInfoSync()
|
||||
this.setData({
|
||||
ios: !isAndroid,
|
||||
innerPaddingRight: `padding-right: ${windowWidth - rect.left}px`,
|
||||
leftWidth: `width: ${windowWidth - rect.left}px`,
|
||||
safeAreaTop: isDevtools || isAndroid ? `height: calc(var(--height) + ${top}px); padding-top: ${top}px` : ``
|
||||
})
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
_showChange(show) {
|
||||
const animated = this.data.animated
|
||||
let displayStyle = ''
|
||||
if (animated) {
|
||||
displayStyle = `opacity: ${show ? '1' : '0'
|
||||
};transition:opacity 0.5s;`
|
||||
} else {
|
||||
displayStyle = `display: ${show ? '' : 'none'}`
|
||||
}
|
||||
this.setData({
|
||||
displayStyle
|
||||
})
|
||||
},
|
||||
back() {
|
||||
const data = this.data
|
||||
if (data.delta) {
|
||||
wx.navigateBack({
|
||||
delta: data.delta
|
||||
})
|
||||
}
|
||||
this.triggerEvent('back', { delta: data.delta }, {})
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"component": true,
|
||||
"styleIsolation": "apply-shared",
|
||||
"usingComponents": {}
|
||||
}
|
||||
64
miniprogram/components/navigation-bar/navigation-bar.wxml
Normal file
@@ -0,0 +1,64 @@
|
||||
<view class="weui-navigation-bar {{extClass}}">
|
||||
<view class="weui-navigation-bar__inner {{ios ? 'ios' : 'android'}}" style="color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}}; {{safeAreaTop}}">
|
||||
|
||||
<!-- 左侧按钮 -->
|
||||
<view class='weui-navigation-bar__left' style="{{leftWidth}}">
|
||||
<block wx:if="{{back || homeButton}}">
|
||||
<!-- 返回上一页 -->
|
||||
<block wx:if="{{back}}">
|
||||
<view class="weui-navigation-bar__buttons weui-navigation-bar__buttons_goback">
|
||||
<view
|
||||
bindtap="back"
|
||||
class="weui-navigation-bar__btn_goback_wrapper"
|
||||
hover-class="weui-active"
|
||||
hover-stay-time="100"
|
||||
aria-role="button"
|
||||
aria-label="返回"
|
||||
>
|
||||
<view class="weui-navigation-bar__button weui-navigation-bar__btn_goback"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 返回首页 -->
|
||||
<block wx:if="{{homeButton}}">
|
||||
<view class="weui-navigation-bar__buttons weui-navigation-bar__buttons_home">
|
||||
<view
|
||||
bindtap="home"
|
||||
class="weui-navigation-bar__btn_home_wrapper"
|
||||
hover-class="weui-active"
|
||||
aria-role="button"
|
||||
aria-label="首页"
|
||||
>
|
||||
<view class="weui-navigation-bar__button weui-navigation-bar__btn_home"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<slot name="left"></slot>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 标题 -->
|
||||
<view class='weui-navigation-bar__center'>
|
||||
<view wx:if="{{loading}}" class="weui-navigation-bar__loading" aria-role="alert">
|
||||
<view
|
||||
class="weui-loading"
|
||||
aria-role="img"
|
||||
aria-label="加载中"
|
||||
></view>
|
||||
</view>
|
||||
<block wx:if="{{title}}">
|
||||
<text>{{title}}</text>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<slot name="center"></slot>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 右侧留空 -->
|
||||
<view class='weui-navigation-bar__right'>
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
96
miniprogram/components/navigation-bar/navigation-bar.wxss
Normal file
@@ -0,0 +1,96 @@
|
||||
.weui-navigation-bar {
|
||||
--weui-FG-0:rgba(0,0,0,.9);
|
||||
--height: 44px;
|
||||
--left: 16px;
|
||||
}
|
||||
.weui-navigation-bar .android {
|
||||
--height: 48px;
|
||||
}
|
||||
|
||||
.weui-navigation-bar {
|
||||
overflow: hidden;
|
||||
color: var(--weui-FG-0);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.weui-navigation-bar__inner {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: calc(var(--height) + env(safe-area-inset-top));
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: env(safe-area-inset-top);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.weui-navigation-bar__left {
|
||||
position: relative;
|
||||
padding-left: var(--left);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.weui-navigation-bar__btn_goback_wrapper {
|
||||
padding: 11px 18px 11px 16px;
|
||||
margin: -11px -18px -11px -16px;
|
||||
}
|
||||
|
||||
.weui-navigation-bar__btn_goback_wrapper.weui-active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.weui-navigation-bar__btn_goback {
|
||||
font-size: 12px;
|
||||
width: 12px;
|
||||
height: 24px;
|
||||
-webkit-mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E") no-repeat 50% 50%;
|
||||
mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E") no-repeat 50% 50%;
|
||||
-webkit-mask-size: cover;
|
||||
mask-size: cover;
|
||||
background-color: var(--weui-FG-0);
|
||||
}
|
||||
|
||||
.weui-navigation-bar__center {
|
||||
font-size: 17px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.weui-navigation-bar__loading {
|
||||
margin-right: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.weui-loading {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
background: transparent url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A") no-repeat;
|
||||
background-size: 100%;
|
||||
margin-left: 0;
|
||||
animation: loading linear infinite 1s;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
from {
|
||||
transform: rotate(0);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
102
miniprogram/components/order-card/order-card.js
Normal file
@@ -0,0 +1,102 @@
|
||||
const app = getApp();
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
orderId: { type: String, value: '' }
|
||||
},
|
||||
|
||||
data: {
|
||||
order: null,
|
||||
loading: false,
|
||||
errorMsg: '',
|
||||
// 跨平台字段
|
||||
isCross: false, // 是否跨平台订单
|
||||
dispatchType: 0, // 1-我方派单,2-对方派单
|
||||
partnerClubId: '', // 对方俱乐部ID
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
if (this.properties.orderId) {
|
||||
this.fetchOrderDetail();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 获取订单详情(含跨平台字段)
|
||||
fetchOrderDetail() {
|
||||
if (!this.properties.orderId) return;
|
||||
this.setData({ loading: true, errorMsg: '' });
|
||||
const baseUrl = app.globalData.apiBaseUrl;
|
||||
wx.request({
|
||||
url: `${baseUrl}/dingdan/order_detail/`, // 需后端返回 is_cross, dispatch_type, partner_club_id
|
||||
method: 'POST',
|
||||
data: { order_id: this.properties.orderId },
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
const order = res.data.data;
|
||||
// 处理图片URL
|
||||
if (order.tupian && !order.tupian.startsWith('http')) {
|
||||
order.tupian = app.globalData.ossImageUrl + order.tupian;
|
||||
}
|
||||
this.setData({
|
||||
order,
|
||||
loading: false,
|
||||
isCross: order.is_cross === 1,
|
||||
dispatchType: order.dispatch_type || 0,
|
||||
partnerClubId: order.partner_club_id || '',
|
||||
});
|
||||
// 通知父页面状态
|
||||
this.triggerEvent('statusready', {
|
||||
isCross: this.data.isCross,
|
||||
dispatchType: this.data.dispatchType,
|
||||
partnerClubId: this.data.partnerClubId
|
||||
});
|
||||
} else {
|
||||
this.setData({ errorMsg: res.data?.msg || '获取订单失败', loading: false });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
this.setData({ errorMsg: '网络请求失败', loading: false });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 发送订单卡片消息(原有)
|
||||
sendOrderMessage() {
|
||||
if (!this.data.order) return;
|
||||
this.triggerEvent('sendorder', { order: this.data.order });
|
||||
},
|
||||
|
||||
// 跨平台消息转发(供父页面调用)
|
||||
forwardMessage(payload) {
|
||||
const that = this;
|
||||
const baseUrl = app.globalData.apiBaseUrl;
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${baseUrl}/dingdan/kptxxts`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
order_id: that.properties.orderId,
|
||||
message_type: payload.type || 'text',
|
||||
content: payload.content || '',
|
||||
image_url: payload.imageUrl || '', // 图片等
|
||||
},
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
reject(res.data);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
4
miniprogram/components/order-card/order-card.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
20
miniprogram/components/order-card/order-card.wxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<view class="order-card" wx:if="{{order}}">
|
||||
<view class="card-header">
|
||||
<text class="card-title">关联订单</text>
|
||||
<text class="card-id">{{order.dingdan_id}}</text>
|
||||
<!-- 跨平台标记 -->
|
||||
<text class="cross-tag" wx:if="{{isCross}}">跨平台</text>
|
||||
</view>
|
||||
<view class="card-body" bindtap="sendOrderMessage">
|
||||
<image class="card-img" src="{{order.tupian || '/images/default-goods.png'}}" mode="aspectFill" />
|
||||
<view class="card-info">
|
||||
<text class="card-jieshao">{{order.jieshao || '暂无介绍'}}</text>
|
||||
<text class="card-jine">¥{{order.jine}}</text>
|
||||
</view>
|
||||
<view class="card-send-btn">发送</view>
|
||||
</view>
|
||||
<view wx:if="{{errorMsg}}" class="card-error">{{errorMsg}}</view>
|
||||
</view>
|
||||
<view class="order-card" wx:elif="{{loading}}">
|
||||
<text class="loading-text">加载订单信息...</text>
|
||||
</view>
|
||||
39
miniprogram/components/order-card/order-card.wxss
Normal file
@@ -0,0 +1,39 @@
|
||||
.order-card {
|
||||
background: #fff;
|
||||
margin: 15rpx 20rpx;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05);
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16rpx 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.card-title { font-size: 26rpx; color: #666; }
|
||||
.card-id { font-size: 24rpx; color: #999; margin-left: 15rpx; }
|
||||
.cross-tag {
|
||||
margin-left: auto;
|
||||
background: #ff9900;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
.card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
}
|
||||
.card-img { width: 100rpx; height: 100rpx; border-radius: 8rpx; margin-right: 15rpx; }
|
||||
.card-info { flex: 1; }
|
||||
.card-jieshao { font-size: 28rpx; color: #333; }
|
||||
.card-jine { font-size: 26rpx; color: #e74c3c; }
|
||||
.card-send-btn {
|
||||
width: 80rpx; height: 50rpx; line-height: 50rpx;
|
||||
text-align: center; background: #07c160; color: #fff;
|
||||
border-radius: 8rpx; font-size: 24rpx;
|
||||
}
|
||||
.card-error { padding: 20rpx; color: #e74c3c; text-align: center; }
|
||||
.loading-text { display: block; padding: 20rpx; text-align: center; color: #999; }
|
||||
16
miniprogram/components/order-sender/order-sender.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const app = getApp();
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
visible: Boolean,
|
||||
showDetail: { type: Boolean, value: false }
|
||||
},
|
||||
|
||||
data: {},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
this.triggerEvent('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
4
miniprogram/components/order-sender/order-sender.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
12
miniprogram/components/order-sender/order-sender.wxml
Normal file
@@ -0,0 +1,12 @@
|
||||
<view class="order-sender-mask" wx:if="{{visible}}" catchtap="close"></view>
|
||||
<view class="order-sender {{visible ? 'show' : ''}}">
|
||||
<view class="header">
|
||||
<text class="title">提示</text>
|
||||
<text class="close" bindtap="close">✕</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="icon">📋</view>
|
||||
<text class="notice">此功能暂未开放</text>
|
||||
<text class="sub">敬请期待</text>
|
||||
</view>
|
||||
</view>
|
||||
45
miniprogram/components/order-sender/order-sender.wxss
Normal file
@@ -0,0 +1,45 @@
|
||||
.order-sender-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 2000;
|
||||
}
|
||||
.order-sender {
|
||||
position: fixed; bottom: 0; left: 0; right: 0;
|
||||
height: 360rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
z-index: 2001;
|
||||
transform: translateY(100%);
|
||||
transition: 0.25s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.order-sender.show { transform: translateY(0); }
|
||||
|
||||
.header {
|
||||
display: flex; justify-content: space-between;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.title { font-size: 32rpx; font-weight: 600; }
|
||||
.close { font-size: 44rpx; color: #999; padding: 0 12rpx; }
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
.icon { font-size: 80rpx; margin-bottom: 20rpx; }
|
||||
.notice {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
324
miniprogram/components/popup-notice/popup-notice.js
Normal file
@@ -0,0 +1,324 @@
|
||||
// components/popup-notice/popup-notice.js
|
||||
import PopupService from '../../services/popupService.js';
|
||||
|
||||
Component({
|
||||
data: {
|
||||
// 原有全屏弹窗数据
|
||||
visible: false,
|
||||
title: '',
|
||||
images: [],
|
||||
processedImages: [],
|
||||
hasImages: false,
|
||||
hasTextOnly: false,
|
||||
textContent: '',
|
||||
duration: 0,
|
||||
remainingSeconds: 0,
|
||||
muteChecked: false,
|
||||
showMuteCheckbox: false,
|
||||
isFullscreen: false,
|
||||
timer: null,
|
||||
|
||||
// 悬浮窗相关数据
|
||||
isMinimized: false,
|
||||
floatX: 20,
|
||||
floatY: 200,
|
||||
savedRemainingSeconds: 0,
|
||||
floatAvatarUrl: '',
|
||||
|
||||
// 🆕 自定义预览数据
|
||||
showCustomPreview: false, // 是否显示自定义预览遮罩
|
||||
previewUrls: [], // 当前预览的图片URL列表
|
||||
currentPreviewIndex: 0, // 当前预览图片索引
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
detached() {
|
||||
this.clearTimer();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// ========== 原有所有方法(完全不变,一字不漏) ==========
|
||||
async show(config, onCloseCallback) {
|
||||
const { title, images, duration, showMuteCheckbox, serverTime } = config;
|
||||
this.onCloseCallback = onCloseCallback;
|
||||
|
||||
let safeImages = JSON.parse(JSON.stringify(images || []));
|
||||
const hasImages = safeImages.length > 0;
|
||||
let hasTextOnly = false, textContent = '';
|
||||
if (!hasImages) {
|
||||
hasTextOnly = true;
|
||||
textContent = config.text || '暂无内容';
|
||||
}
|
||||
|
||||
this.setData({
|
||||
visible: true,
|
||||
isMinimized: false,
|
||||
title: title || '',
|
||||
images: safeImages,
|
||||
hasImages,
|
||||
hasTextOnly,
|
||||
textContent,
|
||||
duration: duration || 0,
|
||||
remainingSeconds: duration || 0,
|
||||
muteChecked: false,
|
||||
showMuteCheckbox: showMuteCheckbox || false,
|
||||
isFullscreen: false,
|
||||
processedImages: [],
|
||||
});
|
||||
|
||||
if (duration > 0) {
|
||||
this.startCountdown();
|
||||
}
|
||||
|
||||
if (hasImages) {
|
||||
const processed = [];
|
||||
const screenWidth = wx.getSystemInfoSync().windowWidth;
|
||||
const containerWidth = screenWidth * 0.9;
|
||||
const imageWidth = containerWidth - 60;
|
||||
for (let i = 0; i < safeImages.length; i++) {
|
||||
const img = safeImages[i];
|
||||
const fullUrl = this.getFullImageUrl(img.url);
|
||||
let imageHeight = 200;
|
||||
try {
|
||||
const res = await this.getImageInfoPromise(fullUrl);
|
||||
const realHeight = (imageWidth * res.height) / res.width;
|
||||
imageHeight = realHeight;
|
||||
} catch (e) {
|
||||
console.error('获取图片信息失败', fullUrl, e);
|
||||
}
|
||||
processed.push({
|
||||
url: fullUrl,
|
||||
text: img.text,
|
||||
height: imageHeight,
|
||||
});
|
||||
}
|
||||
this.setData({ processedImages: processed });
|
||||
}
|
||||
},
|
||||
|
||||
getImageInfoPromise(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getImageInfo({
|
||||
src: url,
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
hide(muteToday = false) {
|
||||
this.clearTimer();
|
||||
this.setData({
|
||||
visible: false,
|
||||
isMinimized: false
|
||||
});
|
||||
if (this.onCloseCallback) {
|
||||
this.onCloseCallback({ muteToday });
|
||||
}
|
||||
},
|
||||
|
||||
startCountdown() {
|
||||
this.clearTimer();
|
||||
this.timer = setInterval(() => {
|
||||
let sec = this.data.remainingSeconds;
|
||||
if (sec <= 1) {
|
||||
this.clearTimer();
|
||||
this.setData({ remainingSeconds: 0 });
|
||||
} else {
|
||||
this.setData({ remainingSeconds: sec - 1 });
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
clearTimer() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
|
||||
onCloseTap() {
|
||||
if (this.data.remainingSeconds > 0) {
|
||||
wx.showToast({
|
||||
title: `请阅读${this.data.remainingSeconds}秒后再关闭`,
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.data.showMuteCheckbox && this.data.muteChecked) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '今天将不再收到本页面的任何公告,确认吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) this.hide(true);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.hide(false);
|
||||
}
|
||||
},
|
||||
|
||||
onMuteChange(e) {
|
||||
this.setData({ muteChecked: e.detail.value.length > 0 });
|
||||
},
|
||||
|
||||
// 🆕 替换原有的 onPreviewImage,改用自定义预览(不触发 onShow)
|
||||
onCustomPreview(e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const urls = this.data.processedImages.map(img => img.url);
|
||||
this.setData({
|
||||
showCustomPreview: true,
|
||||
previewUrls: urls,
|
||||
currentPreviewIndex: index !== undefined ? index : 0
|
||||
});
|
||||
},
|
||||
|
||||
// 🆕 关闭自定义预览
|
||||
onCloseCustomPreview() {
|
||||
this.setData({
|
||||
showCustomPreview: false,
|
||||
previewUrls: [],
|
||||
currentPreviewIndex: 0
|
||||
});
|
||||
},
|
||||
|
||||
// 🆕 swiper 切换事件
|
||||
onPreviewSwiperChange(e) {
|
||||
this.setData({
|
||||
currentPreviewIndex: e.detail.current
|
||||
});
|
||||
},
|
||||
|
||||
// 保留原 onPreviewImage 方法以防外部调用,但不再使用
|
||||
onPreviewImage(e) {
|
||||
// 重定向到自定义预览
|
||||
this.onCustomPreview(e);
|
||||
},
|
||||
|
||||
onImageError(e) {
|
||||
console.error('图片加载失败:', e.currentTarget.dataset.url);
|
||||
},
|
||||
|
||||
toggleFullscreen() {
|
||||
this.setData({ isFullscreen: !this.data.isFullscreen });
|
||||
},
|
||||
|
||||
getFullImageUrl(relativeUrl) {
|
||||
if (!relativeUrl) return '';
|
||||
if (relativeUrl.startsWith('http')) return relativeUrl;
|
||||
const app = getApp();
|
||||
let base = app.globalData.ossImageUrl || '';
|
||||
base = base.replace(/\/+$/, '');
|
||||
const clean = relativeUrl.replace(/^\/+/, '');
|
||||
return base ? base + '/' + clean : clean;
|
||||
},
|
||||
|
||||
preventMove() {},
|
||||
|
||||
onMinimize() {
|
||||
const remaining = this.data.remainingSeconds;
|
||||
this.clearTimer();
|
||||
|
||||
const app = getApp();
|
||||
let avatarUrl = '';
|
||||
if (app.globalData.morentouxiang) {
|
||||
avatarUrl = app.globalData.morentouxiang;
|
||||
} else {
|
||||
avatarUrl = '/images/default-avatar.png';
|
||||
}
|
||||
|
||||
if (avatarUrl && !avatarUrl.startsWith('http')) {
|
||||
const ossBase = app.globalData.ossImageUrl || '';
|
||||
avatarUrl = ossBase.replace(/\/+$/, '') + '/' + avatarUrl.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
this.setData({
|
||||
visible: false,
|
||||
isMinimized: true,
|
||||
savedRemainingSeconds: remaining,
|
||||
floatX: 20,
|
||||
floatY: 200,
|
||||
floatAvatarUrl: avatarUrl
|
||||
});
|
||||
},
|
||||
|
||||
onFloatChange(e) {
|
||||
this.setData({
|
||||
floatX: e.detail.x,
|
||||
floatY: e.detail.y
|
||||
});
|
||||
},
|
||||
|
||||
onFloatTap() {
|
||||
this.setData({
|
||||
visible: true,
|
||||
isMinimized: false,
|
||||
remainingSeconds: this.data.savedRemainingSeconds
|
||||
});
|
||||
|
||||
if (this.data.duration > 0 && this.data.savedRemainingSeconds > 0) {
|
||||
this.startCountdown();
|
||||
}
|
||||
},
|
||||
|
||||
onFloatAvatarError() {
|
||||
this.setData({
|
||||
floatAvatarUrl: '/images/avatar-placeholder.png'
|
||||
});
|
||||
},
|
||||
|
||||
cleanup() {
|
||||
this.clearTimer();
|
||||
this.setData({
|
||||
visible: false,
|
||||
isMinimized: false
|
||||
});
|
||||
PopupService.reset();
|
||||
},
|
||||
|
||||
onFloatTouchStart(e) {
|
||||
const touch = e.touches[0];
|
||||
this.touchStartX = touch.clientX;
|
||||
this.touchStartY = touch.clientY;
|
||||
this.startFloatX = this.data.floatX;
|
||||
this.startFloatY = this.data.floatY;
|
||||
this.hasMoved = false;
|
||||
return false;
|
||||
},
|
||||
|
||||
onFloatTouchMove(e) {
|
||||
const touch = e.touches[0];
|
||||
const deltaX = touch.clientX - this.touchStartX;
|
||||
const deltaY = touch.clientY - this.touchStartY;
|
||||
|
||||
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
||||
this.hasMoved = true;
|
||||
}
|
||||
|
||||
let newX = this.startFloatX + deltaX;
|
||||
let newY = this.startFloatY + deltaY;
|
||||
|
||||
const systemInfo = wx.getSystemInfoSync();
|
||||
const screenWidth = systemInfo.windowWidth;
|
||||
const screenHeight = systemInfo.windowHeight;
|
||||
const estimatedSize = 60;
|
||||
|
||||
newX = Math.max(0, Math.min(newX, screenWidth - estimatedSize));
|
||||
newY = Math.max(0, Math.min(newY, screenHeight - estimatedSize));
|
||||
this.setData({ floatX: newX, floatY: newY });
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
onFloatTouchEnd(e) {
|
||||
if (!this.hasMoved) {
|
||||
this.onFloatTap();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// 🆕 阻止事件冒泡(用于预览遮罩内部点击不关闭)
|
||||
stopPropagation() {}
|
||||
}
|
||||
});
|
||||
4
miniprogram/components/popup-notice/popup-notice.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
110
miniprogram/components/popup-notice/popup-notice.wxml
Normal file
@@ -0,0 +1,110 @@
|
||||
<!-- components/popup-notice/popup-notice.wxml -->
|
||||
|
||||
<!-- 全屏弹窗遮罩层(完全不变) -->
|
||||
<view class="popup-mask" wx:if="{{visible}}" catchtouchmove="preventMove">
|
||||
<view class="popup-container {{isFullscreen ? 'fullscreen' : ''}}">
|
||||
<!-- 右上角操作按钮组 -->
|
||||
<view class="action-buttons">
|
||||
<!-- 最小化按钮 -->
|
||||
<view class="minimize-btn" bindtap="onMinimize">
|
||||
<text class="btn-icon">−</text>
|
||||
</view>
|
||||
<!-- 全屏切换按钮 -->
|
||||
<view class="fullscreen-btn" bindtap="toggleFullscreen">
|
||||
<text class="btn-icon">{{isFullscreen ? '✕' : '⛶'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 标题 -->
|
||||
<view wx:if="{{title}}" class="popup-title">{{title}}</view>
|
||||
|
||||
<!-- 可滚动内容区 -->
|
||||
<view class="scroll-wrapper">
|
||||
<scroll-view scroll-y class="scroll-view" enhanced show-scrollbar="{{true}}">
|
||||
<view wx:if="{{hasTextOnly}}" class="text-content">{{textContent}}</view>
|
||||
<view wx:else>
|
||||
<view wx:for="{{processedImages}}" wx:key="index" class="image-block">
|
||||
<view wx:if="{{item.text}}" class="block-text">{{item.text}}</view>
|
||||
<view class="image-padding">
|
||||
<image
|
||||
class="block-image"
|
||||
src="{{item.url}}"
|
||||
style="width:100%; height:{{item.height}}px; display:block;"
|
||||
data-index="{{index}}"
|
||||
data-url="{{item.url}}"
|
||||
bindtap="onCustomPreview"
|
||||
binderror="onImageError"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定区 -->
|
||||
<view class="bottom-fixed">
|
||||
<view wx:if="{{remainingSeconds > 0}}" class="countdown-tip">
|
||||
请阅读 {{remainingSeconds}} 秒后关闭
|
||||
</view>
|
||||
<view wx:if="{{remainingSeconds === 0 && showMuteCheckbox}}" class="mute-row">
|
||||
<checkbox-group bindchange="onMuteChange">
|
||||
<checkbox value="mute" color="#000" />
|
||||
<text class="mute-label">今日不再提示</text>
|
||||
</checkbox-group>
|
||||
</view>
|
||||
<!-- 关闭按钮 -->
|
||||
<view
|
||||
class="close-btn {{remainingSeconds > 0 ? 'close-btn-disabled' : ''}}"
|
||||
bindtap="{{remainingSeconds > 0 ? '' : 'onCloseTap'}}"
|
||||
>
|
||||
<text class="close-btn-text">{{remainingSeconds > 0 ? '阅读中...' : '关闭'}}</text>
|
||||
<view class="close-btn-sweep" wx:if="{{remainingSeconds === 0}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮窗(自定义触摸拖动) -->
|
||||
<view
|
||||
class="floating-avatar-wrapper"
|
||||
wx:if="{{isMinimized}}"
|
||||
style="left: {{floatX}}px; top: {{floatY}}px;"
|
||||
catchtouchstart="onFloatTouchStart"
|
||||
catchtouchmove="onFloatTouchMove"
|
||||
catchtouchend="onFloatTouchEnd"
|
||||
>
|
||||
<image
|
||||
class="floating-avatar"
|
||||
src="{{floatAvatarUrl}}"
|
||||
mode="aspectFill"
|
||||
binderror="onFloatAvatarError"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 🆕 自定义图片预览遮罩(全屏,不触发页面生命周期) -->
|
||||
<view
|
||||
class="custom-preview-mask"
|
||||
wx:if="{{showCustomPreview}}"
|
||||
catchtouchmove="preventMove"
|
||||
bindtap="onCloseCustomPreview"
|
||||
>
|
||||
<view class="preview-header">
|
||||
<text class="preview-index">{{currentPreviewIndex + 1}} / {{previewUrls.length}}</text>
|
||||
<view class="preview-close" bindtap="onCloseCustomPreview">×</view>
|
||||
</view>
|
||||
<swiper
|
||||
class="preview-swiper"
|
||||
current="{{currentPreviewIndex}}"
|
||||
bindchange="onPreviewSwiperChange"
|
||||
indicator-dots="{{false}}"
|
||||
>
|
||||
<swiper-item wx:for="{{previewUrls}}" wx:key="index">
|
||||
<image
|
||||
src="{{item}}"
|
||||
mode="aspectFit"
|
||||
class="preview-image"
|
||||
catchtap="stopPropagation"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
295
miniprogram/components/popup-notice/popup-notice.wxss
Normal file
@@ -0,0 +1,295 @@
|
||||
/* components/popup-notice/popup-notice.wxss */
|
||||
|
||||
/* ========== 全屏弹窗样式(原有,完全不变) ========== */
|
||||
.popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.popup-container {
|
||||
width: 90%;
|
||||
height: 85vh;
|
||||
background: white;
|
||||
border-radius: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.popup-container.fullscreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.minimize-btn,
|
||||
.fullscreen-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(4rpx);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.minimize-btn:active,
|
||||
.fullscreen-btn:active {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 36rpx;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
padding: 30rpx 30rpx 20rpx;
|
||||
border-bottom: 2rpx solid #eee;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.scroll-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scroll-view {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
line-height: 1.6;
|
||||
padding: 30rpx;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.image-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.block-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
padding: 16rpx 30rpx 4rpx 30rpx;
|
||||
text-align: left;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.image-padding {
|
||||
padding: 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.block-image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
background: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.bottom-fixed {
|
||||
background: white;
|
||||
border-top: 1rpx solid #eee;
|
||||
padding: 20rpx 0 30rpx 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.countdown-tip {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #ff6600;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.mute-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.mute-label {
|
||||
font-size: 28rpx;
|
||||
color: #555;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
/* 🔥 关闭按钮(替换原有 button,增加扫光) */
|
||||
.close-btn {
|
||||
width: 600rpx;
|
||||
height: 100rpx;
|
||||
background: #1a1a1a;
|
||||
border-radius: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.close-btn:active {
|
||||
opacity: 0.85;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.close-btn-disabled {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.close-btn-text {
|
||||
color: white;
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.close-btn-sweep {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
|
||||
transform: skewX(-20deg);
|
||||
animation: sweep 2.5s infinite ease-in-out;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes sweep {
|
||||
0% { left: -100%; }
|
||||
40% { left: 100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
/* ========== 悬浮窗样式(纯圆形,适配自定义触摸拖动) ========== */
|
||||
.floating-avatar-wrapper {
|
||||
position: fixed;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
z-index: 10001;
|
||||
transform: translateZ(0);
|
||||
will-change: left, top;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.floating-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.3);
|
||||
border: 2rpx solid rgba(255, 215, 0, 0.6);
|
||||
background: #f0f0f0;
|
||||
display: block;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* 以下原有 movable-view 相关样式已弃用,但保留以维持代码完整(用户要求不删代码) */
|
||||
.floating-area {
|
||||
display: none;
|
||||
}
|
||||
.floating-view {
|
||||
display: none;
|
||||
}
|
||||
.float-avatar-wrapper {
|
||||
display: none;
|
||||
}
|
||||
.float-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ========== 自定义图片预览遮罩样式(全屏,不触发 onShow) ========== */
|
||||
.custom-preview-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
z-index: 100000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.preview-header {
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
color: white;
|
||||
font-size: 32rpx;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.preview-index {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preview-close {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48rpx;
|
||||
color: white;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.preview-swiper {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
144
miniprogram/config/roles.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 角色配置表 —— 用于动态切换底部 TabBar 展示内容
|
||||
* 每个角色的 tabList 数组元素顺序决定 TabBar 显示顺序
|
||||
* 字段说明:
|
||||
* pagePath : 页面路径(以 pages/ 开头)
|
||||
* text : TabBar 文字
|
||||
* iconPath : 未选中图标(相对路径)
|
||||
* selectedIconPath: 选中图标
|
||||
*/
|
||||
const roles = {
|
||||
// 普通老板(默认)
|
||||
normal: {
|
||||
name: '普通老板',
|
||||
defaultPage: 'pages/index/index',
|
||||
tabList: [
|
||||
{
|
||||
pagePath: 'pages/index/index',
|
||||
text: '商城',
|
||||
iconPath: '/images/zhuye.png',
|
||||
selectedIconPath: '/images/zhuye.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/fenlei/fenlei',
|
||||
text: '分类',
|
||||
iconPath: '/images/fenlei.png',
|
||||
selectedIconPath: '/images/fenlei.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/xiaoxi/xiaoxi',
|
||||
text: '消息',
|
||||
iconPath: '/images/xiaoxi.png',
|
||||
selectedIconPath: '/images/xiaoxi.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/wode/wode',
|
||||
text: '我的',
|
||||
iconPath: '/images/wode.png',
|
||||
selectedIconPath: '/images/wode.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
// 打手
|
||||
dashou: {
|
||||
name: '打手',
|
||||
defaultPage: 'pages/jiedanchi/jiedanchi',
|
||||
tabList: [
|
||||
{
|
||||
pagePath: 'pages/jiedanchi/jiedanchi',
|
||||
text: '接单池',
|
||||
iconPath: '/images/jiedanchi.png',
|
||||
selectedIconPath: '/images/jiedanchi.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/xiaoxi/xiaoxi',
|
||||
text: '消息',
|
||||
iconPath: '/images/xiaoxi.png',
|
||||
selectedIconPath: '/images/xiaoxi.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/dashoudingdan/dashoudingdan',
|
||||
text: '订单',
|
||||
iconPath: '/images/dashoudingdan_icon.png', // 请替换为实际图标
|
||||
selectedIconPath: '/images/dashoudingdan_icon.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/dashouduan/dashouduan',
|
||||
text: '我的',
|
||||
iconPath: '/images/dashouduan_icon.png',
|
||||
selectedIconPath: '/images/dashouduan_icon.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
// 商家
|
||||
shangjia: {
|
||||
name: '商家',
|
||||
defaultPage: 'pages/index/index',
|
||||
tabList: [
|
||||
{
|
||||
pagePath: 'pages/index/index',
|
||||
text: '首页',
|
||||
iconPath: '/images/zhuye.png',
|
||||
selectedIconPath: '/images/zhuye.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/sjdingdan/sjdingdan',
|
||||
text: '订单',
|
||||
iconPath: '/images/sjdingdan_icon.png', // 请替换为实际图标
|
||||
selectedIconPath: '/images/sjdingdan_icon.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/xiaoxi/xiaoxi',
|
||||
text: '消息',
|
||||
iconPath: '/images/xiaoxi.png',
|
||||
selectedIconPath: '/images/xiaoxi.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/shangjiaduan/shangjiaduan',
|
||||
text: '我的',
|
||||
iconPath: '/images/shangjiaduan_icon.png',
|
||||
selectedIconPath: '/images/shangjiaduan_icon.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
// 管事
|
||||
guanshi: {
|
||||
name: '管事',
|
||||
defaultPage: 'pages/guanshiduan/guanshiduan',
|
||||
tabList: [
|
||||
{
|
||||
pagePath: 'pages/xiaoxi/xiaoxi',
|
||||
text: '消息',
|
||||
iconPath: '/images/xiaoxi.png',
|
||||
selectedIconPath: '/images/xiaoxi.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/guanshiduan/guanshiduan',
|
||||
text: '我的',
|
||||
iconPath: '/images/guanshiduan_icon.png',
|
||||
selectedIconPath: '/images/guanshiduan_icon.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
// 组长
|
||||
zuzhang: {
|
||||
name: '组长',
|
||||
defaultPage: 'pages/zuzhangduan/zuzhangduan',
|
||||
tabList: [
|
||||
{
|
||||
pagePath: 'pages/xiaoxi/xiaoxi',
|
||||
text: '消息',
|
||||
iconPath: '/images/xiaoxi.png',
|
||||
selectedIconPath: '/images/xiaoxi.png'
|
||||
},
|
||||
{
|
||||
pagePath: 'pages/zuzhangduan/zuzhangduan',
|
||||
text: '我的',
|
||||
iconPath: '/images/zuzhangduan_icon.png',
|
||||
selectedIconPath: '/images/zuzhangduan_icon.png'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { roles };
|
||||
194
miniprogram/custom-tab-bar/index.js
Normal file
@@ -0,0 +1,194 @@
|
||||
// custom-tab-bar/index.js
|
||||
const app = getApp();
|
||||
|
||||
const roleCfg = {
|
||||
normal: {
|
||||
name: '点单老板',
|
||||
defaultPage: '/pages/index/index',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/index/index', text: '商城', iconPath: '/images/zhuye.png', selectedIconPath: '/images/zhuye.png' },
|
||||
{ pagePath: 'pages/fenlei/fenlei', text: '分类', iconPath: '/images/fenlei.png', selectedIconPath: '/images/fenlei.png' },
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/wode/wode', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
dashou: {
|
||||
name: '打手',
|
||||
defaultPage: '/pages/jiedan/jiedan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/jiedan/jiedan', text: '接单池', iconPath: '/images/jiedanchi.png', selectedIconPath: '/images/jiedanchi.png' },
|
||||
{ pagePath: 'pages/dashoudingdan/dashoudingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' },
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/dashouduan/dashouduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
shangjia: {
|
||||
name: '商家',
|
||||
defaultPage: 'pages/sjdingdan/sjdingdan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/sjdingdan/sjdingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' },
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/shangjiaduan/shangjiaduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
guanshi: {
|
||||
name: '管事',
|
||||
defaultPage: '/pages/guanshiduan/guanshiduan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/guanshiduan/guanshiduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
zuzhang: {
|
||||
name: '组长',
|
||||
defaultPage: '/pages/zuzhangduan/zuzhangduan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/zuzhangduan/zuzhangduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
kaoheguan: {
|
||||
name: '考核官',
|
||||
defaultPage: '/pages/kaoheguan/kaoheguan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/kaoheguan/kaoheguan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
Component({
|
||||
data: {
|
||||
selectedIndex: 0,
|
||||
tabList: [],
|
||||
currentRole: 'normal',
|
||||
showRolePicker: false,
|
||||
availableRoles: [],
|
||||
badgeText: ''
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.refresh();
|
||||
this._badgeListener = (data) => {
|
||||
if (data.badgeText !== undefined) {
|
||||
this.setData({ badgeText: data.badgeText });
|
||||
}
|
||||
};
|
||||
app.on('tabBarBadgeChanged', this._badgeListener);
|
||||
},
|
||||
detached() {
|
||||
if (this._badgeListener) {
|
||||
app.off('tabBarBadgeChanged', this._badgeListener);
|
||||
}
|
||||
}
|
||||
},
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
this.refresh();
|
||||
const globalBadge = app.globalData.messageManager?.tabBarBadgeText || '';
|
||||
if (this.data.badgeText !== globalBadge) {
|
||||
this.setData({ badgeText: globalBadge });
|
||||
}
|
||||
this._syncUnreadWithDebounce();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_syncUnreadWithDebounce() {
|
||||
if (this._syncTimer) clearTimeout(this._syncTimer);
|
||||
this._syncTimer = setTimeout(() => {
|
||||
if (app && app.loadConversations) {
|
||||
app.loadConversations();
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
|
||||
refresh() {
|
||||
const role = app.globalData.currentRole || 'normal';
|
||||
let cfg = roleCfg[role];
|
||||
if (!cfg) return;
|
||||
|
||||
// 打手身份下,如果 isJinpai=1,悄悄把“我的”页面换成金牌页面
|
||||
if (role === 'dashou') {
|
||||
const isJinpai = wx.getStorageSync('isJinpai') === 1;
|
||||
if (isJinpai) {
|
||||
cfg = {
|
||||
...cfg,
|
||||
tabList: cfg.tabList.map(item => {
|
||||
if (item.pagePath === 'pages/dashouduan/dashouduan') {
|
||||
return { ...item, pagePath: 'pages/jinpaids/jinpaids' };
|
||||
}
|
||||
return { ...item };
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const list = cfg.tabList.map((v, i) => ({ ...v, index: i }));
|
||||
const pages = getCurrentPages();
|
||||
const curRoute = pages.length ? pages[pages.length - 1].route : '';
|
||||
let idx = list.findIndex(t => t.pagePath === curRoute);
|
||||
if (idx === -1) idx = 0;
|
||||
const avail = this._getAvail();
|
||||
this.setData({ currentRole: role, tabList: list, selectedIndex: idx, availableRoles: avail });
|
||||
},
|
||||
|
||||
_getAvail() {
|
||||
const arr = [{ key: 'normal', name: '点单老板' }];
|
||||
if (wx.getStorageSync('dashoustatus') === 1) arr.push({ key: 'dashou', name: '打手' });
|
||||
if (wx.getStorageSync('shangjiastatus') === 1) arr.push({ key: 'shangjia', name: '商家' });
|
||||
if (wx.getStorageSync('guanshistatus') === 1) arr.push({ key: 'guanshi', name: '管事' });
|
||||
if (wx.getStorageSync('zuzhangstatus') === 1) arr.push({ key: 'zuzhang', name: '组长' });
|
||||
if (wx.getStorageSync('kaoheguanstatus') === 1) arr.push({ key: 'kaoheguan', name: '考核官' });
|
||||
return arr;
|
||||
},
|
||||
|
||||
toggleRolePicker() {
|
||||
this.setData({ showRolePicker: !this.data.showRolePicker });
|
||||
},
|
||||
|
||||
async selectRole(e) {
|
||||
const newRole = e.currentTarget.dataset.role;
|
||||
if (newRole === this.data.currentRole) {
|
||||
this.setData({ showRolePicker: false });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ showRolePicker: false });
|
||||
|
||||
// 1. 立即更新角色并保存
|
||||
app.globalData.currentRole = newRole;
|
||||
wx.setStorageSync('currentRole', newRole);
|
||||
|
||||
// 2. 🔥 确定跳转页面:所有角色都去“我的”页面
|
||||
let targetPage = '';
|
||||
if (newRole === 'dashou') {
|
||||
// 打手需要根据金牌状态决定跳转
|
||||
const isJinpai = wx.getStorageSync('isJinpai') === 1;
|
||||
targetPage = isJinpai ? '/pages/jinpaids/jinpaids' : '/pages/dashouduan/dashouduan';
|
||||
} else {
|
||||
// 其他角色直接取 tabList 最后一个页面
|
||||
const cfg = roleCfg[newRole];
|
||||
if (cfg && cfg.tabList.length > 0) {
|
||||
targetPage = '/' + cfg.tabList[cfg.tabList.length - 1].pagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 跳转
|
||||
if (targetPage) {
|
||||
wx.reLaunch({ url: targetPage });
|
||||
}
|
||||
|
||||
// 4. 后台异步切换连接
|
||||
app.switchRoleAndReconnect(newRole).catch(err => {
|
||||
console.error('后台切换连接失败:', err);
|
||||
});
|
||||
},
|
||||
|
||||
switchTab(e) {
|
||||
const path = e.currentTarget.dataset.path;
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
if (idx === this.data.selectedIndex) return;
|
||||
wx.redirectTo({ url: '/' + path });
|
||||
}
|
||||
}
|
||||
});
|
||||
4
miniprogram/custom-tab-bar/index.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
42
miniprogram/custom-tab-bar/index.wxml
Normal file
@@ -0,0 +1,42 @@
|
||||
<view class="custom-tab-bar">
|
||||
<view class="bar-row">
|
||||
<!-- 左侧椭圆身份按钮 -->
|
||||
<view class="role-switch-btn" catchtap="toggleRolePicker">
|
||||
<image class="role-switch-icon" src="/images/role-switch.png" mode="aspectFit" />
|
||||
<text class="role-switch-text">切换</text>
|
||||
</view>
|
||||
|
||||
<!-- 右侧纯黑闪光胶囊 -->
|
||||
<view class="tab-capsule">
|
||||
<block wx:for="{{tabList}}" wx:key="index">
|
||||
<view class="tab-item {{selectedIndex === index ? 'active' : ''}}"
|
||||
data-path="{{item.pagePath}}"
|
||||
data-index="{{index}}"
|
||||
catchtap="switchTab">
|
||||
<!-- 🔥 根据页面路径判断是否显示角标,不再写死 index -->
|
||||
<view class="badge-container" wx:if="{{item.pagePath === 'pages/xiaoxi/xiaoxi' && badgeText}}">
|
||||
<view class="tab-badge">{{badgeText}}</view>
|
||||
</view>
|
||||
<image class="tab-icon" src="{{selectedIndex === index ? item.selectedIconPath : item.iconPath}}" mode="aspectFit" />
|
||||
<text class="tab-text">{{item.text}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 身份选择弹窗 -->
|
||||
<view wx:if="{{showRolePicker}}" class="picker-mask" catchtap="toggleRolePicker"></view>
|
||||
<view wx:if="{{showRolePicker}}" class="picker-panel">
|
||||
<view class="picker-header">
|
||||
<text class="picker-title">切换身份</text>
|
||||
<text class="picker-close" catchtap="toggleRolePicker">✕</text>
|
||||
</view>
|
||||
<block wx:for="{{availableRoles}}" wx:key="key">
|
||||
<view class="role-option {{currentRole === item.key ? 'active' : ''}}"
|
||||
data-role="{{item.key}}" catchtap="selectRole">
|
||||
<text>{{item.name}}</text>
|
||||
<text wx:if="{{currentRole === item.key}}" class="check-mark">✓</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
165
miniprogram/custom-tab-bar/index.wxss
Normal file
@@ -0,0 +1,165 @@
|
||||
/* custom-tab-bar/index.wxss - 角标浮于右上角,可超出胶囊 */
|
||||
|
||||
.custom-tab-bar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.bar-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15rpx;
|
||||
padding: 0 20rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 左侧椭圆身份按钮 */
|
||||
.role-switch-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 110rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50rpx;
|
||||
background: #000000;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.8);
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.role-switch-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-bottom: 2rpx;
|
||||
}
|
||||
|
||||
.role-switch-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
color: #00f7ff;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* 纯黑闪光胶囊,移除overflow保证角标可见 */
|
||||
.tab-capsule {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
height: 100rpx;
|
||||
background: #000000;
|
||||
border-radius: 50rpx;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.8);
|
||||
padding: 0 20rpx;
|
||||
position: relative;
|
||||
}
|
||||
.tab-capsule::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: -100%;
|
||||
width: 60%; height: 100%;
|
||||
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%, transparent 100%);
|
||||
animation: shine 3s infinite;
|
||||
z-index: 1;
|
||||
}
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
25% { left: 150%; }
|
||||
100% { left: 150%; }
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
position: relative; /* 角标的定位参考 */
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 70rpx;
|
||||
}
|
||||
.tab-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 4rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.tab-text {
|
||||
font-size: 20rpx;
|
||||
color: #aaa;
|
||||
z-index: 2;
|
||||
}
|
||||
.active .tab-icon {
|
||||
opacity: 1;
|
||||
filter: brightness(0) invert(1) drop-shadow(0 0 8rpx #00aaff);
|
||||
}
|
||||
.active .tab-text {
|
||||
color: #00aaff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 🔴 角标容器:绝对定位到图标右上角,允许超出 */
|
||||
.badge-container {
|
||||
position: absolute;
|
||||
top: -10rpx; /* 往上提,更接近图标右上角 */
|
||||
right: -15rpx; /* 往右伸,超出tab-item */
|
||||
z-index: 100;
|
||||
pointer-events: none; /* 不阻挡点击 */
|
||||
}
|
||||
.tab-badge {
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
padding: 0 10rpx;
|
||||
background: #ff3b30;
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 36rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.3);
|
||||
border: 1rpx solid rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
/* 弹窗部分保持不变 */
|
||||
.picker-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.6);
|
||||
z-index: 10000;
|
||||
}
|
||||
.picker-panel {
|
||||
position: fixed; bottom: 0; left: 0; right: 0;
|
||||
background: #000000;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
z-index: 10001;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.picker-header {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 30rpx 40rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
.picker-title { font-size: 32rpx; color: #00f7ff; }
|
||||
.picker-close { font-size: 36rpx; color: #aaa; padding: 0 10rpx; }
|
||||
.role-option {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 28rpx 40rpx;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||
color: #ccc; font-size: 28rpx;
|
||||
}
|
||||
.role-option.active { color: #00f7ff; }
|
||||
.check-mark { font-size: 32rpx; color: #00f7ff; }
|
||||
|
||||
page {
|
||||
padding-bottom: 100rpx;
|
||||
}
|
||||
BIN
miniprogram/images/dingdan.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
miniprogram/images/fenlei.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
miniprogram/images/gonggao.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
miniprogram/images/goumai.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
miniprogram/images/jiedanchi.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
miniprogram/images/jieshao.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
miniprogram/images/jinpaidashou.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
miniprogram/images/role-switch.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
miniprogram/images/shuaxin.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
miniprogram/images/sousuo.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
miniprogram/images/tiaoxuan.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
miniprogram/images/tuichu.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
miniprogram/images/weixinzhifu.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
miniprogram/images/wode.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
miniprogram/images/xiaoxi.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
miniprogram/images/yituikuan.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
miniprogram/images/yiwancheng.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
miniprogram/images/zhuanqu.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
miniprogram/images/zhuye.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
100
miniprogram/pages/cfss/cfss/cfss.js
Normal file
@@ -0,0 +1,100 @@
|
||||
// pages/cfss/cfss.js
|
||||
import request from '../../../utils/request.js'; // 向上4级就是根目录
|
||||
// 如果有 COS:
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 统计数字(由接口 /yonghu/cffktjhq 返回)
|
||||
stats: {
|
||||
fakuan: { total: 0, daijiaona: 0, shensuzhong: 0, yijiaona: 0, yibohui: 0 },
|
||||
jifen: { total: 0, daichuli: 0, yichuli: 0, daichuli_0: 0, shensuzhong_3: 0, yichufa_1: 0, yibohui_2: 0 }
|
||||
},
|
||||
|
||||
// 当前选中的主标签:'fakuan' 或 'jifen'
|
||||
activeTab: 'fakuan',
|
||||
|
||||
// 子标签状态值
|
||||
activeFakuanStatus: 1, // 罚款默认展示“待缴纳”
|
||||
activeJifenStatus: 0, // 积分默认展示“待处理”
|
||||
|
||||
// 搜索关键词(订单号)
|
||||
searchDingdan: '',
|
||||
|
||||
// 刷新触发器,传给子组件
|
||||
trigger: {
|
||||
fakuan: 0,
|
||||
jifen: 0
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.fetchTongji();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面都重新拉取统计,确保数字最新
|
||||
this.fetchTongji();
|
||||
},
|
||||
|
||||
// 获取综合统计
|
||||
async fetchTongji() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/cffktjhq',
|
||||
method: 'POST'
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
this.setData({ stats: res.data.data });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取处罚统计失败', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 切换主Tab(罚款 / 积分)
|
||||
switchMainTab(e) {
|
||||
const tab = e.currentTarget.dataset.tab;
|
||||
if (tab === this.data.activeTab) return;
|
||||
this.setData({ activeTab: tab });
|
||||
},
|
||||
|
||||
// 切换罚款状态子标签
|
||||
switchFakuanStatus(e) {
|
||||
const status = Number(e.currentTarget.dataset.status);
|
||||
if (status === this.data.activeFakuanStatus) return;
|
||||
this.setData({ activeFakuanStatus: status });
|
||||
this.bumpTrigger('fakuan'); // 通知罚款列表组件刷新
|
||||
},
|
||||
|
||||
// 切换积分状态子标签
|
||||
switchJifenStatus(e) {
|
||||
const status = Number(e.currentTarget.dataset.status);
|
||||
if (status === this.data.activeJifenStatus) return;
|
||||
this.setData({ activeJifenStatus: status });
|
||||
this.bumpTrigger('jifen'); // 通知积分列表组件刷新
|
||||
},
|
||||
|
||||
// 搜索输入
|
||||
onSearchInput(e) {
|
||||
this.setData({ searchDingdan: e.detail.value });
|
||||
},
|
||||
|
||||
// 执行搜索(按键盘搜索按钮或点击搜索图标)
|
||||
onSearchConfirm() {
|
||||
this.bumpTrigger(this.data.activeTab);
|
||||
},
|
||||
|
||||
// 清空搜索关键字
|
||||
clearSearch() {
|
||||
this.setData({ searchDingdan: '' });
|
||||
this.bumpTrigger(this.data.activeTab);
|
||||
},
|
||||
|
||||
// 递增触发器,子组件通过 observer 响应
|
||||
bumpTrigger(tab) {
|
||||
const key = `trigger.${tab}`;
|
||||
this.setData({ [key]: this.data.trigger[tab] + 1 });
|
||||
}
|
||||
});
|
||||
10
miniprogram/pages/cfss/cfss/cfss.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"fakuan-list": "/pages/cfss/components/fakuan-list/fakuan-list",
|
||||
"jifen-list": "/pages/cfss/components/jifen-list/jifen-list"
|
||||
},
|
||||
"navigationBarTitleText": "处罚中心",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f7f8fa"
|
||||
}
|
||||
68
miniprogram/pages/cfss/cfss/cfss.wxml
Normal file
@@ -0,0 +1,68 @@
|
||||
<!-- pages/cfss/cfss.wxml -->
|
||||
<view class="page-container">
|
||||
<!-- 统计卡片 -->
|
||||
<view class="stats-card">
|
||||
<view class="stats-number-row">
|
||||
<view class="number-block">
|
||||
<text class="big-num green">{{ stats.fakuan.daijiaona }}</text>
|
||||
<text class="num-label">待缴罚款</text>
|
||||
</view>
|
||||
<view class="number-block">
|
||||
<text class="big-num orange">{{ stats.jifen.daichuli }}</text>
|
||||
<text class="num-label">待处理积分</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-detail-row">
|
||||
<text>罚款:已缴{{ stats.fakuan.yijiaona }} · 申诉中{{ stats.fakuan.shensuzhong }} · 成功{{ stats.fakuan.yibohui }}</text>
|
||||
<text>积分:已处理{{ stats.jifen.yichuli }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主Tab -->
|
||||
<view class="tab-row">
|
||||
<view class="tab-item {{ activeTab === 'fakuan' ? 'active' : '' }}" bindtap="switchMainTab" data-tab="fakuan">
|
||||
罚款
|
||||
</view>
|
||||
<view class="tab-item {{ activeTab === 'jifen' ? 'active' : '' }}" bindtap="switchMainTab" data-tab="jifen">
|
||||
积分处罚
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 子状态标签(横向滑动) -->
|
||||
<block wx:if="{{ activeTab === 'fakuan' }}">
|
||||
<scroll-view class="sub-tabs" scroll-x enable-flex>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 1 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="1">待缴纳 {{ stats.fakuan.daijiaona }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 3 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="3">申诉中 {{ stats.fakuan.shensuzhong }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 2 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="2">已缴纳 {{ stats.fakuan.yijiaona }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 4 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="4">申诉成功 {{ stats.fakuan.yibohui }}</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<scroll-view class="sub-tabs" scroll-x enable-flex>
|
||||
<view class="sub-tag {{ activeJifenStatus === 0 ? 'sub-active' : '' }}" bindtap="switchJifenStatus" data-status="0">待处理 {{ stats.jifen.daichuli }}</view>
|
||||
<view class="sub-tag {{ activeJifenStatus === 1 ? 'sub-active' : '' }}" bindtap="switchJifenStatus" data-status="1">已处理 {{ stats.jifen.yichuli }}</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<input class="search-input" placeholder="输入订单号搜索" value="{{ searchDingdan }}" bindinput="onSearchInput" bindconfirm="onSearchConfirm" confirm-type="search"/>
|
||||
<view class="search-btn" bindtap="onSearchConfirm">搜索</view>
|
||||
<view wx:if="{{ searchDingdan }}" class="clear-btn" bindtap="clearSearch">✕</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表区域(动态显示对应组件) -->
|
||||
<view wx:if="{{ activeTab === 'fakuan' }}" class="list-area">
|
||||
<fakuan-list
|
||||
status="{{ activeFakuanStatus }}"
|
||||
searchDingdan="{{ searchDingdan }}"
|
||||
trigger="{{ trigger.fakuan }}"
|
||||
/>
|
||||
</view>
|
||||
<view wx:else class="list-area">
|
||||
<jifen-list
|
||||
status="{{ activeJifenStatus }}"
|
||||
trigger="{{ trigger.jifen }}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
121
miniprogram/pages/cfss/cfss/cfss.wxss
Normal file
@@ -0,0 +1,121 @@
|
||||
/* pages/cfss/cfss.wxss */
|
||||
page { background: #f7f8fa; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
|
||||
.page-container { padding-bottom: 30rpx; }
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-card {
|
||||
margin: 24rpx 28rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
.stats-number-row {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.number-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.big-num {
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.green { color: #2E7D32; }
|
||||
.orange { color: #E67E22; }
|
||||
.num-label {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.stats-detail-row {
|
||||
border-top: 1rpx solid #eee;
|
||||
padding-top: 18rpx;
|
||||
}
|
||||
.stats-detail-row text {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #aaa;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
/* 主Tab */
|
||||
.tab-row {
|
||||
display: flex;
|
||||
margin: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 24rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
transition: 0.2s;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #2E7D32;
|
||||
font-weight: 600;
|
||||
background: #F0F9F0;
|
||||
}
|
||||
|
||||
/* 子状态标签 */
|
||||
.sub-tabs {
|
||||
white-space: nowrap;
|
||||
margin: 8rpx 28rpx 16rpx;
|
||||
}
|
||||
.sub-tag {
|
||||
display: inline-block;
|
||||
padding: 10rpx 26rpx;
|
||||
margin-right: 14rpx;
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
box-shadow: 0 1rpx 4rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
.sub-tag.sub-active {
|
||||
background: #E8F5E9;
|
||||
color: #2E7D32;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 搜索栏 */
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 12rpx 22rpx;
|
||||
box-shadow: 0 1rpx 6rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
.search-btn {
|
||||
margin-left: 18rpx;
|
||||
padding: 12rpx 30rpx;
|
||||
background: #2E7D32;
|
||||
color: #fff;
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.clear-btn {
|
||||
margin-left: 16rpx;
|
||||
font-size: 30rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.list-area {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
317
miniprogram/pages/cfss/components/fakuan-list/fakuan-list.js
Normal file
@@ -0,0 +1,317 @@
|
||||
// components/fakuan-list/fakuan-list.js
|
||||
import request from '../../../../utils/request.js';
|
||||
|
||||
const app = getApp();
|
||||
const MEIYE_TIAOSHU = 5;
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
status: { type: Number, value: 1, observer: 'reload' },
|
||||
searchDingdan: { type: String, value: '', observer: 'reload' },
|
||||
trigger: { type: Number, value: 0, observer: 'reload' }
|
||||
},
|
||||
|
||||
data: {
|
||||
list: [],
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
|
||||
// 详情弹窗
|
||||
showDetail: false,
|
||||
detailItem: null,
|
||||
|
||||
// 申诉弹窗
|
||||
showShensu: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
uploading: false,
|
||||
uploadProgress: { current: 0, total: 0 },
|
||||
uploadProgressWidth: '0%',
|
||||
|
||||
// 支付弹窗
|
||||
showPay: false,
|
||||
payFadan: null
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() { this.reload(); }
|
||||
},
|
||||
|
||||
methods: {
|
||||
// ==================== 数据加载 ====================
|
||||
reload() {
|
||||
this.setData({ page: 1, list: [], hasMore: true });
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
async loadData(isLoadMore = false) {
|
||||
// 防止重复请求
|
||||
if (this.data.loading || this.data.loadingMore) return;
|
||||
|
||||
if (!isLoadMore) {
|
||||
this.setData({ loading: true });
|
||||
} else {
|
||||
// 加载更多时,如果后端已经告知没有更多数据,不发送请求
|
||||
if (!this.data.hasMore) {
|
||||
wx.showToast({ title: '没有更多了', icon: 'none', duration: 1000 });
|
||||
return;
|
||||
}
|
||||
this.setData({ loadingMore: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/dsfklbhq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
page: this.data.page,
|
||||
page_size: MEIYE_TIAOSHU,
|
||||
zhuangtai: this.data.status,
|
||||
sousuo_dingdan_id: this.data.searchDingdan
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
const newList = isLoadMore ? this.data.list.concat(data.list || []) : (data.list || []);
|
||||
this.setData({
|
||||
list: newList,
|
||||
hasMore: data.has_more || false, // 以后端返回为准
|
||||
page: isLoadMore ? this.data.page + 1 : 2, // 首次加载后页码为2
|
||||
loading: false,
|
||||
loadingMore: false
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '加载失败', icon: 'none' });
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
if (this.data.list.length === 0) return;
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
// ==================== 详情弹窗 ====================
|
||||
openDetail(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
this.setData({ showDetail: true, detailItem: item });
|
||||
},
|
||||
closeDetail() {
|
||||
this.setData({ showDetail: false, detailItem: null });
|
||||
},
|
||||
|
||||
// ==================== 申诉逻辑 ====================
|
||||
openShensu() {
|
||||
const item = this.data.detailItem;
|
||||
if (item.bohuiliyou) {
|
||||
wx.showToast({ title: '该罚单申诉已被驳回', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const existingImgs = (item.shensu_tupian || []).map(url => this.getFullUrl(url));
|
||||
this.setData({
|
||||
showShensu: true,
|
||||
shensuLiyou: item.shensuliyou || '',
|
||||
shensuTupian: existingImgs,
|
||||
uploading: false,
|
||||
uploadProgress: { current: 0, total: 0 },
|
||||
uploadProgressWidth: '0%'
|
||||
});
|
||||
},
|
||||
closeShensu() {
|
||||
this.setData({ showShensu: false });
|
||||
},
|
||||
onShensuInput(e) {
|
||||
this.setData({ shensuLiyou: e.detail.value.slice(0, 500) });
|
||||
},
|
||||
|
||||
chooseImage() {
|
||||
const remain = 9 - this.data.shensuTupian.length;
|
||||
if (remain <= 0) return;
|
||||
wx.chooseMedia({
|
||||
count: remain,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
const newImgs = [...this.data.shensuTupian, ...res.tempFiles.map(f => f.tempFilePath)];
|
||||
this.setData({ shensuTupian: newImgs.slice(0, 9) });
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteShensuImg(e) {
|
||||
const arr = this.data.shensuTupian;
|
||||
arr.splice(e.currentTarget.dataset.index, 1);
|
||||
this.setData({ shensuTupian: arr });
|
||||
},
|
||||
previewImage(e) {
|
||||
wx.previewImage({ current: e.currentTarget.dataset.url, urls: [e.currentTarget.dataset.url] });
|
||||
},
|
||||
previewLocalImg(e) {
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
wx.previewImage({ current: this.data.shensuTupian[idx], urls: this.data.shensuTupian });
|
||||
},
|
||||
|
||||
// ==================== 提交流程 ====================
|
||||
async submitShensu() {
|
||||
if (!this.data.shensuLiyou.trim()) {
|
||||
wx.showToast({ title: '请输入申诉理由', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '提交中...' });
|
||||
try {
|
||||
const newLocalPaths = this.data.shensuTupian.filter(path => !path.startsWith('http'));
|
||||
let finalUrls = [];
|
||||
if (newLocalPaths.length > 0) {
|
||||
finalUrls = await this.piliangShangchuanShensuTupian(newLocalPaths);
|
||||
if (!finalUrls) {
|
||||
wx.hideLoading();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const oldRemoteRelative = this.data.shensuTupian
|
||||
.filter(path => path.startsWith('http'))
|
||||
.map(url => url.replace(this.data.ossImageUrl, '').replace(/^\//, ''));
|
||||
const allUrls = oldRemoteRelative.concat(finalUrls);
|
||||
await this.doSubmitShensu(allUrls);
|
||||
} catch (err) {
|
||||
console.error('提交申诉失败', err);
|
||||
wx.showToast({ title: err.message || '提交失败', icon: 'none' });
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
async doSubmitShensu(tupianUrls) {
|
||||
const res = await request({
|
||||
url: '/yonghu/fkss',
|
||||
method: 'POST',
|
||||
data: {
|
||||
fadan_id: this.data.detailItem.id,
|
||||
shensu_liyou: this.data.shensuLiyou,
|
||||
shensu_tupian_urls: tupianUrls
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
wx.showToast({ title: '申诉已提交', icon: 'success' });
|
||||
this.setData({ showShensu: false, showDetail: false });
|
||||
this.reload();
|
||||
} else {
|
||||
throw new Error(res.data?.msg || '提交失败');
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 图片上传到COS ====================
|
||||
async piliangShangchuanShensuTupian(localPaths) {
|
||||
const total = localPaths.length;
|
||||
if (!total) return [];
|
||||
this.setData({ uploading: true, uploadProgress: { current: 0, total: total }, uploadProgressWidth: '0%' });
|
||||
|
||||
let tokenData;
|
||||
try {
|
||||
const credRes = await request({
|
||||
url: '/dingdan/dsscpz',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdan_id: this.data.detailItem.guanliandingdan_id || '',
|
||||
fadan_id: this.data.detailItem.id,
|
||||
yongtu: 'fakuan'
|
||||
}
|
||||
});
|
||||
if (credRes.data.code !== 0) throw new Error(credRes.data.msg || '凭证获取失败');
|
||||
tokenData = credRes.data.data;
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '获取上传凭证失败', icon: 'none' });
|
||||
this.setData({ uploading: false });
|
||||
return null;
|
||||
}
|
||||
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.js');
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const cos = new COS({
|
||||
SimpleUploadMethod: 'putObject',
|
||||
getAuthorization: (options, callback) => {
|
||||
callback({
|
||||
TmpSecretId: credentials.tmpSecretId,
|
||||
TmpSecretKey: credentials.tmpSecretKey,
|
||||
SecurityToken: credentials.sessionToken || '',
|
||||
StartTime: tokenData.startTime,
|
||||
ExpiredTime: tokenData.expiredTime
|
||||
});
|
||||
}
|
||||
});
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063';
|
||||
const region = tokenData.region || 'ap-shanghai';
|
||||
|
||||
const yonghuid = app.globalData.userInfo?.yonghuid || wx.getStorageSync('uid') || '0000000';
|
||||
const fadanId = this.data.detailItem.id;
|
||||
const keys = [];
|
||||
for (let i = 0; i < total; i++) {
|
||||
keys.push(`fakuan/dashoufakuan/dashoufakuanshensu/${yonghuid}_${fadanId}_${Date.now() + i}.jpg`);
|
||||
}
|
||||
|
||||
for (let i = 0; i < total; i++) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => resolve(), 2000);
|
||||
cos.uploadFile({
|
||||
Bucket: bucket,
|
||||
Region: region,
|
||||
Key: keys[i],
|
||||
FilePath: localPaths[i],
|
||||
onProgress: (info) => {
|
||||
if (Math.round(info.percent * 100) === 100) {
|
||||
clearTimeout(timeoutId);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}, (err) => {
|
||||
clearTimeout(timeoutId);
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
wx.showToast({ title: `第 ${i + 1} 张上传失败`, icon: 'none' });
|
||||
this.setData({ uploading: false });
|
||||
return null;
|
||||
}
|
||||
const done = i + 1;
|
||||
this.setData({
|
||||
uploadProgress: { current: done, total: total },
|
||||
uploadProgressWidth: `${((done / total) * 100).toFixed(0)}%`
|
||||
});
|
||||
}
|
||||
this.setData({ uploading: false });
|
||||
return keys;
|
||||
},
|
||||
|
||||
// ==================== 缴纳支付 ====================
|
||||
startPay() {
|
||||
this.setData({
|
||||
showDetail: false,
|
||||
showPay: true,
|
||||
payFadan: this.data.detailItem
|
||||
});
|
||||
},
|
||||
onPayClose() {
|
||||
this.setData({ showPay: false, payFadan: null });
|
||||
},
|
||||
onPaySuccess() {
|
||||
this.setData({ showPay: false, payFadan: null });
|
||||
this.reload();
|
||||
},
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
getFullUrl(url) {
|
||||
if (!url) return '';
|
||||
if (url.startsWith('http')) return url;
|
||||
return this.data.ossImageUrl + url;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"fakuan-pay": "/pages/cfss/components/fakuan-pay/fakuan-pay"
|
||||
}
|
||||
}
|
||||
116
miniprogram/pages/cfss/components/fakuan-list/fakuan-list.wxml
Normal file
@@ -0,0 +1,116 @@
|
||||
<!-- components/fakuan-list/fakuan-list.wxml -->
|
||||
<view class="container">
|
||||
<scroll-view scroll-y class="scroll" bindscrolltolower="loadMore">
|
||||
<view wx:if="{{ loading && list.length === 0 }}" class="tip">加载中...</view>
|
||||
<block wx:for="{{ list }}" wx:key="id">
|
||||
<view class="card" bindtap="openDetail" data-item="{{ item }}">
|
||||
<view class="card-head">
|
||||
<text class="time">{{ item.create_time }}</text>
|
||||
<text class="tag {{ item.status_class }}">{{ item.display_status }}</text>
|
||||
</view>
|
||||
<text class="info">金额:¥{{ item.fakuanjine }}</text>
|
||||
<text class="info">理由:{{ item.chufaliyou }}</text>
|
||||
<text class="info" wx:if="{{ item.guanliandingdan_id }}">订单:{{ item.guanliandingdan_id }}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- ✅ 按钮始终显示,只要列表不为空且没有正在加载 -->
|
||||
<view wx:if="{{ list.length > 0 && !loadingMore }}" class="load-more-btn" bindtap="loadMore">
|
||||
<text>点击获取更多</text>
|
||||
</view>
|
||||
<view wx:if="{{ loadingMore }}" class="tip">加载中...</view>
|
||||
<view wx:if="{{ !hasMore && list.length > 0 }}" class="tip">—— 没有更多了 ——</view>
|
||||
<view wx:if="{{ !loading && list.length === 0 }}" class="empty">暂无罚款记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<view wx:if="{{ showDetail }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">罚款详情</text>
|
||||
<view class="modal-close" bindtap="closeDetail">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="row"><text class="lbl">罚款金额</text><text class="val strong">¥{{ detailItem.fakuanjine }}</text></view>
|
||||
<view class="row"><text class="lbl">当前状态</text><text class="tag {{ detailItem.status_class }}">{{ detailItem.display_status }}</text></view>
|
||||
<view class="row"><text class="lbl">处罚理由</text><text class="val">{{ detailItem.chufaliyou }}</text></view>
|
||||
<view wx:if="{{ detailItem.guanliandingdan_id }}" class="row"><text class="lbl">关联订单</text><text class="val">{{ detailItem.guanliandingdan_id }}</text></view>
|
||||
<view wx:if="{{ detailItem.bohuiliyou }}" class="row"><text class="lbl">驳回理由</text><text class="val">{{ detailItem.bohuiliyou }}</text></view>
|
||||
<view wx:if="{{ detailItem.shensuliyou }}" class="row"><text class="lbl">申诉理由</text><text class="val">{{ detailItem.shensuliyou }}</text></view>
|
||||
|
||||
<!-- 证据图片 -->
|
||||
<view wx:if="{{ detailItem.zhengju_tupian && detailItem.zhengju_tupian.length }}" class="section">
|
||||
<text class="sec-title">证据图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.zhengju_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ ossImageUrl + item }}">
|
||||
<image class="img" src="{{ ossImageUrl + item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 申诉图片 -->
|
||||
<view wx:if="{{ detailItem.shensu_tupian && detailItem.shensu_tupian.length }}" class="section">
|
||||
<text class="sec-title">申诉图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.shensu_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ ossImageUrl + item }}">
|
||||
<image class="img" src="{{ ossImageUrl + item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</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>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉弹窗 -->
|
||||
<view wx:if="{{ showShensu }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">提交申诉</text>
|
||||
<view class="modal-close" bindtap="closeShensu">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="form-group">
|
||||
<text class="sec-title">申诉理由</text>
|
||||
<textarea class="textarea" placeholder="请输入申诉理由" value="{{ shensuLiyou }}" bindinput="onShensuInput" maxlength="500" />
|
||||
<text class="word-count">{{ shensuLiyou.length }}/500</text>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="sec-title">上传凭证(最多9张)</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ shensuTupian }}" wx:key="index">
|
||||
<view class="img-box">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" bindtap="previewLocalImg" data-index="{{ index }}" />
|
||||
<view class="img-del" catchtap="deleteShensuImg" data-index="{{ index }}">✕</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ shensuTupian.length < 9 }}" class="img-add" bindtap="chooseImage">+</view>
|
||||
</view>
|
||||
<view wx:if="{{ uploading }}" class="progress-box">
|
||||
<view class="progress-bar"><view class="progress-inner" style="width: {{ uploadProgressWidth }}"></view></view>
|
||||
<text class="progress-text">{{ uploadProgress.current }}/{{ uploadProgress.total }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="btn default" bindtap="closeShensu">取消</view>
|
||||
<view class="btn primary" bindtap="submitShensu">提交申诉</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付组件(缴纳罚款) -->
|
||||
<fakuan-pay
|
||||
visible="{{ showPay }}"
|
||||
fadan="{{ payFadan }}"
|
||||
bind:close="onPayClose"
|
||||
bind:success="onPaySuccess"
|
||||
/>
|
||||
</view>
|
||||
@@ -0,0 +1,77 @@
|
||||
/* 完全复用积分组件的样式 */
|
||||
page { background: #f5f5f5; }
|
||||
|
||||
.container { width: 100%; box-sizing: border-box; display: flex; flex-direction: column; }
|
||||
|
||||
.scroll { width: 100%; box-sizing: border-box; padding: 20rpx 24rpx; }
|
||||
|
||||
.tip { text-align: center; color: #999; font-size: 24rpx; padding: 30rpx 0; }
|
||||
.empty { display: flex; flex-direction: column; align-items: center; padding-top: 200rpx; color: #999; font-size: 28rpx; }
|
||||
|
||||
.card { width: 100%; box-sizing: border-box; background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
|
||||
.time { font-size: 24rpx; color: #999; }
|
||||
.tag { font-size: 20rpx; padding: 2rpx 14rpx; border-radius: 16rpx; display: inline-block; }
|
||||
|
||||
/* 状态标签颜色(与积分组件完全相同) */
|
||||
.zhuangtai-daijiaona { background: #FFF3E0; color: #E65100; } /* 待缴纳 */
|
||||
.zhuangtai-yijiaona { background: #E8F5E9; color: #2E7D32; } /* 已缴纳 */
|
||||
.zhuangtai-shensuzhong { background: #E3F2FD; color: #1565C0; } /* 申诉中 */
|
||||
.zhuangtai-yibohui { background: #F3E5F5; color: #7B1FA2; } /* 申诉成功 */
|
||||
|
||||
.info { display: block; font-size: 26rpx; color: #333; margin-top: 8rpx; }
|
||||
|
||||
.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 999; display: flex; align-items: flex-end; justify-content: center; }
|
||||
.modal-panel { width: 100%; box-sizing: border-box; max-height: 85vh; background: #fff; border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden; }
|
||||
.modal-head { display: flex; justify-content: space-between; align-items: center; padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee; }
|
||||
.modal-title { font-size: 32rpx; font-weight: 600; }
|
||||
.modal-close { width: 44rpx; height: 44rpx; border-radius: 50%; background: #f5f5f5; display: flex; align-items: center; justify-content: center; font-size: 26rpx; color: #888; }
|
||||
|
||||
.modal-body { width: 100%; box-sizing: border-box; padding: 20rpx 30rpx; overflow-y: auto; }
|
||||
.row { display: flex; margin-bottom: 16rpx; }
|
||||
.lbl { width: 150rpx; font-size: 26rpx; color: #888; flex-shrink: 0; }
|
||||
.val { flex: 1; font-size: 26rpx; color: #333; word-break: break-all; }
|
||||
.strong { font-weight: 600; color: #E53935; }
|
||||
.section { margin: 20rpx 0; }
|
||||
.sec-title { font-size: 26rpx; color: #888; margin-bottom: 12rpx; display: block; }
|
||||
|
||||
.img-grid { width: 100%; box-sizing: border-box; display: flex; flex-wrap: wrap; gap: 12rpx; }
|
||||
.img-box { position: relative; width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); }
|
||||
.img-box .img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 10rpx; background: #f0f0f0; }
|
||||
.img-del { position: absolute; top: -6rpx; right: -6rpx; width: 34rpx; height: 34rpx; background: #e53935; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 18rpx; z-index: 2; }
|
||||
.img-add { width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); border: 2rpx dashed #ccc; border-radius: 10rpx; display: flex; align-items: center; justify-content: center; font-size: 48rpx; color: #aaa; position: relative; box-sizing: border-box; }
|
||||
|
||||
.form-group { margin-bottom: 24rpx; }
|
||||
.textarea { width: 100%; box-sizing: border-box; height: 180rpx; background: #f9f9f9; border-radius: 10rpx; padding: 16rpx; font-size: 26rpx; border: 1px solid #eee; }
|
||||
.word-count { text-align: right; font-size: 22rpx; color: #bbb; margin-top: 6rpx; }
|
||||
.progress-box { margin-top: 12rpx; }
|
||||
.progress-bar { height: 10rpx; background: #eee; border-radius: 5rpx; }
|
||||
.progress-inner { height: 100%; background: #2e7d32; border-radius: 5rpx; }
|
||||
.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; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
|
||||
|
||||
/* ===== 获取更多按钮 ===== */
|
||||
.load-more-btn {
|
||||
width: 80%;
|
||||
margin: 20rpx auto 30rpx;
|
||||
padding: 22rpx 0;
|
||||
text-align: center;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
border-radius: 20rpx;
|
||||
color: #555;
|
||||
font-size: 26rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.load-more-btn:active {
|
||||
background: #f5f5f5;
|
||||
border-color: #2e7d32;
|
||||
color: #2e7d32;
|
||||
}
|
||||
178
miniprogram/pages/cfss/components/fakuan-pay/fakuan-pay.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// components/fakuan-list/fakuan-pay/fakuan-pay.js
|
||||
import request from '../../../../utils/request.js'; // 路径保持您原来的样子
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
visible: { type: Boolean, value: false, observer: 'onVisibleChange' },
|
||||
fadan: { type: Object, value: null } // { id, fakuanjine }
|
||||
},
|
||||
|
||||
data: {
|
||||
step: 'method', // method / balance / confirm / loading
|
||||
balanceOptions: [],
|
||||
selectedBalance: null,
|
||||
loadingText: '',
|
||||
payLoading: false,
|
||||
dingdanid: '' // 微信支付内部订单ID
|
||||
},
|
||||
|
||||
methods: {
|
||||
onVisibleChange(val) {
|
||||
if (val) {
|
||||
// 每次打开重置为支付方式选择
|
||||
this.setData({ step: 'method', balanceOptions: [], selectedBalance: null });
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
this.setData({ visible: false });
|
||||
this.triggerEvent('close');
|
||||
},
|
||||
|
||||
// 选择支付方式
|
||||
async chooseMethod(e) {
|
||||
const method = e.currentTarget.dataset.method;
|
||||
if (method === 'wx') {
|
||||
this.wxPay();
|
||||
} else if (method === 'balance') {
|
||||
this.fetchBalanceOptions();
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 微信支付流程 ==========
|
||||
async wxPay() {
|
||||
this.setData({ step: 'loading', loadingText: '发起支付中...' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/fkjn',
|
||||
method: 'POST',
|
||||
data: { fadan_id: this.properties.fadan.id }
|
||||
});
|
||||
if (res.data.code !== 200) throw new Error(res.data.msg || '下单失败');
|
||||
|
||||
// 🔥 唯一修改点:后端返回格式 { code:200, payParams:{...}, dingdanid:'...' }
|
||||
const payParams = res.data.payParams;
|
||||
const dingdanid = res.data.dingdanid;
|
||||
|
||||
this.setData({ dingdanid });
|
||||
await this.requestWxPayment(payParams);
|
||||
} catch (e) {
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
async requestWxPayment(payParams) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!payParams || !payParams.timeStamp || !payParams.paySign) {
|
||||
wx.showToast({ title: '支付参数错误', icon: 'none' });
|
||||
reject(new Error('支付参数错误'));
|
||||
return;
|
||||
}
|
||||
wx.requestPayment({
|
||||
timeStamp: payParams.timeStamp,
|
||||
nonceStr: payParams.nonceStr,
|
||||
package: payParams.package,
|
||||
signType: payParams.signType || 'MD5',
|
||||
paySign: payParams.paySign,
|
||||
success: () => {
|
||||
this.pollPayStatus();
|
||||
resolve();
|
||||
},
|
||||
fail: (err) => {
|
||||
this.setData({ step: 'method' });
|
||||
this.notifyPayFail();
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async pollPayStatus() {
|
||||
this.setData({ step: 'loading', loadingText: '确认支付结果...' });
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/fkzffc',
|
||||
method: 'POST',
|
||||
data: { dingdanid: this.data.dingdanid }
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
this.triggerEvent('success');
|
||||
this.close();
|
||||
wx.showToast({ title: '缴纳成功', icon: 'success' });
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
}
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: '支付确认超时', icon: 'none' });
|
||||
},
|
||||
|
||||
async notifyPayFail() {
|
||||
try {
|
||||
await request({
|
||||
url: '/yonghu/fkzfsb',
|
||||
method: 'POST',
|
||||
data: { dingdanid: this.data.dingdanid }
|
||||
});
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// ========== 佣金抵扣流程 ==========
|
||||
async fetchBalanceOptions() {
|
||||
this.setData({ step: 'loading', loadingText: '获取可抵扣身份...' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/czhqdy',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: 4,
|
||||
fadan_id: this.properties.fadan.id,
|
||||
jine: this.properties.fadan.fakuanjine
|
||||
}
|
||||
});
|
||||
if (res.data.code !== 200) throw new Error(res.data.msg || '获取失败');
|
||||
this.setData({ balanceOptions: res.data.data, step: 'balance' });
|
||||
} catch (e) {
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
selectBalance(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const info = this.data.balanceOptions.find(opt => opt.id === id);
|
||||
if (!info) return;
|
||||
this.setData({ selectedBalance: info, step: 'confirm' });
|
||||
},
|
||||
|
||||
backToMethod() {
|
||||
this.setData({ step: 'method' });
|
||||
},
|
||||
|
||||
async confirmBalance() {
|
||||
if (!this.data.selectedBalance) return;
|
||||
this.setData({ step: 'loading', loadingText: '抵扣扣款中...' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/dsqrgmdh',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: 4,
|
||||
shenfen_id: this.data.selectedBalance.id,
|
||||
fadan_id: this.properties.fadan.id
|
||||
}
|
||||
});
|
||||
if (res.data.code !== 200) throw new Error(res.data.msg || '抵扣失败');
|
||||
this.triggerEvent('success');
|
||||
this.close();
|
||||
wx.showToast({ title: '缴纳成功', icon: 'success' });
|
||||
} catch (e) {
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
61
miniprogram/pages/cfss/components/fakuan-pay/fakuan-pay.wxml
Normal file
@@ -0,0 +1,61 @@
|
||||
<!-- 支付组件模板 -->
|
||||
<view wx:if="{{ visible }}" class="pay-mask" catchtouchmove="prevent">
|
||||
<!-- 支付方式选择 -->
|
||||
<view wx:if="{{ step === 'method' }}" class="pay-panel">
|
||||
<view class="pay-head">
|
||||
<text class="pay-title">选择支付方式</text>
|
||||
<view class="pay-close" bindtap="close">✕</view>
|
||||
</view>
|
||||
<view class="pay-body">
|
||||
<view class="method-item" bindtap="chooseMethod" data-method="wx">
|
||||
<text class="method-icon">💳</text>
|
||||
<text class="method-name">微信支付</text>
|
||||
</view>
|
||||
<view class="method-item" bindtap="chooseMethod" data-method="balance">
|
||||
<text class="method-icon">💰</text>
|
||||
<text class="method-name">佣金抵扣</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 余额抵扣身份选择 -->
|
||||
<view wx:if="{{ step === 'balance' }}" class="pay-panel">
|
||||
<view class="pay-head">
|
||||
<text class="pay-title">选择抵扣身份</text>
|
||||
<view class="pay-close" bindtap="close">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="pay-body">
|
||||
<view wx:for="{{ balanceOptions }}" wx:key="id" class="balance-item" bindtap="selectBalance" data-id="{{ item.id }}">
|
||||
<text class="balance-name">{{ item.jieshao }}</text>
|
||||
<text class="balance-amount">需¥{{ item.xuyao }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 余额抵扣确认 -->
|
||||
<view wx:if="{{ step === 'confirm' }}" class="pay-panel">
|
||||
<view class="pay-head">
|
||||
<text class="pay-title">确认抵扣</text>
|
||||
<view class="pay-close" bindtap="close">✕</view>
|
||||
</view>
|
||||
<view class="pay-body">
|
||||
<view class="confirm-row">
|
||||
<text>抵扣身份:{{ selectedBalance.jieshao }}</text>
|
||||
</view>
|
||||
<view class="confirm-row">
|
||||
<text>需扣除:¥{{ selectedBalance.xuyao }}</text>
|
||||
</view>
|
||||
<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>
|
||||
</view>
|
||||
|
||||
<!-- 支付加载动画 -->
|
||||
<view wx:if="{{ step === 'loading' }}" class="pay-mask loading-box">
|
||||
<view class="spinner"></view>
|
||||
<text class="loading-text">{{ loadingText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
59
miniprogram/pages/cfss/components/fakuan-pay/fakuan-pay.wxss
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 支付组件样式,与罚款列表统一 */
|
||||
.pay-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5); z-index: 2000;
|
||||
display: flex; align-items: flex-end; justify-content: center;
|
||||
}
|
||||
.pay-panel {
|
||||
width: 100%; max-height: 85vh; background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden;
|
||||
}
|
||||
.pay-head {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee;
|
||||
}
|
||||
.pay-title { font-size: 32rpx; font-weight: 600; }
|
||||
.pay-close {
|
||||
width: 44rpx; height: 44rpx; background: #f5f5f5;
|
||||
border-radius: 50%; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 26rpx; color: #888;
|
||||
}
|
||||
.pay-body { padding: 20rpx 30rpx; flex: 1; overflow-y: auto; }
|
||||
.pay-foot {
|
||||
display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.method-item {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
padding: 40rpx 30rpx; background: #f9f9f9; border-radius: 20rpx; margin-bottom: 20rpx;
|
||||
}
|
||||
.method-icon { font-size: 48rpx; margin-bottom: 12rpx; }
|
||||
.method-name { font-size: 28rpx; color: #333; }
|
||||
|
||||
.balance-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 24rpx 0; border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.balance-name { font-size: 28rpx; color: #333; }
|
||||
.balance-amount { font-size: 28rpx; color: #E53935; }
|
||||
|
||||
.confirm-row { font-size: 28rpx; margin-bottom: 16rpx; }
|
||||
.confirm-tip { font-size: 24rpx; color: #888; display: block; margin-top: 20rpx; }
|
||||
|
||||
.loading-box {
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
.spinner {
|
||||
width: 60rpx; height: 60rpx;
|
||||
border: 4rpx solid rgba(255,255,255,0.3);
|
||||
border-top-color: #fff; border-radius: 50%;
|
||||
animation: spin 1s linear infinite; margin-bottom: 20rpx;
|
||||
}
|
||||
@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; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
220
miniprogram/pages/cfss/components/jifen-list/jifen-list.js
Normal file
@@ -0,0 +1,220 @@
|
||||
// components/jifen-list/jifen-list.js
|
||||
import request from '../../../../utils/request.js';
|
||||
const app = getApp();
|
||||
const MEIYE_TIAOSHU = 5;
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
status: { type: Number, value: 0, observer: 'reload' },
|
||||
trigger: { type: Number, value: 0, observer: 'reload' }
|
||||
},
|
||||
|
||||
data: {
|
||||
chufaList: [],
|
||||
dangqianye: 1,
|
||||
haiyougengduo: true,
|
||||
jiazhaozhong: false,
|
||||
jiazhaigengduo: false,
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
|
||||
showXiangqing: false,
|
||||
xuanzhongChufa: {},
|
||||
|
||||
showShensuModal: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
shangchuanJindu: 0,
|
||||
shangchuanZongshu: 0,
|
||||
jinduWidth: '0%',
|
||||
isShangchuanzhong: false,
|
||||
fangdouTimer: null
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() { this.reload(); },
|
||||
detached() { if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer); }
|
||||
},
|
||||
|
||||
methods: {
|
||||
reload() {
|
||||
this.setData({ dangqianye: 1, chufaList: [], haiyougengduo: true });
|
||||
this.loadJifenList();
|
||||
},
|
||||
|
||||
async loadJifenList(isLoadMore = false) {
|
||||
if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return;
|
||||
if (isLoadMore && !this.data.haiyougengduo) return;
|
||||
if (isLoadMore) this.setData({ jiazhaigengduo: true });
|
||||
else this.setData({ jiazhaozhong: true });
|
||||
|
||||
try {
|
||||
const params = { page: this.data.dangqianye, page_size: MEIYE_TIAOSHU, zhuangtai: this.data.status };
|
||||
const res = await request({ url: '/yonghu/dshqcfjl', method: 'POST', data: params });
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
if (data.list && Array.isArray(data.list)) {
|
||||
const processedList = data.list.map(item => {
|
||||
let displayTime = '--';
|
||||
if (item.create_time) displayTime = item.create_time;
|
||||
let displayStatus = '未知状态', statusClass = 'zhuangtai-weizhi';
|
||||
const n = Number(item.sqzhuangtai);
|
||||
if (!isNaN(n)) {
|
||||
if (n === 0) { displayStatus = '待处理'; statusClass = 'zhuangtai-daichuli'; }
|
||||
else if (n === 1) { displayStatus = '已处罚'; statusClass = 'zhuangtai-yichufa'; }
|
||||
else if (n === 2) { displayStatus = '处罚已撤销'; statusClass = 'zhuangtai-yibohui'; }
|
||||
else if (n === 3) { displayStatus = '申诉中'; statusClass = 'zhuangtai-shensuzhong'; }
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
display_time: displayTime, display_status: displayStatus, status_class: statusClass,
|
||||
full_zhengju_tupian: (item.zhengju_tupian || []).map(url => this.getFullImageUrl(url)),
|
||||
full_shensu_tupian: (item.shensu_tupian || []).map(url => this.getFullImageUrl(url))
|
||||
};
|
||||
});
|
||||
const newList = isLoadMore ? [...this.data.chufaList, ...processedList] : processedList;
|
||||
this.setData({ chufaList: newList, haiyougengduo: data.has_more === true, dangqianye: this.data.dangqianye + 1 });
|
||||
} else this.setData({ haiyougengduo: false });
|
||||
}
|
||||
} catch (e) { wx.showToast({ title: '加载失败', icon: 'none' }); this.setData({ haiyougengduo: false }); }
|
||||
finally { if (isLoadMore) this.setData({ jiazhaigengduo: false }); else this.setData({ jiazhaozhong: false }); }
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer);
|
||||
const timer = setTimeout(() => {
|
||||
if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return;
|
||||
if (!this.data.haiyougengduo) return;
|
||||
this.loadJifenList(true);
|
||||
}, 300);
|
||||
this.setData({ fangdouTimer: timer });
|
||||
},
|
||||
|
||||
chakanXiangqing(e) { this.setData({ showXiangqing: true, xuanzhongChufa: e.currentTarget.dataset.item }); },
|
||||
guanbiXiangqing() { this.setData({ showXiangqing: false, xuanzhongChufa: {} }); },
|
||||
|
||||
openShensuModal() {
|
||||
const { xuanzhongChufa } = this.data;
|
||||
if (xuanzhongChufa.ssliyou || (xuanzhongChufa.shensu_tupian && xuanzhongChufa.shensu_tupian.length > 0)) {
|
||||
wx.showToast({ title: '您已经提交过申诉,请等待处理结果', icon: 'none' }); return;
|
||||
}
|
||||
this.setData({ showShensuModal: true, shensuLiyou: '', shensuTupian: [], shangchuanJindu: 0, shangchuanZongshu: 0, jinduWidth: '0%', isShangchuanzhong: false });
|
||||
},
|
||||
closeShensuModal() { this.setData({ showShensuModal: false }); },
|
||||
|
||||
onShensuLiyouInput(e) { this.setData({ shensuLiyou: e.detail.value.slice(0, 500) }); },
|
||||
|
||||
chooseShensuTupian() {
|
||||
if (this.data.isShangchuanzhong) return;
|
||||
const shengyu = 9 - this.data.shensuTupian.length;
|
||||
if (shengyu <= 0) { wx.showToast({ title: '最多只能上传9张图片', icon: 'none' }); return; }
|
||||
wx.chooseMedia({ count: shengyu, mediaType: ['image'], sourceType: ['album', 'camera'], success: (res) => {
|
||||
const newTupian = [...this.data.shensuTupian, ...res.tempFiles.map(f => f.tempFilePath)];
|
||||
this.setData({ shensuTupian: newTupian.slice(0, 9) });
|
||||
}});
|
||||
},
|
||||
deleteShensuTupian(e) { const arr = [...this.data.shensuTupian]; arr.splice(e.currentTarget.dataset.index, 1); this.setData({ shensuTupian: arr }); },
|
||||
yulanShensuTupian(e) {
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
const urls = this.data.shensuTupian;
|
||||
if (!urls[idx]) return;
|
||||
wx.previewImage({ current: urls[idx], urls });
|
||||
},
|
||||
yulanTupian(e) {
|
||||
const current = e.currentTarget.dataset.url;
|
||||
const urls = e.currentTarget.dataset.urls || [];
|
||||
const fullCurrent = current.startsWith('http') ? current : this.getFullImageUrl(current);
|
||||
const fullUrls = urls.map(url => url.startsWith('http') ? url : this.getFullImageUrl(url));
|
||||
wx.previewImage({ current: fullCurrent, urls: fullUrls.length ? fullUrls : [fullCurrent] });
|
||||
},
|
||||
|
||||
/* ========== 图片上传(完全照抄原 cfss.js) ========== */
|
||||
// 【仅替换此方法,其余代码一个字别动】
|
||||
getFullImageUrl(relativeUrl) {
|
||||
if (!relativeUrl) return '';
|
||||
if (relativeUrl.startsWith('http')) return relativeUrl;
|
||||
const ossUrl = this.data.ossImageUrl;
|
||||
if (!ossUrl) return relativeUrl;
|
||||
// 确保拼接时只有一个斜杠
|
||||
const base = ossUrl.replace(/\/+$/, '');
|
||||
const rel = relativeUrl.replace(/^\/+/, '');
|
||||
return base + '/' + rel;
|
||||
},
|
||||
getFileExtension(filePath) {
|
||||
const lastDot = filePath.lastIndexOf('.'); if (lastDot === -1) return '.jpg';
|
||||
const ext = filePath.substring(lastDot).toLowerCase();
|
||||
return ['.jpg','.jpeg','.png','.gif','.bmp','.webp'].includes(ext) ? ext : '.jpg';
|
||||
},
|
||||
async getCOSZhengshu() {
|
||||
const res = await request({ url: '/dingdan/dsscpz', method: 'POST', data: { dingdan_id: this.data.xuanzhongChufa.dingdan_id || '', yongtu: 'chufa' } });
|
||||
if (res.statusCode === 200 && res.data.code === 0 && res.data.data) return res.data.data;
|
||||
throw new Error(res?.data?.msg || '获取上传凭证失败');
|
||||
},
|
||||
initCOSClient(tokenData) {
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.js');
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063';
|
||||
const region = tokenData.region || 'ap-shanghai';
|
||||
const cos = new COS({ SimpleUploadMethod: 'putObject', getAuthorization: (options, callback) => { callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, SecurityToken: credentials.sessionToken || '', StartTime: tokenData.startTime || Math.floor(Date.now() / 1000), ExpiredTime: tokenData.expiredTime || (Math.floor(Date.now() / 1000) + 7200) }); } });
|
||||
return { cos, bucket, region };
|
||||
},
|
||||
shangchuanDanZhangTupian(filePath, cosKey, cosClient, bucket, region) {
|
||||
return new Promise(resolve => {
|
||||
const timeoutId = setTimeout(() => resolve({ status: 'optimistic_success', key: cosKey }), 2000);
|
||||
cosClient.uploadFile({ Bucket: bucket, Region: region, Key: cosKey, FilePath: filePath, onProgress: (info) => { if (Math.round(info.percent * 100) === 100) { clearTimeout(timeoutId); resolve({ status: 'optimistic_success', key: cosKey }); } } });
|
||||
});
|
||||
},
|
||||
async piliangShangchuanShensuTupian() {
|
||||
const total = this.data.shensuTupian.length;
|
||||
if (total === 0) return [];
|
||||
this.setData({ shangchuanZongshu: total, shangchuanJindu: 0, jinduWidth: '0%', isShangchuanzhong: true });
|
||||
|
||||
let yonghuid = '';
|
||||
const uid = wx.getStorageSync('uid'); if (uid) yonghuid = String(uid).padStart(7, '0');
|
||||
else if (app.globalData.userInfo?.yonghuid) yonghuid = String(app.globalData.userInfo.yonghuid).padStart(7, '0');
|
||||
if (!yonghuid) { wx.showToast({ title: '请先登录', icon: 'none' }); this.setData({ isShangchuanzhong: false }); return []; }
|
||||
|
||||
const preGeneratedUrls = [];
|
||||
for (let i = 0; i < total; i++) {
|
||||
const timestamp = Date.now() + i, random = Math.floor(Math.random() * 10000);
|
||||
preGeneratedUrls.push(`a_long/chfajltp/dssstp/${yonghuid}_${timestamp}_${random}${this.getFileExtension(this.data.shensuTupian[i])}`);
|
||||
}
|
||||
|
||||
let cosClient, bucket, region;
|
||||
try { const tokenData = await this.getCOSZhengshu(); const c = this.initCOSClient(tokenData); cosClient = c.cos; bucket = c.bucket; region = c.region; }
|
||||
catch (e) { wx.showToast({ title: '上传初始化失败', icon: 'none' }); this.setData({ isShangchuanzhong: false }); return []; }
|
||||
|
||||
for (let i = 0; i < total; i++) {
|
||||
await this.shangchuanDanZhangTupian(this.data.shensuTupian[i], preGeneratedUrls[i], cosClient, bucket, region);
|
||||
const done = i + 1;
|
||||
this.setData({ shangchuanJindu: done, jinduWidth: `${((done / total) * 100).toFixed(0)}%` });
|
||||
}
|
||||
this.setData({ isShangchuanzhong: false });
|
||||
return preGeneratedUrls;
|
||||
},
|
||||
|
||||
async submitShensu() {
|
||||
if (!this.data.shensuLiyou.trim()) { wx.showToast({ title: '请输入申诉理由', icon: 'none' }); return; }
|
||||
if (this.data.shensuTupian.length === 0) { wx.showModal({ title: '提示', content: '未上传任何图片,确定要提交申诉吗?', success: async (res) => { if (res.confirm) await this.tijiaoShensuData([]); } }); return; }
|
||||
wx.showToast({ title: '开始上传图片...', icon: 'none' });
|
||||
try {
|
||||
const urls = await this.piliangShangchuanShensuTupian();
|
||||
if (!urls || urls.length === 0) throw new Error('图片上传失败');
|
||||
await this.tijiaoShensuData(urls);
|
||||
} catch (e) { wx.showToast({ title: e.message || '提交失败', icon: 'none' }); }
|
||||
},
|
||||
async tijiaoShensuData(tupianUrls) {
|
||||
wx.showToast({ title: '提交申诉中...', icon: 'none' });
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/dscfss', method: 'POST', data: { chufa_id: this.data.xuanzhongChufa.id, shensu_liyou: this.data.shensuLiyou, shensu_tupian_urls: tupianUrls } });
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const newList = this.data.chufaList.map(item => {
|
||||
if (item.id === this.data.xuanzhongChufa.id) { return { ...item, sqzhuangtai: 3, ssliyou: this.data.shensuLiyou, display_status: '申诉中', status_class: 'zhuangtai-shensuzhong', full_shensu_tupian: tupianUrls.map(url => this.getFullImageUrl(url)) }; }
|
||||
return item;
|
||||
});
|
||||
this.setData({ chufaList: newList, showShensuModal: false, showXiangqing: false });
|
||||
wx.showToast({ title: '申诉提交成功,请等待处理', icon: 'success' });
|
||||
} else throw new Error(res.data?.msg || '提交申诉失败');
|
||||
} catch (e) { wx.showToast({ title: e.message || '提交失败', icon: 'none' }); }
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
105
miniprogram/pages/cfss/components/jifen-list/jifen-list.wxml
Normal file
@@ -0,0 +1,105 @@
|
||||
<view class="container">
|
||||
<scroll-view scroll-y class="scroll" bindscrolltolower="onReachBottom">
|
||||
<view wx:if="{{ jiazhaozhong && chufaList.length === 0 }}" class="tip">加载中...</view>
|
||||
<block wx:for="{{ chufaList }}" wx:key="id">
|
||||
<view class="card" bindtap="chakanXiangqing" data-item="{{ item }}">
|
||||
<view class="card-head">
|
||||
<text class="time">{{ item.display_time }}</text>
|
||||
<text class="tag {{ item.status_class }}">{{ item.display_status }}</text>
|
||||
</view>
|
||||
<text class="info">申请人:{{ item.qingqiuid || '--' }}</text>
|
||||
<text class="info">理由:{{ item.cfliyou || '无' }}</text>
|
||||
<text class="info" wx:if="{{ item.dingdan_id }}">订单:{{ item.dingdan_id }}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- ========== 底部:按钮 / 加载中 / 没有更多 ========== -->
|
||||
<view wx:if="{{ haiyougengduo && !jiazhaigengduo }}" class="load-more-btn" bindtap="onReachBottom">
|
||||
<text>点击获取更多</text>
|
||||
</view>
|
||||
<view wx:if="{{ jiazhaigengduo }}" class="tip">加载中...</view>
|
||||
<view wx:if="{{ !haiyougengduo && chufaList.length > 0 }}" class="tip">—— 没有更多了 ——</view>
|
||||
<view wx:if="{{ !jiazhaozhong && chufaList.length === 0 }}" class="empty">暂无积分处罚记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 详情弹窗(不动) -->
|
||||
<view wx:if="{{ showXiangqing }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">处罚详情</text>
|
||||
<view class="modal-close" bindtap="guanbiXiangqing">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="row"><text class="lbl">订单编号</text><text class="val">{{ xuanzhongChufa.dingdan_id || '--' }}</text></view>
|
||||
<view class="row"><text class="lbl">申请人ID</text><text class="val">{{ xuanzhongChufa.qingqiuid || '--' }}</text></view>
|
||||
<view class="row"><text class="lbl">时间</text><text class="val">{{ xuanzhongChufa.display_time }}</text></view>
|
||||
<view class="row"><text class="lbl">状态</text><text class="val"><text class="tag {{ xuanzhongChufa.status_class }}">{{ xuanzhongChufa.display_status }}</text></text></view>
|
||||
<view class="row"><text class="lbl">处罚理由</text><text class="val">{{ xuanzhongChufa.cfliyou || '无' }}</text></view>
|
||||
|
||||
<view wx:if="{{ xuanzhongChufa.full_zhengju_tupian.length }}" class="section">
|
||||
<text class="sec-title">证据图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ xuanzhongChufa.full_zhengju_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_zhengju_tupian }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{ xuanzhongChufa.ssliyou || xuanzhongChufa.full_shensu_tupian.length }}" class="section">
|
||||
<text class="sec-title">我的申诉</text>
|
||||
<view wx:if="{{ xuanzhongChufa.ssliyou }}" class="row"><text class="lbl">理由</text><text class="val">{{ xuanzhongChufa.ssliyou }}</text></view>
|
||||
<view wx:if="{{ xuanzhongChufa.full_shensu_tupian.length }}" class="img-grid">
|
||||
<block wx:for="{{ xuanzhongChufa.full_shensu_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_shensu_tupian }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</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>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉弹窗(不动) -->
|
||||
<view wx:if="{{ showShensuModal }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">提交申诉</text>
|
||||
<view class="modal-close" bindtap="closeShensuModal">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="form-group">
|
||||
<text class="sec-title">申诉理由</text>
|
||||
<textarea class="textarea" placeholder="请输入申诉理由(最多500字)" value="{{ shensuLiyou }}" bindinput="onShensuLiyouInput" maxlength="500" />
|
||||
<text class="word-count">{{ shensuLiyou.length }}/500</text>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="sec-title">上传凭证(最多9张)</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ shensuTupian }}" wx:key="index">
|
||||
<view class="img-box">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" bindtap="yulanShensuTupian" data-index="{{ index }}" />
|
||||
<view class="img-del" catchtap="deleteShensuTupian" data-index="{{ index }}">✕</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ shensuTupian.length < 9 }}" class="img-add" bindtap="chooseShensuTupian">+</view>
|
||||
</view>
|
||||
<view wx:if="{{ shangchuanZongshu > 0 }}" class="progress-box">
|
||||
<view class="progress-bar"><view class="progress-inner" style="width: {{ jinduWidth }}"></view></view>
|
||||
<text class="progress-text">{{ shangchuanJindu }}/{{ shangchuanZongshu }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="btn default" bindtap="closeShensuModal">取消</view>
|
||||
<view class="btn primary" bindtap="submitShensu">提交申诉</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
86
miniprogram/pages/cfss/components/jifen-list/jifen-list.wxss
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 页面与容器 */
|
||||
page { background: #f5f5f5; }
|
||||
|
||||
/* 🔥 组件自身必须占满宽度,且 padding 左右严格相等 */
|
||||
.container { width: 100%; box-sizing: border-box; display: flex; flex-direction: column; }
|
||||
|
||||
/* 🔥 滚动区 padding 左右必须相等,且容器用 box-sizing */
|
||||
.scroll { width: 100%; box-sizing: border-box; padding: 20rpx 24rpx; }
|
||||
|
||||
/* 提示文字 */
|
||||
.tip { text-align: center; color: #999; font-size: 24rpx; padding: 30rpx 0; }
|
||||
.empty { display: flex; flex-direction: column; align-items: center; padding-top: 200rpx; color: #999; font-size: 28rpx; }
|
||||
|
||||
/* 🔥 卡片 width:100% + box-sizing 使左右边距完全由父容器决定 */
|
||||
.card { width: 100%; box-sizing: border-box; background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
|
||||
.time { font-size: 24rpx; color: #999; }
|
||||
.tag { font-size: 20rpx; padding: 2rpx 14rpx; border-radius: 16rpx; display: inline-block; }
|
||||
.zhuangtai-daichuli { background: #fff3e0; color: #e65100; }
|
||||
.zhuangtai-yichufa { background: #e8f5e9; color: #2e7d32; }
|
||||
.zhuangtai-yibohui { background: #f3e5f5; color: #7b1fa2; }
|
||||
.zhuangtai-shensuzhong { background: #e3f2fd; color: #1565c0; }
|
||||
.zhuangtai-weizhi { background: #f0f0f0; color: #888; }
|
||||
.info { display: block; font-size: 26rpx; color: #333; margin-top: 8rpx; }
|
||||
|
||||
/* 弹窗遮罩 */
|
||||
.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 999; display: flex; align-items: flex-end; justify-content: center; }
|
||||
|
||||
/* 🔥 弹窗面板 width:100% + box-sizing */
|
||||
.modal-panel { width: 100%; box-sizing: border-box; max-height: 85vh; background: #fff; border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden; }
|
||||
.modal-head { display: flex; justify-content: space-between; align-items: center; padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee; }
|
||||
.modal-title { font-size: 32rpx; font-weight: 600; }
|
||||
.modal-close { width: 44rpx; height: 44rpx; border-radius: 50%; background: #f5f5f5; display: flex; align-items: center; justify-content: center; font-size: 26rpx; color: #888; }
|
||||
|
||||
/* 🔥 内容区左右 padding 严格相等 */
|
||||
.modal-body { width: 100%; box-sizing: border-box; padding: 20rpx 30rpx; overflow-y: auto; }
|
||||
.row { display: flex; margin-bottom: 16rpx; }
|
||||
.lbl { width: 150rpx; font-size: 26rpx; color: #888; flex-shrink: 0; }
|
||||
.val { flex: 1; font-size: 26rpx; color: #333; word-break: break-all; }
|
||||
.section { margin: 20rpx 0; }
|
||||
.sec-title { font-size: 26rpx; color: #888; margin-bottom: 12rpx; display: block; }
|
||||
|
||||
/* 🔥 图片网格:gap 均等,无任何额外水平偏移 */
|
||||
.img-grid { width: 100%; box-sizing: border-box; display: flex; flex-wrap: wrap; gap: 12rpx; }
|
||||
.img { width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); border-radius: 10rpx; background: #f0f0f0; display: block; position: relative; }
|
||||
.img-box { position: relative; width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); }
|
||||
.img-box .img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding-bottom: 0; }
|
||||
.img-del { position: absolute; top: -6rpx; right: -6rpx; width: 34rpx; height: 34rpx; background: #e53935; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 18rpx; z-index: 2; }
|
||||
.img-add { width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); border: 2rpx dashed #ccc; border-radius: 10rpx; display: flex; align-items: center; justify-content: center; font-size: 52rpx; color: #aaa; box-sizing: border-box; position: relative; }
|
||||
|
||||
/* 表单 */
|
||||
.form-group { margin-bottom: 24rpx; }
|
||||
.textarea { width: 100%; box-sizing: border-box; height: 180rpx; background: #f9f9f9; border-radius: 10rpx; padding: 16rpx; font-size: 26rpx; border: 1px solid #eee; }
|
||||
.word-count { text-align: right; font-size: 22rpx; color: #bbb; margin-top: 6rpx; }
|
||||
.progress-box { margin-top: 12rpx; }
|
||||
.progress-bar { height: 10rpx; background: #eee; border-radius: 5rpx; }
|
||||
.progress-inner { height: 100%; background: #2e7d32; border-radius: 5rpx; }
|
||||
.progress-text { font-size: 22rpx; color: #2e7d32; text-align: right; margin-top: 4rpx; }
|
||||
|
||||
/* 🔥 底部按钮区左右 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; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
|
||||
|
||||
/* ===== 获取更多按钮 ===== */
|
||||
.load-more-btn {
|
||||
width: 80%;
|
||||
margin: 20rpx auto 30rpx;
|
||||
padding: 22rpx 0;
|
||||
text-align: center;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
border-radius: 20rpx;
|
||||
color: #555;
|
||||
font-size: 26rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.load-more-btn:active {
|
||||
background: #f5f5f5;
|
||||
border-color: #2e7d32;
|
||||
color: #2e7d32;
|
||||
}
|
||||
288
miniprogram/pages/czjilu/czjilu.js
Normal file
@@ -0,0 +1,288 @@
|
||||
// 管事会员记录页面逻辑
|
||||
import request from '../../utils/request.js';
|
||||
|
||||
// 每页数据条数常量 - 统一管理,方便修改
|
||||
const MEIYE_TIAOSHU = 50;
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 从全局变量获取的数据
|
||||
yaoqingzongshu: 0, // 邀请打手总数
|
||||
fenyongzonge: '0.00', // 分成总额
|
||||
|
||||
// 从接口获取的数据
|
||||
chongzhiDashouShuliang: 0, // 充值打手数量
|
||||
dashouList: [], // 打手列表数据
|
||||
zongTiaoshu: 0, // 总条数
|
||||
|
||||
// 分页相关
|
||||
dangqianYe: 1, // 当前页
|
||||
meiyouGengduo: false, // 没有更多数据
|
||||
jiazaiZhong: false, // 加载中状态
|
||||
jiazaiGengduo: false, // 加载更多状态
|
||||
|
||||
// UI状态
|
||||
cuowuTishi: '', // 错误提示
|
||||
shuaxinKezhi: false, // 刷新按钮是否可点击
|
||||
shuaxinCd: 2000, // 刷新冷却时间(毫秒)
|
||||
|
||||
// 常量(用于模板)
|
||||
meiyeTiaoshu: MEIYE_TIAOSHU // 每页条数
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
// 从全局变量获取管事数据
|
||||
this.huoquQuanjubianliang();
|
||||
|
||||
// 加载第一页数据
|
||||
this.jiazaiShuju(true);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 页面显示时检查全局变量是否有更新
|
||||
this.registerNotificationComponent();
|
||||
this.huoquQuanjubianliang();
|
||||
},
|
||||
// 🆕 新增:注册通知组件
|
||||
registerNotificationComponent() {
|
||||
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()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 获取全局变量数据
|
||||
huoquQuanjubianliang() {
|
||||
const app = getApp();
|
||||
const guanshiData = app.globalData.guanshi;
|
||||
|
||||
if (guanshiData) {
|
||||
this.setData({
|
||||
yaoqingzongshu: guanshiData.yaoqingzongshu || 0,
|
||||
fenyongzonge: guanshiData.fenyongzonge || '0.00'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 加载数据核心方法
|
||||
async jiazaiShuju(chushihua = false) {
|
||||
// 防止重复请求
|
||||
if (this.data.jiazaiZhong) return;
|
||||
|
||||
// 如果是初始化加载,重置分页
|
||||
if (chushihua) {
|
||||
this.setData({
|
||||
dangqianYe: 1,
|
||||
meiyouGengduo: false,
|
||||
dashouList: [],
|
||||
zongTiaoshu: 0,
|
||||
chongzhiDashouShuliang: 0
|
||||
});
|
||||
} else {
|
||||
// 上拉加载更多时,检查是否还有更多数据
|
||||
if (this.data.meiyouGengduo) return;
|
||||
|
||||
// 如果有数据但不足一页,说明后端已经没数据了
|
||||
if (this.data.dashouList.length > 0 && this.data.dashouList.length < MEIYE_TIAOSHU) {
|
||||
this.setData({ meiyouGengduo: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果数据量已经达到总条数,则不请求
|
||||
if (this.data.dashouList.length >= this.data.zongTiaoshu && this.data.zongTiaoshu > 0) {
|
||||
this.setData({ meiyouGengduo: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置加载状态
|
||||
this.setData({
|
||||
jiazaiZhong: chushihua ? true : false,
|
||||
jiazaiGengduo: !chushihua ? true : false,
|
||||
cuowuTishi: ''
|
||||
});
|
||||
|
||||
try {
|
||||
const { dangqianYe } = this.data;
|
||||
|
||||
// 严格按照您的request规范调用
|
||||
const res = await request({
|
||||
url: '/shangpin/fenhonghq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
page: dangqianYe,
|
||||
page_size: MEIYE_TIAOSHU // 使用统一常量
|
||||
}
|
||||
});
|
||||
|
||||
// 处理返回数据
|
||||
if (res.data.code === 200) {
|
||||
const data = res.data.data || {};
|
||||
const newList = data.list || [];
|
||||
const total = data.total || 0;
|
||||
|
||||
// 处理头像URL拼接
|
||||
const processedList = this.chuliTouxiangURL(newList);
|
||||
|
||||
// 计算是否有更多数据
|
||||
const hasMoreData = processedList.length === MEIYE_TIAOSHU;
|
||||
|
||||
// 更新数据
|
||||
this.setData({
|
||||
dashouList: chushihua ? processedList : [...this.data.dashouList, ...processedList],
|
||||
zongTiaoshu: total,
|
||||
chongzhiDashouShuliang: data.chongzhi_dashou_shuliang || 0,
|
||||
meiyouGengduo: !hasMoreData,
|
||||
dangqianYe: chushihua ? 2 : this.data.dangqianYe + 1
|
||||
});
|
||||
|
||||
// 如果数据为空,显示空状态
|
||||
if (chushihua && processedList.length === 0) {
|
||||
wx.showToast({
|
||||
title: '暂无数据',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
|
||||
// 如果加载了数据,给个提示
|
||||
if (processedList.length > 0) {
|
||||
if (chushihua) {
|
||||
wx.showToast({
|
||||
title: `已加载${processedList.length}条记录`,
|
||||
icon: 'success',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.setData({
|
||||
cuowuTishi: res.data.msg || '数据加载失败'
|
||||
});
|
||||
wx.showToast({
|
||||
title: res.data.msg || '加载失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error);
|
||||
this.setData({
|
||||
cuowuTishi: '网络错误,请检查网络连接'
|
||||
});
|
||||
wx.showToast({
|
||||
title: '网络错误',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
} finally {
|
||||
this.setData({
|
||||
jiazaiZhong: false,
|
||||
jiazaiGengduo: false
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 处理头像URL拼接
|
||||
chuliTouxiangURL(list) {
|
||||
const app = getApp();
|
||||
const ossUrl = app.globalData.ossImageUrl || '';
|
||||
|
||||
return list.map(item => {
|
||||
// 确保有头像URL,如果没有则使用默认头像
|
||||
let touxiangUrl = item.avatar || '';
|
||||
if (touxiangUrl && !touxiangUrl.startsWith('http')) {
|
||||
touxiangUrl = ossUrl + touxiangUrl;
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
let shijian = item.create_time || '';
|
||||
if (shijian) {
|
||||
// 如果是时间戳,转换为日期格式
|
||||
if (/^\d+$/.test(shijian)) {
|
||||
const date = new Date(parseInt(shijian));
|
||||
shijian = `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
} else {
|
||||
// 如果是字符串,提取日期和时间
|
||||
try {
|
||||
const dateObj = new Date(shijian);
|
||||
shijian = `${dateObj.getMonth() + 1}月${dateObj.getDate()}日 ${dateObj.getHours().toString().padStart(2, '0')}:${dateObj.getMinutes().toString().padStart(2, '0')}`;
|
||||
} catch (e) {
|
||||
shijian = shijian.substring(0, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
touxiangUrl: touxiangUrl || '/images/default-avatar.png',
|
||||
shijian: shijian,
|
||||
fenhong: parseFloat(item.fenhong || 0).toFixed(2)
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
// 刷新数据(带防重复点击)
|
||||
shuaxinShuju() {
|
||||
// 检查是否在冷却中
|
||||
if (this.data.shuaxinKezhi) {
|
||||
wx.showToast({
|
||||
title: `请等待${this.data.shuaxinCd/1000}秒`,
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置冷却状态
|
||||
this.setData({ shuaxinKezhi: true });
|
||||
|
||||
// 显示刷新动画
|
||||
this.setData({
|
||||
jiazaiZhong: true
|
||||
});
|
||||
|
||||
// 刷新数据
|
||||
this.jiazaiShuju(true).finally(() => {
|
||||
// 2秒后恢复按钮可点击
|
||||
setTimeout(() => {
|
||||
this.setData({ shuaxinKezhi: false });
|
||||
}, this.data.shuaxinCd);
|
||||
});
|
||||
},
|
||||
|
||||
// 上拉加载更多
|
||||
shanglaJiazai() {
|
||||
// 没有更多数据时不再请求
|
||||
if (this.data.meiyouGengduo) {
|
||||
wx.showToast({
|
||||
title: '已经到底了',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果正在加载,不再请求
|
||||
if (this.data.jiazaiGengduo || this.data.jiazaiZhong) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.jiazaiShuju(false);
|
||||
},
|
||||
|
||||
// 重试加载
|
||||
chongshiJiazai() {
|
||||
this.setData({
|
||||
cuowuTishi: '',
|
||||
meiyouGengduo: false
|
||||
});
|
||||
this.jiazaiShuju(true);
|
||||
}
|
||||
});
|
||||
11
miniprogram/pages/czjilu/czjilu.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
"navigationBarTitleText": "会员记录",
|
||||
"navigationBarBackgroundColor": "#0a0a0f",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0a0a0f",
|
||||
"backgroundTextStyle": "light",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
119
miniprogram/pages/czjilu/czjilu.wxml
Normal file
@@ -0,0 +1,119 @@
|
||||
<!-- 管事会员记录页面 -->
|
||||
<view class="page-container">
|
||||
<!-- 固定顶部区域 - 不动 -->
|
||||
<view class="fixed-top">
|
||||
<!-- 顶部统计区域 -->
|
||||
<view class="tongji-quyu">
|
||||
<!-- 分红总额 -->
|
||||
<view class="fenyong-zonge">
|
||||
<view class="zonge-label">分红总额</view>
|
||||
<view class="zonge-value">¥{{fenyongzonge}}</view>
|
||||
<view class="zonge-tip">累计收益</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部统计信息 -->
|
||||
<view class="tongji-bottom">
|
||||
<view class="tongji-item">
|
||||
<view class="tongji-number">{{yaoqingzongshu}}</view>
|
||||
<view class="tongji-label">邀请打手</view>
|
||||
</view>
|
||||
|
||||
<view class="tongji-divider"></view>
|
||||
|
||||
<view class="tongji-item">
|
||||
<view class="tongji-number">{{chongzhiDashouShuliang}}</view>
|
||||
<view class="tongji-label">充值打手</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 刷新按钮 -->
|
||||
<view class="shuaxin-anniu" bindtap="shuaxinShuju">
|
||||
<text class="shuaxin-icon {{shuaxinKezhi ? 'shuaxin-disabled' : ''}}">↻</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 滚动区域 - 只包裹列表部分 -->
|
||||
<scroll-view
|
||||
class="scroll-area"
|
||||
scroll-y
|
||||
enable-back-to-top
|
||||
bindscrolltolower="shanglaJiazai">
|
||||
|
||||
<!-- 全局加载状态 -->
|
||||
<view wx:if="{{jiazaiZhong && dashouList.length === 0}}" class="jiazai-quanju">
|
||||
<view class="jiazai-spinner"></view>
|
||||
<view class="jiazai-text">加载中...</view>
|
||||
</view>
|
||||
|
||||
<!-- 空数据提示 -->
|
||||
<view wx:if="{{!jiazaiZhong && dashouList.length === 0}}" class="kong-shuju">
|
||||
<view class="kong-icon">🚀</view>
|
||||
<view class="kong-text">暂无会员记录</view>
|
||||
<view class="kong-tip">快去邀请打手吧</view>
|
||||
<view class="kong-btn" bindtap="shuaxinShuju">点击刷新</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员记录卡片列表 -->
|
||||
<block wx:if="{{dashouList.length > 0}}">
|
||||
<!-- 列表标题 -->
|
||||
<view class="list-title">
|
||||
<text class="title-text">充值记录</text>
|
||||
<text class="title-count">({{dashouList.length}}/{{zongTiaoshu}})</text>
|
||||
</view>
|
||||
|
||||
<!-- 卡片列表 -->
|
||||
<view class="card-list">
|
||||
<block wx:for="{{dashouList}}" wx:key="index">
|
||||
<view class="dashou-card">
|
||||
<!-- 圆形头像区域 -->
|
||||
<view class="touxiang-quyu">
|
||||
<image
|
||||
class="dashou-touxiang"
|
||||
src="{{item.touxiangUrl}}"
|
||||
mode="aspectFill">
|
||||
</image>
|
||||
<!-- 头像边框效果 -->
|
||||
<view class="touxiang-border"></view>
|
||||
</view>
|
||||
|
||||
<!-- 信息区域 -->
|
||||
<view class="xinxi-quyu">
|
||||
<view class="nicheng">{{item.nicheng}}</view>
|
||||
<view class="dashou-id">ID: {{item.yonghuid}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 分红金额区域 -->
|
||||
<view class="fenhong-quyu">
|
||||
<view class="fenhong-value">
|
||||
<text class="fenhong-jiahao">+</text>
|
||||
<text class="fenhong-shuzi">¥{{item.fenhong}}</text>
|
||||
</view>
|
||||
<view class="shijian">{{item.shijian}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view wx:if="{{jiazaiGengduo}}" class="jiazai-gengduo">
|
||||
<view class="gengduo-spinner"></view>
|
||||
<text class="gengduo-text">加载更多中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多数据 -->
|
||||
<view wx:if="{{meiyouGengduo && dashouList.length > 0}}" class="meiyou-gengduo">
|
||||
<view class="meiyou-icon">✓</view>
|
||||
<text class="meiyou-text">已经到底了,共{{dashouList.length}}条记录</text>
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<view wx:if="{{cuowuTishi}}" class="cuowu-quyu">
|
||||
<view class="cuowu-icon">⚠️</view>
|
||||
<view class="cuowu-text">{{cuowuTishi}}</view>
|
||||
<view class="cuowu-btn" bindtap="chongshiJiazai">重试加载</view>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
506
miniprogram/pages/czjilu/czjilu.wxss
Normal file
@@ -0,0 +1,506 @@
|
||||
/* 管事会员记录页面样式 - 赛博风格 */
|
||||
|
||||
/* 页面容器 */
|
||||
page {
|
||||
height: 100%;
|
||||
background: #0a0a0f;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 50%, #16213e 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 固定顶部区域 */
|
||||
.fixed-top {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 100%);
|
||||
padding-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* 顶部统计区域 - 赛博卡片设计 */
|
||||
.tongji-quyu {
|
||||
position: relative;
|
||||
margin: 20rpx 30rpx 0;
|
||||
padding: 40rpx 30rpx;
|
||||
background: rgba(20, 20, 35, 0.9);
|
||||
border-radius: 24rpx;
|
||||
border: 1px solid rgba(100, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 10rpx 30rpx rgba(0, 0, 0, 0.4),
|
||||
0 0 20rpx rgba(100, 255, 255, 0.05) inset,
|
||||
0 0 0 1px rgba(100, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tongji-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
linear-gradient(45deg,
|
||||
rgba(100, 255, 255, 0.03) 0%,
|
||||
rgba(255, 100, 255, 0.03) 100%);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* 分红总额样式 - 赛博霓虹效果 */
|
||||
.fenyong-zonge {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 20rpx 0 30rpx;
|
||||
margin-bottom: 25rpx;
|
||||
border-bottom: 1px solid rgba(100, 255, 255, 0.1);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.zonge-label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-bottom: 15rpx;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.zonge-value {
|
||||
font-size: 60rpx;
|
||||
font-weight: 800;
|
||||
color: #00ffea;
|
||||
margin-bottom: 8rpx;
|
||||
text-shadow:
|
||||
0 0 10rpx rgba(0, 255, 234, 0.5),
|
||||
0 0 20rpx rgba(0, 255, 234, 0.3);
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.zonge-tip {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 底部统计信息 */
|
||||
.tongji-bottom {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tongji-item {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tongji-number {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin-bottom: 6rpx;
|
||||
text-shadow: 0 0 10rpx rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.tongji-label {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.tongji-divider {
|
||||
width: 1px;
|
||||
height: 45rpx;
|
||||
background: linear-gradient(to bottom,
|
||||
transparent 0%,
|
||||
rgba(100, 255, 255, 0.3) 50%,
|
||||
transparent 100%);
|
||||
}
|
||||
|
||||
/* 刷新按钮 - 固定在右上角 */
|
||||
.shuaxin-anniu {
|
||||
position: absolute;
|
||||
top: 60rpx;
|
||||
right: 60rpx;
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
background: rgba(20, 20, 35, 0.95);
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(100, 255, 255, 0.2);
|
||||
box-shadow:
|
||||
0 8rpx 20rpx rgba(0, 0, 0, 0.4),
|
||||
0 0 20rpx rgba(100, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.shuaxin-anniu:active {
|
||||
transform: scale(0.95);
|
||||
background: rgba(100, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 4rpx 10rpx rgba(0, 0, 0, 0.3),
|
||||
0 0 30rpx rgba(100, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.shuaxin-icon {
|
||||
font-size: 32rpx;
|
||||
color: #00ffea;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.shuaxin-disabled {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 滚动区域 - 只包裹列表部分 */
|
||||
.scroll-area {
|
||||
position: fixed;
|
||||
top: 340rpx; /* 根据固定区域高度调整 */
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* 全局加载状态 */
|
||||
.jiazai-quanju {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.jiazai-spinner {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border: 4rpx solid rgba(100, 255, 255, 0.1);
|
||||
border-top: 4rpx solid #00ffea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 0 20rpx rgba(0, 255, 234, 0.3);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.jiazai-text {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
/* 空数据提示 */
|
||||
.kong-shuju {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 30rpx;
|
||||
background: rgba(20, 20, 35, 0.5);
|
||||
border-radius: 24rpx;
|
||||
border: 1px solid rgba(100, 255, 255, 0.1);
|
||||
margin: 40rpx 30rpx 0;
|
||||
}
|
||||
|
||||
.kong-icon {
|
||||
font-size: 100rpx;
|
||||
margin-bottom: 30rpx;
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.kong-text {
|
||||
font-size: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-bottom: 10rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.kong-tip {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.kong-btn {
|
||||
padding: 20rpx 50rpx;
|
||||
background: linear-gradient(90deg, #00ffea 0%, #0088ff 100%);
|
||||
color: #000;
|
||||
border-radius: 50rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 255, 234, 0.3);
|
||||
}
|
||||
|
||||
.kong-btn:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 4rpx 10rpx rgba(0, 255, 234, 0.2);
|
||||
}
|
||||
|
||||
/* 列表标题 */
|
||||
.list-title {
|
||||
margin: 0 30rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
margin-right: 10rpx;
|
||||
text-shadow: 0 0 10rpx rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.title-count {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 卡片列表 */
|
||||
.card-list {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
/* 打手卡片 - 赛博风格设计 */
|
||||
.dashou-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background: rgba(20, 20, 35, 0.7);
|
||||
border-radius: 20rpx;
|
||||
border: 1px solid rgba(100, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 8rpx 24rpx rgba(0, 0, 0, 0.3),
|
||||
0 0 0 1px rgba(100, 255, 255, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dashou-card:active {
|
||||
transform: translateY(-2rpx);
|
||||
background: rgba(30, 30, 50, 0.8);
|
||||
box-shadow:
|
||||
0 12rpx 30rpx rgba(0, 0, 0, 0.4),
|
||||
0 0 20rpx rgba(100, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 头像区域 - 纯圆形 */
|
||||
.touxiang-quyu {
|
||||
position: relative;
|
||||
margin-right: 30rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dashou-touxiang {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%; /* 确保是纯圆形 */
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.touxiang-border {
|
||||
position: absolute;
|
||||
top: -4rpx;
|
||||
left: -4rpx;
|
||||
width: 108rpx;
|
||||
height: 108rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(45deg, #00ffea, #ff00ff);
|
||||
z-index: 1;
|
||||
opacity: 0.3;
|
||||
animation: borderGlow 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes borderGlow {
|
||||
0%, 100% { opacity: 0.3; }
|
||||
50% { opacity: 0.6; }
|
||||
}
|
||||
|
||||
/* 信息区域 */
|
||||
.xinxi-quyu {
|
||||
flex: 1;
|
||||
min-width: 0; /* 防止内容撑开 */
|
||||
}
|
||||
|
||||
.nicheng {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 10rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 0 10rpx rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.dashou-id {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* 分红金额区域 */
|
||||
.fenhong-quyu {
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
min-width: 180rpx;
|
||||
}
|
||||
|
||||
.fenhong-value {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.fenhong-jiahao {
|
||||
font-size: 26rpx;
|
||||
color: #00ff00;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.fenhong-shuzi {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #00ff00;
|
||||
text-shadow: 0 0 10rpx rgba(0, 255, 0, 0.5);
|
||||
}
|
||||
|
||||
.shijian {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* 加载更多提示 */
|
||||
.jiazai-gengduo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40rpx 0 60rpx;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.gengduo-spinner {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 3rpx solid rgba(100, 255, 255, 0.1);
|
||||
border-top: 3rpx solid #00ffea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.gengduo-text {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
/* 没有更多数据 */
|
||||
.meiyou-gengduo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 50rpx 0 80rpx;
|
||||
margin: 0 30rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.meiyou-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 255, 234, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
color: #00ffea;
|
||||
margin-bottom: 20rpx;
|
||||
border: 1px solid rgba(0, 255, 234, 0.2);
|
||||
}
|
||||
|
||||
.meiyou-text {
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 错误提示 */
|
||||
.cuowu-quyu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 30rpx;
|
||||
background: rgba(40, 20, 20, 0.8);
|
||||
border-radius: 24rpx;
|
||||
border: 1px solid rgba(255, 100, 100, 0.2);
|
||||
margin: 40rpx 30rpx 0;
|
||||
}
|
||||
|
||||
.cuowu-icon {
|
||||
font-size: 80rpx;
|
||||
color: #ff5555;
|
||||
margin-bottom: 30rpx;
|
||||
text-shadow: 0 0 20rpx rgba(255, 85, 85, 0.5);
|
||||
}
|
||||
|
||||
.cuowu-text {
|
||||
font-size: 30rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.cuowu-btn {
|
||||
padding: 20rpx 50rpx;
|
||||
background: linear-gradient(90deg, #ff5555 0%, #ff8800 100%);
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 8rpx 20rpx rgba(255, 85, 85, 0.3);
|
||||
}
|
||||
|
||||
.cuowu-btn:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 4rpx 10rpx rgba(255, 85, 85, 0.2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* 调整滚动区域的顶部距离,增加间隔 */
|
||||
.scroll-area {
|
||||
position: fixed;
|
||||
top: 400rpx; /* 从 340rpx 增加到 360rpx,增加20rpx的间隔 */
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* 如果觉得还不够,可以再增加列表标题的上边距 */
|
||||
.list-title {
|
||||
margin: 10rpx 30rpx 20rpx; /* 从 0 30rpx 20rpx 改为 10rpx 30rpx 20rpx */
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
975
miniprogram/pages/dashouchongzhi/dashouchongzhi.js
Normal file
@@ -0,0 +1,975 @@
|
||||
// pages/dashou-chongzhi/index.js
|
||||
import request from '../../utils/request';
|
||||
// 引入弹窗服务(路径根据实际调整)
|
||||
import PopupService from '../../services/popupService.js';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// ========== 全局数据 ==========
|
||||
clubmber: [], // 会员列表(注意:全局变量中是 clumber,这里保持原样)
|
||||
yajin: 0, // 押金
|
||||
jifen: 0, // 积分(注意:全局变量中是 jinfen)
|
||||
jifenProgress: 0, // 积分进度条角度
|
||||
|
||||
// ========== 会员商品列表 ==========
|
||||
huiyuanList: [], // 从后端获取的会员列表
|
||||
currentHuiyuanIndex: 0, // 当前选中的会员索引
|
||||
currentHuiyuan: {}, // 当前选中的会员详情
|
||||
|
||||
// ========== 押金充值相关 ==========
|
||||
showYajinModal: false, // 显示押金弹窗
|
||||
yajinAmount: '100', // 押金金额
|
||||
|
||||
// ========== 支付相关 ==========
|
||||
payLoading: false, // 支付加载状态
|
||||
loadingText: '支付中...',
|
||||
|
||||
// ========== 订单ID记录 ==========
|
||||
currentDingdanid: '', // 当前操作的订单ID
|
||||
|
||||
// ========== 计算高度 ==========
|
||||
huiyuanListHeight: 120,
|
||||
|
||||
// ========== 会员详情规则 ==========
|
||||
detailRules: [
|
||||
'会员有效期为30天,从购买当天开始计算',
|
||||
'会员期间享受优先接单特权',
|
||||
'可享受专属客服服务',
|
||||
'会员到期前3天会有提醒',
|
||||
'支持随时续费,续费天数叠加'
|
||||
],
|
||||
|
||||
// ========== 新增:跳转参数处理 ==========
|
||||
// 用于接收从其他页面跳转过来的参数,决定是否自动滚动
|
||||
jumpParams: {},
|
||||
|
||||
// ========== 新增:支付方式选择 ==========
|
||||
showPayMethodModal: false, // 支付方式选择弹窗
|
||||
currentBuyType: null, // 当前购买类型:1会员 2押金 3积分
|
||||
currentHuiyuanId: null, // 当前选中的会员ID(用于会员购买)
|
||||
currentYajinAmount: null, // 当前押金金额(用于押金购买)
|
||||
|
||||
// ========== 新增:余额抵扣身份选择 ==========
|
||||
showBalanceModal: false, // 余额抵扣身份选择弹窗
|
||||
balanceOptions: [], // 后端返回的身份列表
|
||||
selectedBalanceId: null, // 选中的身份ID
|
||||
|
||||
// ========== 新增:余额抵扣确认弹窗 ==========
|
||||
showConfirmModal: false, // 余额抵扣确认弹窗
|
||||
selectedBalanceInfo: null, // 选中的身份详情(用于确认弹窗)
|
||||
|
||||
// ========== 新增:会员详情弹窗 ==========
|
||||
showMemberModal: false,
|
||||
currentMemberDetail: null,
|
||||
},
|
||||
|
||||
// ========== 生命周期函数 ==========
|
||||
onLoad(options) {
|
||||
//console.log('页面加载,跳转参数:', options);
|
||||
|
||||
// ✅ 新增:保存跳转参数
|
||||
this.setData({
|
||||
jumpParams: options || {}
|
||||
});
|
||||
|
||||
this.initPage();
|
||||
},
|
||||
|
||||
// pages/tijiao/tijiao.js 中添加 onHide 方法(如果已有则合并内容)
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面都刷新数据
|
||||
this.registerNotificationComponent();
|
||||
this.loadFromGlobalData();
|
||||
this.checkAndRefreshData();
|
||||
|
||||
// 调用统一弹窗组件检查并展示公告
|
||||
PopupService.checkAndShow(this, 'dashouchongzhi');
|
||||
},
|
||||
|
||||
// 注册通知组件(原有,保持不变)
|
||||
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()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
onReady() {
|
||||
// ✅ 新增:页面渲染完成后处理自动滚动
|
||||
this.handleAutoScroll();
|
||||
},
|
||||
|
||||
// ========== 初始化页面 ==========
|
||||
async initPage() {
|
||||
// 从全局数据加载
|
||||
this.loadFromGlobalData();
|
||||
|
||||
// 获取会员商品列表
|
||||
await this.fetchHuiyuanList();
|
||||
|
||||
// 计算会员列表高度
|
||||
this.calculateHuiyuanHeight();
|
||||
},
|
||||
|
||||
// ========== 从全局变量加载数据 ==========
|
||||
loadFromGlobalData() {
|
||||
const app = getApp();
|
||||
const globalData = app.globalData || {};
|
||||
|
||||
// ✅ 重要:保持字段对应关系
|
||||
// 全局变量中是 clumber,页面中是 clubmber
|
||||
// 全局变量中是 jinfen,页面中是 jifen
|
||||
this.setData({
|
||||
clubmber: globalData.clumber || [],
|
||||
yajin: globalData.yajin || 0,
|
||||
jifen: globalData.jinfen || 0 // ✅ 注意:这里从 jinfen 读取
|
||||
});
|
||||
|
||||
// 计算积分进度条
|
||||
this.calculateJifenProgress();
|
||||
},
|
||||
|
||||
// ========== 计算积分进度条角度 ==========
|
||||
calculateJifenProgress() {
|
||||
const jifen = this.data.jifen || 0;
|
||||
const progress = (jifen / 10) * 360; // 满分10分
|
||||
this.setData({
|
||||
jifenProgress: progress
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 计算会员列表高度 ==========
|
||||
calculateHuiyuanHeight() {
|
||||
const clubmber = this.data.clubmber || [];
|
||||
|
||||
const itemHeight = 100; // 每个会员标签高度
|
||||
const maxHeight = 300; // 最大高度(3个会员)
|
||||
|
||||
let height = clubmber.length * itemHeight;
|
||||
height = Math.min(height, maxHeight);
|
||||
|
||||
this.setData({
|
||||
huiyuanListHeight: height
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 获取会员商品列表 ==========
|
||||
async fetchHuiyuanList() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/dshyhq',
|
||||
method: 'POST',
|
||||
data: {}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const huiyuanList = res.data.data || [];
|
||||
|
||||
// 处理数据,标记已购买的会员
|
||||
const processedList = this.processHuiyuanList(huiyuanList);
|
||||
|
||||
this.setData({
|
||||
huiyuanList: processedList,
|
||||
currentHuiyuan: processedList[0] || {}
|
||||
});
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '获取会员列表失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取会员列表失败:', error);
|
||||
wx.showToast({
|
||||
title: '网络错误,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 处理会员列表,标记已购买的会员 ==========
|
||||
processHuiyuanList(huiyuanList) {
|
||||
const clubmber = this.data.clubmber || [];
|
||||
|
||||
return huiyuanList.map(item => {
|
||||
// 检查是否已购买(注意:会员ID字段是 id)
|
||||
const boughtItem = clubmber.find(c => c.huiyuanid === item.id);
|
||||
|
||||
return {
|
||||
...item,
|
||||
isBought: !!boughtItem,
|
||||
buyInfo: boughtItem || null
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
// ========== Swiper切换事件 ==========
|
||||
onSwiperChange(e) {
|
||||
const current = e.detail.current;
|
||||
const huiyuanList = this.data.huiyuanList || [];
|
||||
|
||||
this.setData({
|
||||
currentHuiyuanIndex: current,
|
||||
currentHuiyuan: huiyuanList[current] || {}
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 手动选择会员 ==========
|
||||
selectHuiyuan(e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const huiyuanList = this.data.huiyuanList || [];
|
||||
|
||||
this.setData({
|
||||
currentHuiyuanIndex: index,
|
||||
currentHuiyuan: huiyuanList[index] || {}
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 格式化到期时间 ==========
|
||||
formatDaoqiTime(daoqi) {
|
||||
if (!daoqi) return '';
|
||||
|
||||
try {
|
||||
// 如果是标准日期格式,格式化为 YYYY-MM-DD
|
||||
if (daoqi.includes(' ')) {
|
||||
const dateStr = daoqi.split(' ')[0];
|
||||
const dateParts = dateStr.split('-');
|
||||
if (dateParts.length === 3) {
|
||||
return `${dateParts[0]}-${dateParts[1]}-${dateParts[2]}`;
|
||||
}
|
||||
}
|
||||
return daoqi;
|
||||
} catch (error) {
|
||||
console.error('格式化到期时间错误:', error);
|
||||
return daoqi;
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 计算剩余时间 ==========
|
||||
formatRemainTime(daoqi) {
|
||||
if (!daoqi) return '';
|
||||
|
||||
try {
|
||||
const now = new Date();
|
||||
const end = new Date(daoqi);
|
||||
const diff = end - now;
|
||||
|
||||
if (diff <= 0) return '已过期';
|
||||
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
if (days > 0) {
|
||||
return `${days}天`;
|
||||
}
|
||||
|
||||
const hours = Math.floor(diff / (1000 * 60 * 60));
|
||||
if (hours > 0) {
|
||||
return `${hours}小时`;
|
||||
}
|
||||
|
||||
return '即将到期';
|
||||
} catch (error) {
|
||||
console.error('计算剩余时间错误:', error);
|
||||
return '时间错误';
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 处理自动滚动 ==========
|
||||
handleAutoScroll() {
|
||||
const { jumpParams } = this.data;
|
||||
|
||||
if (!jumpParams || !jumpParams.needScroll) return;
|
||||
|
||||
setTimeout(() => {
|
||||
let scrollTop = 0;
|
||||
|
||||
if (jumpParams.scrollTo === 'bottom') {
|
||||
scrollTop = 2000;
|
||||
} else if (jumpParams.scrollTo === 'member') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollTop > 0) {
|
||||
wx.pageScrollTo({
|
||||
scrollTop: scrollTop,
|
||||
duration: 300
|
||||
});
|
||||
}
|
||||
}, 800);
|
||||
},
|
||||
|
||||
// ========== 押金充值相关 ==========
|
||||
showYajinModal() {
|
||||
this.setData({
|
||||
showYajinModal: true,
|
||||
yajinAmount: '100'
|
||||
});
|
||||
},
|
||||
|
||||
hideYajinModal() {
|
||||
this.setData({
|
||||
showYajinModal: false
|
||||
});
|
||||
},
|
||||
|
||||
onYajinInput(e) {
|
||||
let value = e.detail.value;
|
||||
value = value.replace(/[^\d]/g, '');
|
||||
|
||||
if (value) {
|
||||
const num = parseInt(value);
|
||||
if (num < 1) value = '1';
|
||||
if (num > 10000) value = '10000';
|
||||
}
|
||||
|
||||
this.setData({
|
||||
yajinAmount: value
|
||||
});
|
||||
},
|
||||
|
||||
setYajinAmount(e) {
|
||||
const amount = e.currentTarget.dataset.amount;
|
||||
this.setData({
|
||||
yajinAmount: amount
|
||||
});
|
||||
},
|
||||
|
||||
async handleYajinPay() {
|
||||
const amount = this.data.yajinAmount;
|
||||
|
||||
if (!amount || amount < 1 || amount > 10000) {
|
||||
wx.showToast({
|
||||
title: '请输入1-10000元的金额',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('发起支付中...');
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/yajingoumai',
|
||||
method: 'POST',
|
||||
data: {
|
||||
jine: parseInt(amount)
|
||||
}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const payParams = res.data.payParams;
|
||||
const dingdanid = res.data.dingdanid;
|
||||
|
||||
this.setData({
|
||||
currentDingdanid: dingdanid
|
||||
});
|
||||
|
||||
await this.wechatPay(payParams, 'yajin');
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '支付发起失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('押金支付失败:', error);
|
||||
wx.showToast({
|
||||
title: '支付失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 积分充值相关 ==========
|
||||
// 🔥 修改点1:增加积分已满的硬拦截,不弹任何提示,直接返回
|
||||
onJifenClick(e) {
|
||||
// 优先判断积分是否已满(10分为满分)
|
||||
if (this.data.jifen >= 10) {
|
||||
// 积分已满,不弹窗,不提示,静默返回
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有会员
|
||||
const clubmber = this.data.clubmber || [];
|
||||
if (clubmber.length === 0) {
|
||||
wx.showToast({
|
||||
title: '请先购买会员才能充值积分',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 积分未满,正常打开支付方式选择
|
||||
this.setData({
|
||||
currentBuyType: 3,
|
||||
showPayMethodModal: true
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 会员购买相关 ==========
|
||||
async handleHuiyuanBuy(e) {
|
||||
const huiyuanId = e.currentTarget.dataset.id;
|
||||
|
||||
if (!huiyuanId) {
|
||||
wx.showToast({
|
||||
title: '会员信息错误',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('发起会员支付中...');
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/huiyuangm',
|
||||
method: 'POST',
|
||||
data: {
|
||||
huiyuanid: huiyuanId
|
||||
}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const payParams = res.data.payParams;
|
||||
const dingdanid = res.data.dingdanid;
|
||||
|
||||
this.setData({
|
||||
currentDingdanid: dingdanid
|
||||
});
|
||||
|
||||
await this.wechatPay(payParams, 'huiyuan', huiyuanId);
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res.data.message || '会员支付发起失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('会员支付失败:', error);
|
||||
wx.showToast({
|
||||
title: '支付失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 积分购买(微信支付)—— 已替换为正确的后端接口 ==========
|
||||
async handleJifenBuy(e) {
|
||||
const disabled = e.currentTarget.dataset.disabled;
|
||||
|
||||
// 检查积分是否已满
|
||||
if (disabled === 'true') {
|
||||
wx.showToast({ title: '积分已满,无需补充', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有会员
|
||||
const clubmber = this.data.clubmber || [];
|
||||
if (clubmber.length === 0) {
|
||||
wx.showToast({ title: '请先购买会员才能充值积分', icon: 'none', duration: 3000 });
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('发起积分支付中...');
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/jifenbc', // 正确的积分下单接口
|
||||
method: 'POST',
|
||||
data: {}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const payParams = res.data.payParams;
|
||||
const dingdanid = res.data.dingdanid;
|
||||
|
||||
this.setData({ currentDingdanid: dingdanid });
|
||||
await this.wechatPay(payParams, 'jifen');
|
||||
} else {
|
||||
wx.showToast({ title: res.data.message || '积分支付发起失败', icon: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
wx.showToast({ title: '支付失败,请重试', icon: 'none' });
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 微信支付封装 ==========
|
||||
async wechatPay(payParams, payType, extraData = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!payParams || !payParams.timeStamp || !payParams.paySign) {
|
||||
wx.showToast({
|
||||
title: '支付参数错误',
|
||||
icon: 'none'
|
||||
});
|
||||
reject(new Error('支付参数错误'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('调起支付中...');
|
||||
|
||||
wx.requestPayment({
|
||||
timeStamp: payParams.timeStamp,
|
||||
nonceStr: payParams.nonceStr,
|
||||
package: payParams.package,
|
||||
signType: payParams.signType || 'MD5',
|
||||
paySign: payParams.paySign,
|
||||
success: async () => {
|
||||
this.hideLoading();
|
||||
|
||||
wx.showToast({
|
||||
title: '支付成功!确认中...',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
try {
|
||||
await this.startPolling(payType, extraData);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
fail: async (res) => {
|
||||
this.hideLoading();
|
||||
|
||||
let errorMsg = '支付失败';
|
||||
if (res.errMsg.includes('cancel')) {
|
||||
errorMsg = '您取消了支付';
|
||||
} else if (res.errMsg.includes('fail')) {
|
||||
errorMsg = '支付失败,请重试';
|
||||
}
|
||||
|
||||
wx.showToast({
|
||||
title: errorMsg,
|
||||
icon: 'none'
|
||||
});
|
||||
|
||||
try {
|
||||
await this.handlePayFailure(payType, extraData);
|
||||
} catch (error) {
|
||||
console.error('支付失败通知失败:', error);
|
||||
}
|
||||
|
||||
reject(new Error(res.errMsg));
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 开始轮询支付结果 ==========
|
||||
async startPolling(payType, extraData = null) {
|
||||
const maxRetries = 10;
|
||||
const interval = 2000;
|
||||
const dingdanid = this.data.currentDingdanid;
|
||||
|
||||
if (!dingdanid) {
|
||||
wx.showToast({
|
||||
title: '订单号缺失',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('确认支付结果中...');
|
||||
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
const pollResult = await this.checkPayStatus(payType, dingdanid, extraData);
|
||||
|
||||
if (pollResult.success) {
|
||||
this.hideLoading();
|
||||
await this.updateAfterPayment(payType, extraData, pollResult.data);
|
||||
|
||||
wx.showToast({
|
||||
title: this.getSuccessMessage(payType),
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.sleep(interval);
|
||||
} catch (error) {
|
||||
console.error(`轮询失败第${i + 1}次:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
this.hideLoading();
|
||||
wx.showToast({
|
||||
title: '支付确认超时,请联系客服',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 检查支付状态 ==========
|
||||
async checkPayStatus(payType, dingdanid, extraData = null) {
|
||||
let url = '';
|
||||
let data = { dingdanid: dingdanid };
|
||||
|
||||
switch (payType) {
|
||||
case 'yajin':
|
||||
url = '/shangpin/yajinlunxun';
|
||||
break;
|
||||
case 'jifen':
|
||||
url = '/shangpin/jifenlunxun';
|
||||
break;
|
||||
case 'huiyuan':
|
||||
url = '/shangpin/huiyuanlx';
|
||||
data.huiyuanid = extraData;
|
||||
break;
|
||||
default:
|
||||
throw new Error('未知的支付类型');
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url,
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
|
||||
return {
|
||||
success: res.data.code === 200,
|
||||
data: res.data
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('检查支付状态失败:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 支付失败处理 ==========
|
||||
async handlePayFailure(payType, extraData = null) {
|
||||
let url = '';
|
||||
let data = { dingdanid: this.data.currentDingdanid };
|
||||
|
||||
switch (payType) {
|
||||
case 'yajin':
|
||||
url = '/shangpin/yajinshibai';
|
||||
break;
|
||||
case 'jifen':
|
||||
url = '/shangpin/jifenshibai';
|
||||
break;
|
||||
case 'huiyuan':
|
||||
url = '/shangpin/huiyuanshibai';
|
||||
data.huiyuanid = extraData;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await request({
|
||||
url,
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('支付失败通知失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 支付成功后更新数据 ==========
|
||||
async updateAfterPayment(payType, extraData = null, responseData = null) {
|
||||
const app = getApp();
|
||||
|
||||
switch (payType) {
|
||||
case 'yajin':
|
||||
if (responseData && responseData.yajin !== undefined) {
|
||||
app.globalData.yajin = responseData.yajin;
|
||||
}
|
||||
await this.refreshYajinData();
|
||||
break;
|
||||
|
||||
case 'jifen':
|
||||
if (responseData && responseData.jifen !== undefined) {
|
||||
app.globalData.jinfen = responseData.jifen;
|
||||
}
|
||||
await this.refreshJifenData();
|
||||
break;
|
||||
|
||||
case 'huiyuan':
|
||||
if (responseData && responseData.huiyuan) {
|
||||
app.globalData.jinfen = responseData.jifen;
|
||||
await this.updateClubmberData(responseData.huiyuan);
|
||||
}
|
||||
await this.refreshHuiyuanData(extraData);
|
||||
break;
|
||||
}
|
||||
|
||||
this.loadFromGlobalData();
|
||||
},
|
||||
|
||||
// ========== 刷新押金数据 ==========
|
||||
async refreshYajinData() {
|
||||
this.loadFromGlobalData();
|
||||
},
|
||||
|
||||
// ========== 刷新积分数据 ==========
|
||||
async refreshJifenData() {
|
||||
this.loadFromGlobalData();
|
||||
},
|
||||
|
||||
// ========== 刷新会员数据 ==========
|
||||
async refreshHuiyuanData(huiyuanId) {
|
||||
this.loadFromGlobalData();
|
||||
await this.fetchHuiyuanList();
|
||||
},
|
||||
|
||||
// ========== 更新会员数据到全局变量 ==========
|
||||
async updateClubmberData(huiyuanData) {
|
||||
const app = getApp();
|
||||
const globalClubmber = app.globalData.clumber || [];
|
||||
|
||||
const { huiyuanid, huiyuanming, daoqi } = huiyuanData;
|
||||
|
||||
const index = globalClubmber.findIndex(item => item.huiyuanid === huiyuanid);
|
||||
|
||||
if (index >= 0) {
|
||||
globalClubmber[index].daoqi = daoqi;
|
||||
} else {
|
||||
globalClubmber.push({
|
||||
huiyuanid: huiyuanid,
|
||||
huiyuanming: huiyuanming,
|
||||
daoqi: daoqi
|
||||
});
|
||||
}
|
||||
|
||||
app.globalData.clumber = globalClubmber;
|
||||
|
||||
this.setData({
|
||||
clubmber: [...globalClubmber]
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 检查并刷新数据 ==========
|
||||
async checkAndRefreshData() {
|
||||
this.loadFromGlobalData();
|
||||
},
|
||||
|
||||
// ========== 工具方法 ==========
|
||||
getSuccessMessage(payType) {
|
||||
switch (payType) {
|
||||
case 'yajin': return '押金充值成功';
|
||||
case 'jifen': return '积分补充成功';
|
||||
case 'huiyuan': return '会员购买成功';
|
||||
default: return '支付成功';
|
||||
}
|
||||
},
|
||||
|
||||
showLoading(text = '加载中...') {
|
||||
this.setData({
|
||||
payLoading: true,
|
||||
loadingText: text
|
||||
});
|
||||
},
|
||||
|
||||
hideLoading() {
|
||||
this.setData({
|
||||
payLoading: false
|
||||
});
|
||||
},
|
||||
|
||||
sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
},
|
||||
|
||||
// ========== 新增功能(余额抵扣等) ==========
|
||||
onBuyHuiyuanClick(e) {
|
||||
const huiyuanId = e.currentTarget.dataset.id;
|
||||
this.setData({
|
||||
currentBuyType: 1,
|
||||
currentHuiyuanId: huiyuanId,
|
||||
showPayMethodModal: true
|
||||
});
|
||||
},
|
||||
|
||||
onYajinClick() {
|
||||
this.showYajinModal();
|
||||
},
|
||||
|
||||
onYajinConfirm() {
|
||||
const amount = this.data.yajinAmount;
|
||||
if (!amount || amount < 1 || amount > 10000) {
|
||||
wx.showToast({ title: '请输入1-10000元的金额', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.hideYajinModal();
|
||||
this.setData({
|
||||
currentBuyType: 2,
|
||||
currentYajinAmount: amount,
|
||||
showPayMethodModal: true
|
||||
});
|
||||
},
|
||||
|
||||
hidePayMethodModal() {
|
||||
this.setData({ showPayMethodModal: false });
|
||||
},
|
||||
|
||||
// 🔥 修改点2:选择支付方式时再次校验积分状态(防止异步过程中积分变化)
|
||||
onPayMethodSelect(e) {
|
||||
const method = e.currentTarget.dataset.method;
|
||||
const { currentBuyType, currentHuiyuanId, currentYajinAmount, jifen } = this.data;
|
||||
this.hidePayMethodModal();
|
||||
|
||||
// 如果是积分购买,且积分已满,直接拒绝
|
||||
if (currentBuyType === 3 && jifen >= 10) {
|
||||
wx.showToast({
|
||||
title: '积分已满,无需补充',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === 'wx') {
|
||||
if (currentBuyType === 1) {
|
||||
this.handleHuiyuanBuy({ currentTarget: { dataset: { id: currentHuiyuanId } } });
|
||||
} else if (currentBuyType === 2) {
|
||||
this.setData({ yajinAmount: currentYajinAmount }, () => {
|
||||
this.handleYajinPay();
|
||||
});
|
||||
} else if (currentBuyType === 3) {
|
||||
// ✅ 调用已修正的积分支付函数
|
||||
this.handleJifenBuy({ currentTarget: { dataset: { disabled: 'false' } } });
|
||||
}
|
||||
} else if (method === 'balance') {
|
||||
this.fetchBalanceOptions();
|
||||
}
|
||||
},
|
||||
|
||||
// 🔥 修改点3:请求余额抵扣选项前校验积分状态
|
||||
async fetchBalanceOptions() {
|
||||
const { currentBuyType, currentHuiyuanId, currentYajinAmount, jifen } = this.data;
|
||||
|
||||
// 如果是积分购买,且积分已满,拒绝发起余额抵扣
|
||||
if (currentBuyType === 3 && jifen >= 10) {
|
||||
wx.showToast({
|
||||
title: '积分已满,无需补充',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading('获取抵扣信息...');
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/czhqdy',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: currentBuyType,
|
||||
huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
|
||||
yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
|
||||
}
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
const options = res.data.data || [];
|
||||
this.setData({
|
||||
balanceOptions: options,
|
||||
showBalanceModal: true
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: res.data.message || '获取失败', icon: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取抵扣身份失败:', error);
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
hideBalanceModal() {
|
||||
this.setData({ showBalanceModal: false });
|
||||
},
|
||||
|
||||
onBalanceSelect(e) {
|
||||
const identityId = e.currentTarget.dataset.id;
|
||||
const selected = this.data.balanceOptions.find(opt => opt.id === identityId);
|
||||
if (!selected) return;
|
||||
|
||||
this.setData({
|
||||
selectedBalanceId: identityId,
|
||||
selectedBalanceInfo: selected,
|
||||
showConfirmModal: true,
|
||||
showBalanceModal: false
|
||||
});
|
||||
},
|
||||
|
||||
hideConfirmModal() {
|
||||
this.setData({ showConfirmModal: false });
|
||||
},
|
||||
|
||||
async confirmBalancePay() {
|
||||
const { selectedBalanceId, currentBuyType, currentHuiyuanId, currentYajinAmount } = this.data;
|
||||
if (!selectedBalanceId) return;
|
||||
|
||||
this.hideConfirmModal();
|
||||
this.showLoading('抵扣支付中...');
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/dsqrgmdh',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: currentBuyType,
|
||||
shenfen_id: selectedBalanceId,
|
||||
huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
|
||||
yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
|
||||
}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
const responseData = res.data.data || {};
|
||||
await this.updateAfterPayment(
|
||||
currentBuyType === 1 ? 'huiyuan' : (currentBuyType === 2 ? 'yajin' : 'jifen'),
|
||||
currentBuyType === 1 ? currentHuiyuanId : null,
|
||||
responseData
|
||||
);
|
||||
wx.showToast({ title: '购买成功', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: res.data.message || '购买失败', icon: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('抵扣支付失败:', error);
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
showMemberDetail(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
this.setData({
|
||||
currentMemberDetail: item,
|
||||
showMemberModal: true
|
||||
});
|
||||
},
|
||||
|
||||
hideMemberModal() {
|
||||
this.setData({ showMemberModal: false });
|
||||
},
|
||||
});
|
||||
12
miniprogram/pages/dashouchongzhi/dashouchongzhi.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
"navigationBarTitleText": "充值中心",
|
||||
"navigationBarBackgroundColor": "#0a0e2a",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0a0e2a",
|
||||
"backgroundTextStyle": "light",
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
514
miniprogram/pages/dashouchongzhi/dashouchongzhi.wxml
Normal file
@@ -0,0 +1,514 @@
|
||||
<!--pages/dashou-chongzhi/index.wxml-->
|
||||
<view class="page-container">
|
||||
<!-- ❌ 删除自定义导航栏区块 -->
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- VIP会员区域 - 放在上面 -->
|
||||
<!-- ======================= -->
|
||||
<view class="vip-section">
|
||||
<view class="section-header">
|
||||
<view class="header-decoration">
|
||||
<view class="dec-line"></view>
|
||||
<view class="dec-diamond"></view>
|
||||
<text class="dec-text">VIP会员充值</text>
|
||||
<view class="dec-diamond"></view>
|
||||
<view class="dec-line"></view>
|
||||
</view>
|
||||
<text class="section-subtitle">选择你的会员</text>
|
||||
</view>
|
||||
|
||||
<!-- 会员卡片滑动区域 -->
|
||||
<swiper
|
||||
class="vip-swiper"
|
||||
current="{{currentHuiyuanIndex}}"
|
||||
circular="{{true}}"
|
||||
previous-margin="120rpx"
|
||||
next-margin="120rpx"
|
||||
duration="500"
|
||||
easing-function="easeOutCubic"
|
||||
bindchange="onSwiperChange">
|
||||
<swiper-item wx:for="{{huiyuanList}}" wx:key="id">
|
||||
<view class="vip-card {{index === currentHuiyuanIndex ? 'active' : ''}}"
|
||||
data-index="{{index}}" bindtap="selectHuiyuan">
|
||||
<!-- 机甲风格边框 -->
|
||||
<view class="card-frame">
|
||||
<view class="frame-corner tl"></view>
|
||||
<view class="frame-corner tr"></view>
|
||||
<view class="frame-corner bl"></view>
|
||||
<view class="frame-corner br"></view>
|
||||
<view class="frame-line top"></view>
|
||||
<view class="frame-line right"></view>
|
||||
<view class="frame-line bottom"></view>
|
||||
<view class="frame-line left"></view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<view class="vip-badge" wx:if="{{item.isBought}}">
|
||||
<text class="badge-text">已拥有</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-title">
|
||||
<text class="title-text">{{item.mingzi}}</text>
|
||||
<view class="title-underline"></view>
|
||||
</view>
|
||||
|
||||
<view class="vip-price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-number">{{item.jiage}}</text>
|
||||
<text class="price-unit">/30天</text>
|
||||
</view>
|
||||
|
||||
<view class="vip-features">
|
||||
<view class="feature-item" wx:for="{{item.features || ['特权功能', '专属标识', '优先接单']}}"
|
||||
wx:key="index">
|
||||
<image class="feature-icon" src="/static/images/check-tech.png"></image>
|
||||
<text class="feature-text">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选中状态指示器 -->
|
||||
<view class="card-indicator" wx:if="{{index === currentHuiyuanIndex}}">
|
||||
<view class="indicator-triangle"></view>
|
||||
<view class="indicator-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 会员详细介绍 -->
|
||||
<view class="vip-detail">
|
||||
<view class="detail-header">
|
||||
<view class="detail-title">
|
||||
<image class="detail-icon" src="/static/images/info-tech.png"></image>
|
||||
<text class="title-text">{{currentHuiyuan.mingzi}} - 会员详情</text>
|
||||
</view>
|
||||
<view class="detail-tech"></view>
|
||||
</view>
|
||||
|
||||
<view class="detail-content">
|
||||
<scroll-view class="detail-scroll" scroll-y>
|
||||
<text class="detail-text">{{currentHuiyuan.jieshao || '加载中...'}}</text>
|
||||
|
||||
<view class="detail-rules">
|
||||
<view class="rule-item" wx:for="{{detailRules}}" wx:key="index">
|
||||
<view class="rule-number">{{index + 1}}</view>
|
||||
<text class="rule-text">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 购买区域 - 点击按钮先弹出支付方式选择 -->
|
||||
<view class="buy-section">
|
||||
<view class="buy-info">
|
||||
<view class="price-display">
|
||||
<!-- 🔥 将“机甲能源”改为“价格” -->
|
||||
<text class="price-label">价格:</text>
|
||||
<text class="price-value">¥{{currentHuiyuan.jiage}}</text>
|
||||
</view>
|
||||
<text class="buy-desc" wx:if="{{currentHuiyuan.isBought && currentHuiyuan.buyInfo}}">
|
||||
已激活,<text class="expire-date">{{currentHuiyuan.buyInfo.daoqi}}</text>后到期
|
||||
</text>
|
||||
<text class="buy-desc" wx:else>激活会员特权</text>
|
||||
</view>
|
||||
|
||||
<view class="buy-action">
|
||||
<view class="tech-buy-btn {{currentHuiyuan.isBought ? 'renew' : ''}}"
|
||||
bindtap="onBuyHuiyuanClick" data-id="{{currentHuiyuan.id}}">
|
||||
<view class="buy-btn-bg"></view>
|
||||
<view class="buy-btn-glow"></view>
|
||||
<text class="buy-btn-text">
|
||||
{{currentHuiyuan.isBought ? '立即续费' : '立即激活'}}
|
||||
</text>
|
||||
<view class="buy-btn-energy">
|
||||
<view class="energy-dot"></view>
|
||||
<view class="energy-dot"></view>
|
||||
<view class="energy-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 三卡片区域 - 放在下面 -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-grid">
|
||||
<!-- 会员区域 - 高度限制最多3个,超出可滚动 -->
|
||||
<view class="tech-card huiyuan-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<text class="title-text">我的会员</text>
|
||||
<view class="title-text">👑</view>
|
||||
</view>
|
||||
<view class="card-badge">{{clubmber.length}}</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="huiyuan-scroll"
|
||||
scroll-y
|
||||
enable-flex
|
||||
style="height: {{huiyuanListHeight}}rpx">
|
||||
<view wx:for="{{clubmber}}" wx:key="huiyuanid" class="huiyuan-item" data-item="{{item}}" bindtap="showMemberDetail">
|
||||
<view class="huiyuan-tag tech-tag">
|
||||
<view class="tag-triangle"></view>
|
||||
<text class="tag-name">{{item.huiyuanming}}</text>
|
||||
<text class="tag-time">到期: {{formatDaoqiTime(item.daoqi)}}</text>
|
||||
<view class="tag-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{clubmber.length === 0}}" class="empty-huiyuan">
|
||||
<image class="empty-icon" src="/static/images/empty-member.png"></image>
|
||||
<text class="empty-text">暂无会员</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="card-footer">
|
||||
<text class="footer-text">共{{clubmber.length}}个会员</text>
|
||||
<view class="footer-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 押金区域 - 点击充值按钮弹出押金输入弹窗 -->
|
||||
<view class="tech-card yajin-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<text class="title-text">我的押金</text>
|
||||
<view class="title-text">💰</view>
|
||||
</view>
|
||||
<view class="card-corner"></view>
|
||||
</view>
|
||||
|
||||
<view class="yajin-display">
|
||||
<view class="amount-container">
|
||||
<text class="amount-symbol">¥</text>
|
||||
<text class="amount-number">{{yajin}}</text>
|
||||
</view>
|
||||
<text class="amount-unit">可用余额</text>
|
||||
</view>
|
||||
|
||||
<view class="tech-line"></view>
|
||||
|
||||
<view class="action-container">
|
||||
<view class="tech-btn yajin-btn" bindtap="onYajinClick">
|
||||
<view class="btn-bg"></view>
|
||||
<text class="btn-text">立即充值</text>
|
||||
<view class="btn-arrow" style="display:none;">➤</view>
|
||||
<view class="btn-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-corner corner-bottom"></view>
|
||||
</view>
|
||||
|
||||
<!-- 积分区域 - 点击补充按钮弹出支付方式选择 -->
|
||||
<view class="tech-card jifen-card">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<text class="title-text">我的积分</text>
|
||||
<view class="title-text">⭐</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="jifen-display">
|
||||
<view class="circle-progress">
|
||||
<view class="circle-bg"></view>
|
||||
<view class="circle-mask" style="transform: rotate({{jifenProgress}}deg);"></view>
|
||||
<view class="circle-fill" style="transform: rotate({{jifenProgress}}deg);"></view>
|
||||
<view class="circle-center">
|
||||
<text class="jifen-value">{{jifen}}</text>
|
||||
<text class="jifen-label">积分</text>
|
||||
</view>
|
||||
<view class="circle-dots">
|
||||
<view class="circle-dot" wx:for="{{10}}" wx:key="index"
|
||||
style="transform: rotate({{index * 36}}deg);"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="jifen-status">
|
||||
<text class="status-text" wx:if="{{jifen < 10}}">积分不足,影响接单,提现权限</text>
|
||||
<text class="status-text full" wx:else>积分充足,无需补充</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-container">
|
||||
<view class="tech-btn jifen-btn" bindtap="onJifenClick" data-disabled="{{jifen >= 10}}">
|
||||
<view class="btn-bg"></view>
|
||||
<text class="btn-text">{{jifen >= 10 ? '积分已满' : '立即补充'}}</text>
|
||||
<view class="btn-energy" wx:if="{{jifen < 10}}"></view>
|
||||
<view class="btn-glow" wx:if="{{jifen < 10}}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 押金充值弹窗(原有,确认事件改为 onYajinConfirm) -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-modal" wx:if="{{showYajinModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideYajinModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">押金充值</text>
|
||||
<text class="modal-subtitle">充值保证金</text>
|
||||
<view class="modal-close" bindtap="hideYajinModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="input-section">
|
||||
<view class="input-label">
|
||||
<image class="label-icon" src="/static/images/money-tech.png"></image>
|
||||
<text class="label-text">充值金额 (1-10000元)</text>
|
||||
</view>
|
||||
|
||||
<view class="input-container">
|
||||
<view class="input-prefix">¥</view>
|
||||
<input
|
||||
class="yajin-input"
|
||||
type="digit"
|
||||
placeholder="输入充值金额"
|
||||
placeholder-class="input-placeholder"
|
||||
value="{{yajinAmount}}"
|
||||
bindinput="onYajinInput"
|
||||
maxlength="5"
|
||||
focus="{{showYajinModal}}"/>
|
||||
<view class="input-underline"></view>
|
||||
</view>
|
||||
|
||||
<text class="input-tip">支持1-10000元整数充值</text>
|
||||
</view>
|
||||
|
||||
<view class="quick-section">
|
||||
<text class="quick-title">快速选择</text>
|
||||
<view class="quick-buttons">
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '100' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="100">
|
||||
<text class="quick-text">100元</text>
|
||||
</view>
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '500' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="500">
|
||||
<text class="quick-text">500元</text>
|
||||
</view>
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '1000' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="1000">
|
||||
<text class="quick-text">1000元</text>
|
||||
</view>
|
||||
<view
|
||||
class="quick-btn {{yajinAmount == '5000' ? 'active' : ''}}"
|
||||
bindtap="setYajinAmount"
|
||||
data-amount="5000">
|
||||
<text class="quick-text">5000元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" bindtap="hideYajinModal">
|
||||
<text class="btn-text">取消</text>
|
||||
</view>
|
||||
<view class="modal-divider"></view>
|
||||
<view class="modal-btn confirm-btn" bindtap="onYajinConfirm">
|
||||
<text class="btn-text">确认支付</text>
|
||||
<view class="btn-sparkle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 支付方式选择弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showPayMethodModal}}">
|
||||
<view class="modal-backdrop" bindtap="hidePayMethodModal"></view>
|
||||
<view class="modal-container small">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择支付方式</text>
|
||||
<view class="modal-close" bindtap="hidePayMethodModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="pay-method-list">
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="wx">
|
||||
<image class="method-icon" src="/static/images/wechat-pay.png"></image>
|
||||
<text class="method-name">微信支付</text>
|
||||
<text class="method-desc">安全快捷</text>
|
||||
</view>
|
||||
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="balance">
|
||||
<image class="method-icon" src="/static/images/balance-pay.png"></image>
|
||||
<text class="method-name">余额抵扣</text>
|
||||
<text class="method-desc">使用佣金或分红</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 余额抵扣身份选择弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showBalanceModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideBalanceModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择抵扣身份</text>
|
||||
<view class="modal-close" bindtap="hideBalanceModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="balance-list">
|
||||
<view wx:for="{{balanceOptions}}" wx:key="id" class="balance-item"
|
||||
data-id="{{item.id}}" bindtap="onBalanceSelect">
|
||||
<view class="balance-info">
|
||||
<text class="balance-name">{{item.jieshao}}</text>
|
||||
<text class="balance-amount">需¥{{item.xuyao}}</text>
|
||||
</view>
|
||||
<view class="balance-arrow">➤</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 余额抵扣确认弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showConfirmModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideConfirmModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">确认抵扣</text>
|
||||
<view class="modal-close" bindtap="hideConfirmModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="confirm-detail">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">抵扣身份:</text>
|
||||
<text class="detail-value">{{selectedBalanceInfo && selectedBalanceInfo.jieshao}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">所需金额:</text>
|
||||
<text class="detail-value">¥{{selectedBalanceInfo && selectedBalanceInfo.xuyao}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">实际扣除:</text>
|
||||
<text class="detail-value">¥{{selectedBalanceInfo && selectedBalanceInfo.xuyao}}</text>
|
||||
</view>
|
||||
<view class="detail-tip">确认后将从您的余额中扣除</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-footer">
|
||||
<view class="modal-btn cancel-btn" bindtap="hideConfirmModal">
|
||||
<text class="btn-text">取消</text>
|
||||
</view>
|
||||
<view class="modal-divider"></view>
|
||||
<view class="modal-btn confirm-btn" bindtap="confirmBalancePay">
|
||||
<text class="btn-text">确认支付</text>
|
||||
<view class="btn-sparkle"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ====== 会员详情弹窗 ====== -->
|
||||
<view class="tech-modal" wx:if="{{showMemberModal}}">
|
||||
<view class="modal-backdrop" bindtap="hideMemberModal"></view>
|
||||
<view class="modal-container">
|
||||
<view class="modal-corner tl"></view>
|
||||
<view class="modal-corner tr"></view>
|
||||
<view class="modal-corner bl"></view>
|
||||
<view class="modal-corner br"></view>
|
||||
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">会员详情</text>
|
||||
<view class="modal-close" bindtap="hideMemberModal">
|
||||
<text class="close-text">✕</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="modal-body">
|
||||
<view class="member-detail">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">会员名称:</text>
|
||||
<text class="detail-value">{{currentMemberDetail && currentMemberDetail.huiyuanming}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">到期时间:</text>
|
||||
<text class="detail-value">{{currentMemberDetail && currentMemberDetail.daoqi}}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">剩余天数:</text>
|
||||
<text class="detail-value">{{formatRemainTime(currentMemberDetail && currentMemberDetail.daoqi)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ❌ 删除“进入提示弹窗”整个区块 -->
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 支付加载动画(原有) -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-loading" wx:if="{{payLoading}}">
|
||||
<view class="loading-backdrop"></view>
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner">
|
||||
<view class="spinner-ring ring-1"></view>
|
||||
<view class="spinner-ring ring-2"></view>
|
||||
<view class="spinner-ring ring-3"></view>
|
||||
<view class="spinner-core"></view>
|
||||
<view class="spinner-energy"></view>
|
||||
</view>
|
||||
<text class="loading-text">{{loadingText}}</text>
|
||||
<text class="loading-subtext">正在连接支付系统...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- 底部机甲装饰 -->
|
||||
<!-- ======================= -->
|
||||
<view class="tech-footer">
|
||||
<view class="footer-line"></view>
|
||||
<view class="footer-text">星阙网络 © 7891 科技</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 通知组件 -->
|
||||
<global-notification id="global-notification" />
|
||||
|
||||
<!-- 🆕 统一弹窗组件 -->
|
||||
<popup-notice id="popupNotice" />
|
||||
1705
miniprogram/pages/dashouchongzhi/dashouchongzhi.wxss
Normal file
344
miniprogram/pages/dashoudingdan/dashoudingdan.js
Normal file
@@ -0,0 +1,344 @@
|
||||
const app = getApp()
|
||||
import request from '../../utils/request.js'
|
||||
|
||||
Page({
|
||||
onShow() {
|
||||
// 原有代码...
|
||||
|
||||
// 🆕 注册通知组件
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
// 🆕 新增:注册通知组件
|
||||
registerNotificationComponent() {
|
||||
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()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
data: {
|
||||
// 打手订单状态tab配置(去掉已下单,其他保留)
|
||||
dsDingdanZhuangtaiTabs: [
|
||||
{ name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2] },
|
||||
{ name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4] },
|
||||
{ name: '已退款', key: 'yituikuan', zhuangtaiList: [5] },
|
||||
{ name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6] },
|
||||
{ name: '已完成', key: 'yiwancheng', zhuangtaiList: [3] },
|
||||
{ name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8] }
|
||||
],
|
||||
|
||||
// 当前选中的tab索引(默认0=进行中)
|
||||
xuanzhongTabIndex: 0,
|
||||
// 当前选中的tab的key
|
||||
currentTabKey: 'jinxingzhong',
|
||||
|
||||
// 防抖标志:是否允许切换tab
|
||||
allowSwitchTab: true,
|
||||
// 防抖时间(毫秒)
|
||||
switchDebounceTime: 2000,
|
||||
|
||||
// 每个tab对应的订单数据
|
||||
dsDingdanShuju: {
|
||||
jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
tuikuanzhong: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
yituikuan: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
tuikuanshibai: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }
|
||||
},
|
||||
|
||||
// 打手订单图片URL(从缓存或全局变量获取后拼接)
|
||||
dsDingdanTupianUrl: '',
|
||||
|
||||
// 页面加载状态
|
||||
isLoading: true,
|
||||
|
||||
// 是否显示加载更多
|
||||
xianshiJiazaigengduo: false
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
// 设置页面标题
|
||||
wx.setNavigationBarTitle({
|
||||
title: '我的接单'
|
||||
})
|
||||
|
||||
// 初始化:获取图片URL并拼接
|
||||
this.getAndSetTupianUrl()
|
||||
|
||||
// 加载初始数据(默认进行中)
|
||||
this.setData({
|
||||
isLoading: false
|
||||
})
|
||||
|
||||
this.jiazaiDsDingdanShuju(0, false)
|
||||
},
|
||||
|
||||
// 获取并设置图片URL
|
||||
getAndSetTupianUrl() {
|
||||
const app = getApp()
|
||||
|
||||
// 1. 先从缓存获取touxiang
|
||||
const cacheTouxiang = wx.getStorageSync('touxiang') || ''
|
||||
|
||||
// 2. 如果缓存没有,从全局变量获取morentouxiang
|
||||
let tupianPath = cacheTouxiang || app.globalData.morentouxiang || ''
|
||||
|
||||
// 3. 拼接完整的图片URL
|
||||
const ossImageUrl = app.globalData.ossImageUrl || ''
|
||||
let fullTupianUrl = ''
|
||||
|
||||
if (tupianPath && ossImageUrl) {
|
||||
// 确保路径不以斜杠开头(防止双斜杠)
|
||||
if (tupianPath.startsWith('/')) {
|
||||
tupianPath = tupianPath.substring(1)
|
||||
}
|
||||
fullTupianUrl = ossImageUrl + tupianPath
|
||||
}
|
||||
|
||||
this.setData({
|
||||
dsDingdanTupianUrl: fullTupianUrl
|
||||
})
|
||||
},
|
||||
|
||||
// 切换订单tab(增加防抖)
|
||||
switchDsDingdanTab(e) {
|
||||
const tabIndex = e.currentTarget.dataset.index
|
||||
|
||||
// 如果当前正在加载,不允许切换
|
||||
const currentTabKey = this.data.currentTabKey
|
||||
const currentTabData = this.data.dsDingdanShuju[currentTabKey]
|
||||
if (currentTabData.isLoading) {
|
||||
console.log('当前正在加载,禁止切换')
|
||||
return
|
||||
}
|
||||
|
||||
// 防抖:检查是否允许切换
|
||||
if (!this.data.allowSwitchTab) {
|
||||
|
||||
wx.showToast({
|
||||
title: '切换太频繁,请稍后',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果点击的是当前选中的tab,不处理
|
||||
if (tabIndex === this.data.xuanzhongTabIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
const tabKey = this.data.dsDingdanZhuangtaiTabs[tabIndex].key
|
||||
|
||||
// 设置防抖标志,2秒内不允许再次切换
|
||||
this.setData({
|
||||
allowSwitchTab: false
|
||||
})
|
||||
|
||||
// 设置定时器,2秒后恢复允许切换
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
allowSwitchTab: true
|
||||
})
|
||||
}, this.data.switchDebounceTime)
|
||||
|
||||
// 更新选中的tab
|
||||
this.setData({
|
||||
xuanzhongTabIndex: tabIndex,
|
||||
currentTabKey: tabKey
|
||||
})
|
||||
|
||||
// 加载对应tab的数据
|
||||
this.jiazaiDsDingdanShuju(tabIndex, false)
|
||||
},
|
||||
|
||||
// 加载打手订单数据
|
||||
async jiazaiDsDingdanShuju(tabIndex, isLoadMore = false) {
|
||||
const tab = this.data.dsDingdanZhuangtaiTabs[tabIndex]
|
||||
const tabKey = tab.key
|
||||
|
||||
const tabData = this.data.dsDingdanShuju[tabKey]
|
||||
|
||||
const page = isLoadMore ? tabData.page + 1 : 1
|
||||
|
||||
// 检查是否正在加载
|
||||
if (tabData.isLoading) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 如果不是加载更多,清空已有数据
|
||||
if (!isLoadMore) {
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${tabKey}.list`]: [],
|
||||
[`dsDingdanShuju.${tabKey}.page`]: 1
|
||||
})
|
||||
}
|
||||
|
||||
// 设置加载状态
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${tabKey}.isLoading`]: true
|
||||
})
|
||||
|
||||
// 显示加载提示(非加载更多时显示)
|
||||
if (!isLoadMore) {
|
||||
wx.showLoading({
|
||||
title: '加载订单...',
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
// 准备请求数据
|
||||
const requestData = {
|
||||
zhuangtai_list: tab.zhuangtaiList,
|
||||
page: page,
|
||||
page_size: 10
|
||||
}
|
||||
|
||||
|
||||
|
||||
const res = await request({
|
||||
url: '/dingdan/dshqdingdan',
|
||||
method: 'POST',
|
||||
data: requestData,
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// 隐藏加载提示
|
||||
if (!isLoadMore) {
|
||||
wx.hideLoading()
|
||||
}
|
||||
|
||||
// 处理返回数据
|
||||
if (res && res.data.code === 0) {
|
||||
const newDingdanList = res.data.data.list || []
|
||||
const hasMore = res.data.data.has_more || false
|
||||
|
||||
|
||||
|
||||
// 为每个订单添加状态中文描述和统一的图片URL
|
||||
const processedList = newDingdanList.map(item => {
|
||||
const zhuangtaiZh = this.getZhuangtaiZhongwen(item.zhuangtai)
|
||||
|
||||
return {
|
||||
...item,
|
||||
tupian: this.data.dsDingdanTupianUrl, // 使用统一的图片URL
|
||||
zhuangtaiZh: zhuangtaiZh
|
||||
}
|
||||
})
|
||||
|
||||
const currentList = this.data.dsDingdanShuju[tabKey].list
|
||||
const updatedList = isLoadMore
|
||||
? [...currentList, ...processedList]
|
||||
: processedList
|
||||
|
||||
// 更新数据
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${tabKey}.list`]: updatedList,
|
||||
[`dsDingdanShuju.${tabKey}.page`]: page,
|
||||
[`dsDingdanShuju.${tabKey}.hasMore`]: hasMore,
|
||||
[`dsDingdanShuju.${tabKey}.isLoading`]: false,
|
||||
xianshiJiazaigengduo: hasMore
|
||||
})
|
||||
|
||||
} else {
|
||||
// 请求失败处理
|
||||
const errorMsg = res ? (res.data.msg || '加载失败') : '请求失败'
|
||||
|
||||
if (!isLoadMore) {
|
||||
wx.showToast({
|
||||
title: errorMsg,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${tabKey}.isLoading`]: false
|
||||
})
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
|
||||
if (!isLoadMore) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({
|
||||
title: '网络请求失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
this.setData({
|
||||
[`dsDingdanShuju.${tabKey}.isLoading`]: false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 获取状态中文描述
|
||||
getZhuangtaiZhongwen(zhuangtai) {
|
||||
const zhuangtaiMap = {
|
||||
1: '已下单', // 打手看不到这个状态
|
||||
2: '进行中',
|
||||
3: '已完成',
|
||||
4: '退款中',
|
||||
5: '已退款',
|
||||
6: '退款失败',
|
||||
7: '指定中', // 打手看不到这个状态
|
||||
8: '结算中'
|
||||
}
|
||||
|
||||
return zhuangtaiMap[zhuangtai] || '未知状态'
|
||||
},
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom() {
|
||||
const tabKey = this.data.currentTabKey
|
||||
const tabData = this.data.dsDingdanShuju[tabKey]
|
||||
|
||||
// 检查是否还有更多数据并且当前没有正在加载
|
||||
if (tabData.hasMore && !tabData.isLoading) {
|
||||
const tabIndex = this.data.xuanzhongTabIndex
|
||||
this.jiazaiDsDingdanShuju(tabIndex, true)
|
||||
}
|
||||
},
|
||||
|
||||
// 跳转到订单详情页面
|
||||
goToDsDingdanXiangqing(e) {
|
||||
const dingdanItem = e.currentTarget.dataset.item
|
||||
if (!dingdanItem) return
|
||||
|
||||
// 编码订单数据
|
||||
const dingdanDataStr = encodeURIComponent(JSON.stringify(dingdanItem))
|
||||
|
||||
// 跳转到打手订单详情页
|
||||
wx.navigateTo({
|
||||
url: `/pages/dsddxq/dsddxq?dingdanData=${dingdanDataStr}`
|
||||
})
|
||||
},
|
||||
|
||||
// 图片加载失败处理
|
||||
onImageError(e) {
|
||||
// 如果统一图片加载失败,使用默认图片
|
||||
const defaultImage = app.globalData.lunbozhanwei || '/images/default.jpg'
|
||||
|
||||
this.setData({
|
||||
dsDingdanTupianUrl: defaultImage
|
||||
})
|
||||
}
|
||||
})
|
||||
8
miniprogram/pages/dashoudingdan/dashoudingdan.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
"navigationBarTitleText": "我的接单",
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
111
miniprogram/pages/dashoudingdan/dashoudingdan.wxml
Normal file
@@ -0,0 +1,111 @@
|
||||
<!-- pages/dashouDingdan/dashouDingdan.wxml -->
|
||||
<view class="ds-dingdan-page">
|
||||
|
||||
<!-- 打手订单状态tab区域 -->
|
||||
<scroll-view
|
||||
class="ds-dingdan-tabs-scroll"
|
||||
scroll-x="true"
|
||||
scroll-with-animation="true"
|
||||
>
|
||||
<view class="ds-dingdan-tabs-container">
|
||||
<block wx:for="{{dsDingdanZhuangtaiTabs}}" wx:key="key">
|
||||
<view
|
||||
class="ds-dingdan-tab-item {{index === xuanzhongTabIndex ? 'ds-tab-active' : ''}}"
|
||||
bindtap="switchDsDingdanTab"
|
||||
data-index="{{index}}"
|
||||
>
|
||||
<text class="ds-tab-text">{{item.name}}</text>
|
||||
<view class="ds-tab-active-line" wx:if="{{index === xuanzhongTabIndex}}"></view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 打手订单列表区域 -->
|
||||
<view class="ds-dingdan-list-container">
|
||||
|
||||
<!-- 页面加载中 -->
|
||||
<view class="ds-loading-container" wx:if="{{isLoading}}">
|
||||
<view class="ds-loading-spinner"></view>
|
||||
<text class="ds-loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view
|
||||
class="ds-empty-container"
|
||||
wx:if="{{!isLoading && dsDingdanShuju[currentTabKey].list.length === 0}}"
|
||||
>
|
||||
<image
|
||||
class="ds-empty-image"
|
||||
src="/images/empty-order.png"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<text class="ds-empty-text">暂无订单</text>
|
||||
<text class="ds-empty-subtext">快去接单吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 打手订单列表 -->
|
||||
<block wx:if="{{!isLoading && dsDingdanShuju[currentTabKey].list.length > 0}}">
|
||||
<block wx:for="{{dsDingdanShuju[currentTabKey].list}}" wx:key="dingdan_id">
|
||||
<view
|
||||
class="ds-dingdan-card"
|
||||
bindtap="goToDsDingdanXiangqing"
|
||||
data-item="{{item}}"
|
||||
>
|
||||
<!-- 商品图片(统一图片) -->
|
||||
<view class="ds-dingdan-card-left">
|
||||
<image
|
||||
class="ds-dingdan-image"
|
||||
src="{{item.tupian}}"
|
||||
mode="aspectFill"
|
||||
binderror="onImageError"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息(订单介绍显示三行) -->
|
||||
<view class="ds-dingdan-card-center">
|
||||
<view class="ds-dingdan-jieshao ds-jieshao-three-line">
|
||||
{{item.jieshao || '暂无订单描述'}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单状态和价格 -->
|
||||
<view class="ds-dingdan-card-right">
|
||||
<!-- 订单状态 -->
|
||||
<view class="ds-dingdan-zhuangtai">
|
||||
{{item.zhuangtaiZh || '未知状态'}}
|
||||
</view>
|
||||
|
||||
<!-- 订单价格 -->
|
||||
<view class="ds-dingdan-jine">
|
||||
<text class="ds-jine-symbol">¥</text>
|
||||
<text class="ds-jine-number">{{item.jine || '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view
|
||||
class="ds-load-more-container"
|
||||
wx:if="{{xianshiJiazaigengduo && !isLoading && dsDingdanShuju[currentTabKey].list.length > 0}}"
|
||||
>
|
||||
<view class="ds-load-more-line"></view>
|
||||
<text class="ds-load-more-text">上拉加载更多</text>
|
||||
<view class="ds-load-more-line"></view>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多提示 -->
|
||||
<view
|
||||
class="ds-no-more-container"
|
||||
wx:if="{{!xianshiJiazaigengduo && !isLoading && dsDingdanShuju[currentTabKey].list.length > 0}}"
|
||||
>
|
||||
<text class="ds-no-more-text">没有更多订单了</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<custom-tab-bar />
|
||||
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
273
miniprogram/pages/dashoudingdan/dashoudingdan.wxss
Normal file
@@ -0,0 +1,273 @@
|
||||
/* pages/dashouDingdan/dashouDingdan.wxss */
|
||||
|
||||
.ds-dingdan-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-tabs-scroll {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
white-space: nowrap;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.ds-dingdan-tabs-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-tab-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 60rpx;
|
||||
padding: 0 30rpx;
|
||||
margin-right: 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #f5f5f5;
|
||||
color: #666666;
|
||||
font-size: 28rpx;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ds-dingdan-tab-item:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ds-dingdan-tab-item.ds-tab-active {
|
||||
background-color: #e3f2fd;
|
||||
color: #1565c0;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4rpx 12rpx rgba(21, 101, 192, 0.1);
|
||||
}
|
||||
|
||||
.ds-tab-active-line {
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background-color: #2196f3;
|
||||
border-radius: 2rpx;
|
||||
margin-top: 6rpx;
|
||||
animation: ds-fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes ds-fadeIn {
|
||||
from { opacity: 0; transform: scale(0.8); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
.ds-tab-text {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.ds-dingdan-list-container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.ds-loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 400rpx;
|
||||
}
|
||||
|
||||
.ds-loading-spinner {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 6rpx solid #e0e0e0;
|
||||
border-top-color: #2196f3;
|
||||
border-radius: 50%;
|
||||
animation: ds-spin 1s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
@keyframes ds-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.ds-loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.ds-empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 500rpx;
|
||||
padding-top: 80rpx;
|
||||
}
|
||||
|
||||
.ds-empty-image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 30rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.ds-empty-text {
|
||||
font-size: 32rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.ds-empty-subtext {
|
||||
font-size: 26rpx;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.ds-dingdan-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.ds-dingdan-card:active {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.ds-dingdan-card-left {
|
||||
flex-shrink: 0;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.ds-dingdan-card-center {
|
||||
flex: 1;
|
||||
height: 160rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 关键不同点:订单介绍显示三行 */
|
||||
.ds-dingdan-jieshao.ds-jieshao-three-line {
|
||||
font-size: 28rpx;
|
||||
line-height: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: 400;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3; /* 改为3行 */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 8rpx;
|
||||
min-height: 108rpx; /* 3 * 36rpx */
|
||||
}
|
||||
|
||||
.ds-dingdan-card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
height: 160rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-zhuangtai {
|
||||
font-size: 24rpx;
|
||||
color: #ff6b6b;
|
||||
font-weight: 500;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #fff5f5;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
min-width: 100rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-jine {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ds-jine-symbol {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.ds-jine-number {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ds-load-more-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.ds-load-more-line {
|
||||
width: 80rpx;
|
||||
height: 1rpx;
|
||||
background-color: #e0e0e0;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.ds-load-more-text {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ds-no-more-container {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.ds-no-more-text {
|
||||
font-size: 26rpx;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
.ds-dingdan-image {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-card-center {
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-card-right {
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
.ds-jine-number {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.ds-dingdan-jieshao.ds-jieshao-three-line {
|
||||
min-height: 96rpx; /* 调整小屏幕下的最小高度 */
|
||||
font-size: 26rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
}
|
||||
543
miniprogram/pages/dashouduan/dashouduan.js
Normal file
@@ -0,0 +1,543 @@
|
||||
// pages/dashouzhongxin/dashouzhongxin.js
|
||||
import request from '../../utils/request.js'
|
||||
import popupService from '../../services/popupService.js'
|
||||
|
||||
const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
isDashou: false,
|
||||
isLoading: false,
|
||||
inviteCode: '',
|
||||
uid: '',
|
||||
touxiang: '',
|
||||
avatarUrl: '',
|
||||
dashouNicheng: '',
|
||||
zhanghaoStatus: '',
|
||||
yongjin: '',
|
||||
zonge: '',
|
||||
yajin: '',
|
||||
chenghao: '',
|
||||
jinfen: '',
|
||||
clumber: [],
|
||||
chengjiaoliang: '',
|
||||
zaixianZhuangtai: 0,
|
||||
lastRefreshTime: 0,
|
||||
canRefresh: true,
|
||||
isAutoRegistering: false,
|
||||
inviterCache: null,
|
||||
// 🆕 称号标签列表(从独立接口获取)
|
||||
chenghaoList: []
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
if (options.scene) {
|
||||
try {
|
||||
const scene = decodeURIComponent(options.scene);
|
||||
options.inviteCode = scene;
|
||||
} catch (e) {
|
||||
console.error('scene解码失败', e);
|
||||
}
|
||||
}
|
||||
this.checkDashouStatus();
|
||||
const { inviteCode } = options || {};
|
||||
if (inviteCode) {
|
||||
this.setData({ inviteCode: inviteCode });
|
||||
setTimeout(() => {
|
||||
this.handleInviteCodeWithLoginCheck(inviteCode);
|
||||
}, 500);
|
||||
}
|
||||
const app = getApp();
|
||||
if (!app.globalData.hasShownPopupOnColdStart) {
|
||||
app.globalData.hasShownPopupOnColdStart = true;
|
||||
setTimeout(() => {
|
||||
popupService.checkAndShow(this, 'dashouduan');
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
const popupComp = this.selectComponent('#popupNotice');
|
||||
if (popupComp && popupComp.cleanup) {
|
||||
popupComp.cleanup();
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 从缓存读取邀请人信息
|
||||
const inviterCache = wx.getStorageSync('inviterCache') || null;
|
||||
this.setData({ inviterCache });
|
||||
|
||||
this.registerNotificationComponent();
|
||||
this.checkAndRedirectToJinpai();
|
||||
if (this.data.isDashou) {
|
||||
setTimeout(() => {
|
||||
this.refreshDashouInfo();
|
||||
this.fetchChenghaoList(); // 🆕 获取标签
|
||||
}, 300);
|
||||
}
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const options = currentPage.options || {};
|
||||
if (options.inviteCode && this.data.isDashou === false) {
|
||||
const { inviteCode } = options;
|
||||
setTimeout(() => {
|
||||
this.handleInviteCodeWithLoginCheck(inviteCode);
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 🆕 获取称号标签列表 ====================
|
||||
async fetchChenghaoList() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dengji/dsbqhq',
|
||||
method: 'POST'
|
||||
});
|
||||
if (res && res.data.code === 0 && res.data.data) {
|
||||
const list = res.data.data.chenghao_list || [];
|
||||
this.setData({ chenghaoList: list });
|
||||
// 可选:也存入全局,方便其他页面使用
|
||||
const app = getApp();
|
||||
app.globalData.chenghaoList = list;
|
||||
} else {
|
||||
this.setData({ chenghaoList: [] });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取称号标签失败', e);
|
||||
// 失败不影响主流程
|
||||
}
|
||||
},
|
||||
// ========================================================
|
||||
|
||||
// 联系邀请人(保持不变)
|
||||
async contactInviter() {
|
||||
wx.showLoading({ title: '获取邀请人...', mask: true });
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/hqwdyqr', method: 'POST' });
|
||||
if (res && res.data.code === 200) {
|
||||
const inviter = res.data.data;
|
||||
const ossImageUrl = getApp().globalData.ossImageUrl || '';
|
||||
const fullAvatar = inviter.touxiang
|
||||
? (inviter.touxiang.startsWith('http') ? inviter.touxiang : ossImageUrl + inviter.touxiang)
|
||||
: '';
|
||||
|
||||
const cacheData = {
|
||||
uid: inviter.uid,
|
||||
nicheng: inviter.nicheng || '',
|
||||
avatar: fullAvatar
|
||||
};
|
||||
wx.setStorageSync('inviterCache', cacheData);
|
||||
this.setData({ inviterCache: cacheData });
|
||||
|
||||
const app = getApp();
|
||||
const myUid = wx.getStorageSync('uid');
|
||||
if (app.globalData.currentRole !== 'dashou') {
|
||||
app.globalData.currentRole = 'dashou';
|
||||
wx.setStorageSync('currentRole', 'dashou');
|
||||
if (app.switchRoleAndReconnect) {
|
||||
await app.switchRoleAndReconnect('dashou');
|
||||
}
|
||||
} else {
|
||||
if (app.connectForCurrentRole) app.connectForCurrentRole();
|
||||
}
|
||||
|
||||
const param = {
|
||||
toUserId: 'Gs' + inviter.uid,
|
||||
toName: inviter.nicheng || '邀请人',
|
||||
toAvatar: fullAvatar
|
||||
};
|
||||
wx.navigateTo({
|
||||
url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param))
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: res?.data?.msg || '获取失败', icon: 'none' });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '网络错误', icon: 'none' });
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
handleInviteCodeWithLoginCheck(inviteCode) {
|
||||
const that = this;
|
||||
const token = wx.getStorageSync('token');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (!token || !uid) {
|
||||
this.loginAndRegisterWithInviteCode(inviteCode);
|
||||
} else {
|
||||
if (!that.data.isDashou) {
|
||||
wx.showLoading({ title: '自动注册中...', mask: true });
|
||||
that.setData({ isAutoRegistering: true });
|
||||
that.onRegister();
|
||||
} else {
|
||||
wx.showToast({ title: '您已是打手', icon: 'success', duration: 2000 });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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();
|
||||
console.error('微信登录失败');
|
||||
wx.reLaunch({ url: '/pages/gerenzhongxin/gerenzhongxin' });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
callLoginRegisterApi(code, inviteCode) {
|
||||
const that = this;
|
||||
const app = getApp();
|
||||
const apiBaseUrl = app.globalData.apiBaseUrl;
|
||||
if (!apiBaseUrl) {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '服务器配置错误', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.request({
|
||||
url: apiBaseUrl + '/yonghu/wdlyhdl',
|
||||
method: 'POST',
|
||||
data: { code: code, inviteCode: inviteCode },
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
|
||||
const userData = res.data.data;
|
||||
if (userData.token) wx.setStorageSync('token', userData.token);
|
||||
const identityFields = ['nicheng', 'uid', 'touxiang', 'dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus'];
|
||||
identityFields.forEach(field => {
|
||||
if (userData[field] !== undefined) {
|
||||
wx.setStorageSync(field, userData[field]);
|
||||
app.globalData[field] = userData[field];
|
||||
}
|
||||
});
|
||||
const groupFields = ['dashouqun', 'dashouqunid', 'guanshiqun', 'guanshiqunid'];
|
||||
groupFields.forEach(field => {
|
||||
if (userData[field] !== undefined) {
|
||||
wx.setStorageSync(field, userData[field]);
|
||||
app.globalData[field] = userData[field];
|
||||
}
|
||||
});
|
||||
if (userData.dashounicheng || userData.yongjin) {
|
||||
const updateData = {
|
||||
dashouNicheng: userData.dashounicheng || '',
|
||||
zhanghaoStatus: userData.zhanghaostatus || '',
|
||||
yongjin: userData.yongjin || '0.00',
|
||||
zonge: userData.zonge || '0.00',
|
||||
yajin: userData.yajin || '0.00',
|
||||
chenghao: userData.chenghao || '',
|
||||
jinfen: userData.jifen || userData.jinfen || '0',
|
||||
clumber: userData.clumber || [],
|
||||
chengjiaoliang: userData.chengjiaoliang || '0',
|
||||
zaixianZhuangtai: userData.zaixianzhuangtai || 0,
|
||||
dashouzhuangtai: userData.dashouzhuangtai || '',
|
||||
// 🔥 不再从这里取 chenghaoList,单独调用 fetchChenghaoList
|
||||
};
|
||||
Object.assign(app.globalData, updateData);
|
||||
}
|
||||
that.setData({
|
||||
isDashou: true,
|
||||
uid: userData.uid || '',
|
||||
touxiang: userData.touxiang || '',
|
||||
isAutoRegistering: false
|
||||
// 不设置 chenghaoList,稍后在 onShow 中自动获取
|
||||
});
|
||||
that.loadAvatar();
|
||||
that.getDashouInfo();
|
||||
that.switchToDashouRole();
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '登录注册成功!', icon: 'success', duration: 2000 });
|
||||
} else {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: (res.data && res.data.msg) || '登录注册失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
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');
|
||||
const uid = wx.getStorageSync('uid');
|
||||
const touxiang = wx.getStorageSync('touxiang');
|
||||
if (dashoustatus === 1) {
|
||||
this.setData({ isDashou: true, uid: uid || '', touxiang: touxiang || '' });
|
||||
this.loadAvatar();
|
||||
this.checkGlobalData();
|
||||
} else {
|
||||
this.setData({ isDashou: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取缓存失败:', error);
|
||||
this.setData({ isDashou: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadAvatar() {
|
||||
const app = getApp();
|
||||
const { ossImageUrl = '', morentouxiang = 'avatar/default.jpg' } = app.globalData || {};
|
||||
const { touxiang } = this.data;
|
||||
let avatarUrl = '';
|
||||
if (touxiang) {
|
||||
avatarUrl = ossImageUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang);
|
||||
} else {
|
||||
avatarUrl = ossImageUrl + (morentouxiang.startsWith('/') ? morentouxiang : '/' + morentouxiang);
|
||||
}
|
||||
this.setData({ avatarUrl });
|
||||
},
|
||||
|
||||
checkGlobalData() {
|
||||
const app = getApp();
|
||||
const globalData = app.globalData || {};
|
||||
const needRefresh = !globalData.dashouNicheng || globalData.dashouNicheng === '' || !globalData.yongjin;
|
||||
if (needRefresh) {
|
||||
this.getDashouInfo();
|
||||
} else {
|
||||
this.setDataFromGlobalData();
|
||||
}
|
||||
},
|
||||
|
||||
setDataFromGlobalData() {
|
||||
const app = getApp();
|
||||
const {
|
||||
dashouNicheng = '', zhanghaoStatus = '', yongjin = '0.00', zonge = '0.00',
|
||||
yajin = '0.00', chenghao = '', jinfen = '0', clumber = [],
|
||||
chengjiaoliang = '0', zaixianZhuangtai = 0
|
||||
// 🔥 chenghaoList 不再从全局取,避免旧数据干扰
|
||||
} = app.globalData || {};
|
||||
this.setData({
|
||||
dashouNicheng, zhanghaoStatus, yongjin, zonge, yajin,
|
||||
chenghao, jinfen,
|
||||
clumber: Array.isArray(clumber) ? clumber : [],
|
||||
chengjiaoliang,
|
||||
zaixianZhuangtai: zaixianZhuangtai === 1 ? 1 : 0
|
||||
});
|
||||
},
|
||||
|
||||
async getDashouInfo() {
|
||||
this.setData({ isLoading: true });
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/dashouxinxi', method: 'POST' });
|
||||
if (res && res.data.code == 200) {
|
||||
const data = res.data.data;
|
||||
const app = getApp();
|
||||
const updateData = {
|
||||
dashouNicheng: data.dashounicheng || '',
|
||||
zhanghaoStatus: data.zhanghaostatus || '',
|
||||
yongjin: data.yongjin || '0.00',
|
||||
zonge: data.zonge || '0.00',
|
||||
yajin: data.yajin || '0.00',
|
||||
chenghao: data.chenghao || '',
|
||||
jinfen: data.jifen || '0',
|
||||
clumber: data.clumber || [],
|
||||
chengjiaoliang: data.chengjiaoliang || '0',
|
||||
zaixianZhuangtai: data.zaixianzhuangtai || 0,
|
||||
dashouzhuangtai: data.dashouzhuangtai || '',
|
||||
// 🔥 不再包含 chenghaoList
|
||||
};
|
||||
Object.assign(app.globalData, updateData);
|
||||
if (data.dashoustatus !== undefined) {
|
||||
wx.setStorageSync('dashoustatus', data.dashoustatus);
|
||||
app.globalData.dashoustatus = data.dashoustatus;
|
||||
}
|
||||
if (data.guanshistatus !== undefined) {
|
||||
wx.setStorageSync('guanshistatus', data.guanshistatus);
|
||||
app.globalData.guanshistatus = data.guanshistatus;
|
||||
}
|
||||
this.setData({ ...updateData, isLoading: false });
|
||||
if (this.data.isAutoRegistering) {
|
||||
this.setData({ isAutoRegistering: false });
|
||||
wx.showToast({ title: '注册成功!', icon: 'success', duration: 2000 });
|
||||
} else {
|
||||
wx.showToast({ title: '信息已更新', icon: 'success', duration: 1500 });
|
||||
}
|
||||
this.handleJinpaiStatus(data.chenghao);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取打手信息失败:', error);
|
||||
this.setData({ isLoading: false, isAutoRegistering: false });
|
||||
wx.showToast({ title: '获取信息失败', icon: 'error', duration: 2000 });
|
||||
}
|
||||
},
|
||||
|
||||
refreshDashouInfo() {
|
||||
const now = Date.now();
|
||||
if (!this.data.canRefresh || (now - this.data.lastRefreshTime < 3000)) return;
|
||||
this.setData({ lastRefreshTime: now, canRefresh: false });
|
||||
this.getDashouInfo();
|
||||
this.fetchChenghaoList(); // 🆕 同时刷新标签
|
||||
setTimeout(() => this.setData({ canRefresh: true }), 3000);
|
||||
},
|
||||
|
||||
onInviteCodeInput(e) {
|
||||
this.setData({ inviteCode: e.detail.value.trim() });
|
||||
},
|
||||
|
||||
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: '/yonghu/dashouzhuce',
|
||||
method: 'POST',
|
||||
data: { inviteCode: inviteCode }
|
||||
});
|
||||
if (res && res.data.code == 200) {
|
||||
const data = res.data.data;
|
||||
const app = getApp();
|
||||
const identityFields = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus'];
|
||||
identityFields.forEach(field => {
|
||||
if (data[field] !== undefined) {
|
||||
wx.setStorageSync(field, data[field]);
|
||||
app.globalData[field] = data[field];
|
||||
}
|
||||
});
|
||||
if (data.dashoustatus === undefined) {
|
||||
wx.setStorageSync('dashoustatus', 1);
|
||||
app.globalData.dashoustatus = 1;
|
||||
}
|
||||
const updateData = {
|
||||
dashouNicheng: data.dashounicheng || '',
|
||||
zhanghaoStatus: data.zhanghaostatus || '',
|
||||
yongjin: data.yongjin || '0.00',
|
||||
zonge: data.zonge || '0.00',
|
||||
yajin: data.yajin || '0.00',
|
||||
chenghao: data.chenghao || '',
|
||||
jinfen: data.jinfen || '0',
|
||||
clumber: data.clumber || [],
|
||||
chengjiaoliang: data.chengjiaoliang || '0',
|
||||
zaixianZhuangtai: data.zaixianzhuangtai || 0,
|
||||
dashouzhuangtai: data.dashouzhuangtai || '',
|
||||
// chenghaoList 不从这取
|
||||
};
|
||||
Object.assign(app.globalData, updateData);
|
||||
this.setData({
|
||||
isDashou: true,
|
||||
...updateData,
|
||||
isLoading: false,
|
||||
isAutoRegistering: false
|
||||
});
|
||||
this.loadAvatar();
|
||||
if (this.data.isAutoRegistering || !wx.getStorageSync('token')) {
|
||||
this.switchToDashouRole();
|
||||
}
|
||||
wx.showToast({ title: '注册成功!', icon: 'success', duration: 2000 });
|
||||
this.handleJinpaiStatus(data.chenghao);
|
||||
} else {
|
||||
this.setData({ isLoading: false, isAutoRegistering: false });
|
||||
wx.showToast({ title: (res.data && res.data.message) || '注册失败', icon: 'error', duration: 2000 });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error);
|
||||
this.setData({ isLoading: false, isAutoRegistering: false });
|
||||
wx.showToast({ title: error.message || '注册失败', icon: 'error', duration: 2000 });
|
||||
}
|
||||
},
|
||||
|
||||
switchToDashouRole() {
|
||||
const app = getApp();
|
||||
app.globalData.currentRole = 'dashou';
|
||||
wx.setStorageSync('currentRole', 'dashou');
|
||||
const tabBar = this.getTabBar();
|
||||
if (tabBar && tabBar.refresh) tabBar.refresh();
|
||||
if (app.connectForCurrentRole) {
|
||||
app.connectForCurrentRole();
|
||||
} else if (app.ensureConnection) {
|
||||
app.ensureConnection();
|
||||
}
|
||||
},
|
||||
|
||||
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] });
|
||||
},
|
||||
|
||||
goToWithdraw() { wx.navigateTo({ url: '/pages/tixian/tixian' }) },
|
||||
goToReceiveOrder() { wx.navigateTo({ url: '/pages/jiedan/jiedan' }) },
|
||||
goToMyOrders() { wx.navigateTo({ url: '/pages/dashoudingdan/dashoudingdan' }) },
|
||||
goToMyPunishment() { wx.navigateTo({ url: '/pages/cfss/cfss/cfss' }) },
|
||||
goToRecharge() { wx.navigateTo({ url: '/pages/dashouchongzhi/dashouchongzhi' }) },
|
||||
goToRanking() { wx.navigateTo({ url: '/pages/dashoupaihang/dashoupaihang' }) },
|
||||
goToModifyInfo() { wx.navigateTo({ url: '/pages/dashouxiugai/dashouxiugai' }) },
|
||||
goToRules() { wx.navigateTo({ url: '/pages/dashouguize/dashouguize' }) },
|
||||
// 跳转到考核金牌
|
||||
goToKaohe() {
|
||||
wx.navigateTo({ url: '/pages/kaohe_jinpai/kaohe_jinpai' });
|
||||
},
|
||||
|
||||
quickRegisterAsPlatform() {
|
||||
const that = this;
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '使用此功能后,您将不属于任何管事,确认注册吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
that.setData({ inviteCode: PLATFORM_INVITE_CODE });
|
||||
that.onRegister();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
checkAndRedirectToJinpai() {
|
||||
const isJinpai = wx.getStorageSync('isJinpai');
|
||||
if (isJinpai === 1) {
|
||||
wx.redirectTo({ url: '/pages/jinpaids/jinpaids' });
|
||||
}
|
||||
},
|
||||
|
||||
handleJinpaiStatus(chenghao) {
|
||||
const isJinpai = (chenghao === '金牌选手') ? 1 : 0;
|
||||
wx.setStorageSync('isJinpai', isJinpai);
|
||||
if (isJinpai) {
|
||||
wx.redirectTo({ url: '/pages/jinpaids/jinpaids' });
|
||||
}
|
||||
}
|
||||
})
|
||||
14
miniprogram/pages/dashouduan/dashouduan.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification",
|
||||
"chenghao-tag": "/components/chenghao-tag/chenghao-tag"
|
||||
|
||||
},
|
||||
"navigationBarBackgroundColor": "#0F0A1F",
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "服务中心",
|
||||
"backgroundColor": "#0F0A1F",
|
||||
"backgroundTextStyle": "light",
|
||||
"enablePullDownRefresh": false,
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
212
miniprogram/pages/dashouduan/dashouduan.wxml
Normal file
@@ -0,0 +1,212 @@
|
||||
<!-- pages/dashouzhongxin/dashouzhongxin.wxml -->
|
||||
<view class="page-container unregistered-page" wx:if="{{!isDashou}}">
|
||||
<!-- 未注册部分完全不变 -->
|
||||
<view class="status-tip">您还未解锁此功能,请输入邀请码解锁</view>
|
||||
|
||||
<view class="input-container">
|
||||
<input
|
||||
class="invite-input"
|
||||
type="text"
|
||||
placeholder="请输入100位以内邀请码"
|
||||
maxlength="100"
|
||||
value="{{inviteCode}}"
|
||||
bindinput="onInviteCodeInput"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="register-view-btn" bindtap="onRegister">
|
||||
<text class="register-text">输入邀请码注册</text>
|
||||
<view class="register-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="quick-register-btn" bindtap="quickRegisterAsPlatform">
|
||||
|
||||
<view class="quick-register-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已注册状态 -->
|
||||
<view class="page-container registered-page" wx:else>
|
||||
<!-- 刷新按钮 -->
|
||||
<view class="refresh-btn" bindtap="refreshDashouInfo">
|
||||
<text class="refresh-icon">↻</text>
|
||||
</view>
|
||||
|
||||
<!-- 头像区域 -->
|
||||
<view class="avatar-section">
|
||||
<view class="avatar-info">
|
||||
<image
|
||||
class="avatar-img"
|
||||
src="{{avatarUrl}}"
|
||||
mode="aspectFill"
|
||||
bindtap="previewAvatar"
|
||||
/>
|
||||
<view class="user-info">
|
||||
<!-- 昵称行:去掉原称号标签 -->
|
||||
<view class="nickname-row">
|
||||
<text class="dashou-nicheng">{{dashouNicheng || '打手昵称'}}</text>
|
||||
<!-- 原称号标签已移除 -->
|
||||
</view>
|
||||
|
||||
<view class="uid-row">
|
||||
<text class="uid-label">UID:</text>
|
||||
<text class="uid-value">{{uid || '未获取'}}</text>
|
||||
<view class="copy-btn" bindtap="copyUid">
|
||||
<text class="copy-icon">⎘</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 🆕 称号标签展示区(自适应两行,超出可滑动) -->
|
||||
<view class="badge-area" wx:if="{{chenghaoList.length > 0}}">
|
||||
<scroll-view class="badge-scroll" scroll-y="true" enable-flex>
|
||||
<view class="badge-grid">
|
||||
<view class="badge-item" wx:for="{{chenghaoList}}" wx:key="index">
|
||||
<chenghao-tag
|
||||
mingcheng="{{item.mingcheng}}"
|
||||
texiaoJson="{{item.texiao_json}}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 其余部分完全不变 -->
|
||||
<!-- 资产区域 -->
|
||||
<view class="assets-section">
|
||||
<view class="black-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">资产总览</text>
|
||||
</view>
|
||||
|
||||
<view class="yongjin-row">
|
||||
<view class="yongjin-info">
|
||||
<text class="asset-label">我的佣金</text>
|
||||
<view class="asset-value-container">
|
||||
<text class="asset-value">{{yongjin || '0.00'}}</text>
|
||||
<text class="asset-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="withdraw-view-btn" bindtap="goToWithdraw">
|
||||
<text class="withdraw-text">提现</text>
|
||||
<view class="withdraw-glow"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="assets-bottom">
|
||||
<view class="bottom-item">
|
||||
<text class="bottom-label">积分</text>
|
||||
<text class="bottom-value">{{jinfen || '0'}}</text>
|
||||
</view>
|
||||
|
||||
<view class="vertical-divider"></view>
|
||||
|
||||
<view class="bottom-item">
|
||||
<text class="bottom-label">保证金</text>
|
||||
<text class="bottom-value">{{yajin || '0.00'}}元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能区域 -->
|
||||
<view class="function-section">
|
||||
<view class="main-function" bindtap="goToReceiveOrder">
|
||||
<text class="main-function-icon">🎮</text>
|
||||
<text class="main-function-text">抢单大厅</text>
|
||||
<view class="main-function-glow"></view>
|
||||
</view>
|
||||
|
||||
<view class="function-grid-second">
|
||||
<view class="function-item-second" bindtap="goToRecharge">
|
||||
<text class="function-icon-second">💳</text>
|
||||
<text class="function-text-second">充值会员保证金</text>
|
||||
</view>
|
||||
|
||||
<view class="function-item-second" bindtap="goToMyOrders">
|
||||
<text class="function-icon-second">📦</text>
|
||||
<text class="function-text-second">我的订单</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="function-grid-third">
|
||||
<view class="function-item-third" bindtap="goToMyPunishment">
|
||||
<text class="function-icon-third">⚖️</text>
|
||||
<text class="function-text-third">我的处罚</text>
|
||||
</view>
|
||||
|
||||
<view class="function-item-third" bindtap="goToRanking">
|
||||
<text class="function-icon-third">🏆</text>
|
||||
<text class="function-text-third">排行榜</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他功能区域 (邀请人等) 保持原样 -->
|
||||
|
||||
<view class="other-section">
|
||||
<view class="other-list">
|
||||
|
||||
|
||||
|
||||
<view class="other-item" wx:if="{{inviterCache && inviterCache.uid}}">
|
||||
<view class="other-item-left" bindtap="contactInviter">
|
||||
<image class="inviter-avatar" src="{{inviterCache.avatar}}" mode="aspectFill" />
|
||||
<view class="inviter-text-info">
|
||||
<text class="other-text">邀请人:{{inviterCache.nicheng || '未知'}}</text>
|
||||
<text class="inviter-uid-text">ID: {{inviterCache.uid}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="contact-badge" bindtap="contactInviter">联系</view>
|
||||
</view>
|
||||
<view class="other-item" wx:else bindtap="contactInviter">
|
||||
|
||||
<view class="other-item-left">
|
||||
<text class="other-icon">🤝</text>
|
||||
<text class="other-text">联系邀请人</text>
|
||||
</view>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
|
||||
<!-- 考核金牌(新增,放在最上面) -->
|
||||
<view class="other-item" bindtap="goToKaohe">
|
||||
<view class="other-item-left">
|
||||
<text class="other-icon">🏅</text>
|
||||
<text class="other-text">考核金牌</text>
|
||||
</view>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
|
||||
<view class="other-item" bindtap="goToModifyInfo">
|
||||
<view class="other-item-left">
|
||||
<text class="other-icon">✏️</text>
|
||||
<text class="other-text">修改个人信息</text>
|
||||
</view>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
|
||||
<view class="other-item" bindtap="goToRules">
|
||||
<view class="other-item-left">
|
||||
<text class="other-icon">📖</text>
|
||||
<text class="other-text">用户规则</text>
|
||||
</view>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载提示保持不变 -->
|
||||
<view class="loading-mask" wx:if="{{isLoading}}">
|
||||
<view class="loading-content">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
<custom-tab-bar />
|
||||
<popup-notice id="popupNotice" />
|
||||
860
miniprogram/pages/dashouduan/dashouduan.wxss
Normal file
@@ -0,0 +1,860 @@
|
||||
/* pages/dashouzhongxin/dashouzhongxin.wxss */
|
||||
|
||||
/* =========== 全局样式 =========== */
|
||||
page {
|
||||
background: linear-gradient(135deg, #121212 0%, #1a1a2e 30%, #16213e 70%, #0f3460 100%);
|
||||
min-height: 100vh;
|
||||
color: #FFFFFF;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* =========== 未注册状态页面 =========== */
|
||||
.unregistered-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 200rpx;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.status-tip {
|
||||
font-size: 32rpx;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 80rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
animation: fadeIn 1s ease-out forwards;
|
||||
line-height: 1.5;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
width: 85%;
|
||||
margin-bottom: 60rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.invite-input {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 20rpx;
|
||||
padding: 0 30rpx;
|
||||
font-size: 30rpx;
|
||||
color: #FFFFFF;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.3);
|
||||
transition: all 0.3s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.invite-input:focus {
|
||||
border-color: #FFD700;
|
||||
box-shadow: 0 15rpx 40rpx rgba(255, 215, 0, 0.25);
|
||||
transform: translateY(-2rpx);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.register-view-btn {
|
||||
width: 85%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
||||
border-radius: 40rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10rpx 30rpx rgba(255, 215, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.register-view-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.register-text {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
letter-spacing: 4rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-shadow: 0 2rpx 4rpx rgba(255, 255, 255, 0.2);
|
||||
line-height: 80rpx;
|
||||
}
|
||||
|
||||
.register-glow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.4),
|
||||
transparent);
|
||||
animation: shine 2s infinite;
|
||||
}
|
||||
|
||||
/* 一键注册按钮样式 */
|
||||
.quick-register-btn {
|
||||
width: 85%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
|
||||
border-radius: 40rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10rpx 30rpx rgba(76, 175, 80, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.quick-register-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.quick-register-text {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
letter-spacing: 4rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
|
||||
line-height: 80rpx;
|
||||
}
|
||||
|
||||
.quick-register-glow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.3),
|
||||
transparent);
|
||||
animation: shine 2s infinite;
|
||||
}
|
||||
|
||||
/* =========== 已注册状态页面 =========== */
|
||||
.registered-page {
|
||||
padding: 30rpx 25rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
position: absolute;
|
||||
top: 30rpx;
|
||||
right: 25rpx;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.25);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.refresh-btn:active {
|
||||
transform: rotate(180deg);
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.refresh-icon {
|
||||
font-size: 32rpx;
|
||||
color: #FFD700;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 头像区域 */
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30rpx;
|
||||
margin-top: 40rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 3rpx rgba(255, 215, 0, 0.5),
|
||||
0 8rpx 25rpx rgba(0, 0, 0, 0.4);
|
||||
margin-right: 25rpx;
|
||||
transition: transform 0.3s ease;
|
||||
background: linear-gradient(45deg, #2a2a2a, #1a1a1a);
|
||||
}
|
||||
|
||||
.avatar-img:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-left: 25rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nickname-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dashou-nicheng {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
margin-right: 15rpx;
|
||||
max-width: 200rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.chenghao-tag {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 215, 0, 0.9) 0%,
|
||||
rgba(255, 165, 0, 0.9) 100%);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.5);
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
|
||||
}
|
||||
|
||||
.chenghao-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-shadow: 0 1rpx 1rpx rgba(255, 255, 255, 0.8);
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.uid-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.uid-label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-right: 10rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uid-value {
|
||||
font-size: 26rpx;
|
||||
color: #FFFFFF;
|
||||
font-family: 'Courier New', monospace;
|
||||
margin-right: 15rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
padding: 6rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 215, 0, 0.1);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.3);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.copy-btn:active {
|
||||
transform: scale(0.95);
|
||||
background: rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FFD700;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 资产区域 */
|
||||
.assets-section {
|
||||
position: relative;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.black-card {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(30, 30, 46, 0.9) 0%,
|
||||
rgba(22, 33, 62, 0.9) 100%);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
position: relative;
|
||||
box-shadow:
|
||||
0 15rpx 40rpx rgba(0, 0, 0, 0.4);
|
||||
overflow: hidden;
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.15);
|
||||
min-height: 180rpx;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
margin-bottom: 25rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 28rpx;
|
||||
color: #FFD700;
|
||||
letter-spacing: 2rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.yongjin-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 25rpx;
|
||||
}
|
||||
|
||||
.yongjin-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.asset-label {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-weight: 400;
|
||||
margin-bottom: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.asset-value-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.asset-value {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #FFD700;
|
||||
font-family: 'Arial', sans-serif;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.asset-unit {
|
||||
font-size: 28rpx;
|
||||
color: #FFD700;
|
||||
margin-left: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.withdraw-view-btn {
|
||||
width: 120rpx;
|
||||
height: 55rpx;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 215, 0, 0.9) 0%,
|
||||
rgba(255, 165, 0, 0.9) 100%);
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow:
|
||||
0 6rpx 20rpx rgba(255, 215, 0, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.withdraw-view-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.withdraw-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
letter-spacing: 2rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-shadow: 0 1rpx 2rpx rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.assets-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 25rpx;
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.bottom-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bottom-label {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-bottom: 8rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.bottom-value {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #FFD700;
|
||||
}
|
||||
|
||||
.vertical-divider {
|
||||
width: 1rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(to bottom,
|
||||
transparent,
|
||||
rgba(255, 215, 0, 0.5) 50%,
|
||||
transparent);
|
||||
}
|
||||
|
||||
/* 功能区域 */
|
||||
.function-section {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx 25rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow:
|
||||
0 10rpx 25rpx rgba(0, 0, 0, 0.2);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 抢单大厅 */
|
||||
.main-function {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 35rpx 0;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 150, 255, 0.2) 0%,
|
||||
rgba(0, 100, 200, 0.2) 100%);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-bottom: 25rpx;
|
||||
border: 1rpx solid rgba(0, 150, 255, 0.3);
|
||||
}
|
||||
|
||||
.main-function:active {
|
||||
transform: scale(0.98);
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 150, 255, 0.3) 0%,
|
||||
rgba(0, 100, 200, 0.3) 100%);
|
||||
}
|
||||
|
||||
.main-function-icon {
|
||||
font-size: 60rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.main-function-text {
|
||||
font-size: 32rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.main-function-glow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.2),
|
||||
transparent);
|
||||
animation: shine 3s infinite;
|
||||
}
|
||||
|
||||
/* 第二行:充值和订单 */
|
||||
.function-grid-second {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.function-grid-second .function-item-second:nth-child(1) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 215, 0, 0.2) 0%,
|
||||
rgba(255, 165, 0, 0.2) 100%);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.4);
|
||||
box-shadow:
|
||||
0 8rpx 25rpx rgba(255, 215, 0, 0.3),
|
||||
0 0 15rpx rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
.function-grid-second .function-item-second:nth-child(1):active {
|
||||
transform: scale(0.96);
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 215, 0, 0.3) 0%,
|
||||
rgba(255, 165, 0, 0.3) 100%);
|
||||
}
|
||||
|
||||
.function-grid-second .function-item-second:nth-child(1) .function-icon-second {
|
||||
font-size: 48rpx;
|
||||
color: #FFD700;
|
||||
text-shadow:
|
||||
0 2rpx 8rpx rgba(255, 215, 0, 0.5),
|
||||
0 0 15rpx rgba(255, 215, 0, 0.3);
|
||||
animation: goldPulse 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.function-grid-second .function-item-second:nth-child(1) .function-text-second {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #FFD700;
|
||||
text-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.5);
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.function-grid-second .function-item-second:nth-child(1)::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.3),
|
||||
transparent);
|
||||
animation: shine 2.5s infinite;
|
||||
}
|
||||
|
||||
.function-item-second {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 25rpx 0;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 5rpx 15rpx rgba(0, 0, 0, 0.2);
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.function-item-second:active {
|
||||
transform: scale(0.96);
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.function-icon-second {
|
||||
font-size: 44rpx;
|
||||
margin-bottom: 15rpx;
|
||||
}
|
||||
|
||||
.function-text-second {
|
||||
font-size: 26rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 第三行:我的处罚和排行 */
|
||||
.function-grid-third {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.function-item-third {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 22rpx 0;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 4rpx 12rpx rgba(0, 0, 0, 0.2);
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.function-item-third:active {
|
||||
transform: scale(0.96);
|
||||
background: rgba(255, 255, 255, 0.09);
|
||||
}
|
||||
|
||||
.function-icon-third {
|
||||
font-size: 40rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.function-text-third {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 其他功能区域 */
|
||||
.other-section {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 20rpx;
|
||||
padding: 0 25rpx;
|
||||
box-shadow:
|
||||
0 10rpx 25rpx rgba(0, 0, 0, 0.2);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.1);
|
||||
}
|
||||
|
||||
.other-list {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.other-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 28rpx 0;
|
||||
border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.other-item:active {
|
||||
background: rgba(255, 215, 0, 0.05);
|
||||
transform: translateX(5rpx);
|
||||
}
|
||||
|
||||
.other-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.other-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.other-icon {
|
||||
font-size: 36rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.other-text {
|
||||
font-size: 28rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 36rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-weight: bold;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.other-item:active .arrow-icon {
|
||||
color: #FFD700;
|
||||
transform: translateX(5rpx);
|
||||
}
|
||||
|
||||
/* 邀请人专属样式(新增) */
|
||||
.inviter-avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
border: 2rpx solid rgba(255, 215, 0, 0.5);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.inviter-text-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.inviter-uid-text {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.contact-badge {
|
||||
background: rgba(255, 215, 0, 0.2);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.5);
|
||||
color: #FFD700;
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.contact-badge:active {
|
||||
background: rgba(255, 215, 0, 0.4);
|
||||
}
|
||||
|
||||
/* 加载遮罩 */
|
||||
.loading-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(18, 18, 18, 0.9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
background: rgba(30, 30, 46, 0.95);
|
||||
padding: 60rpx 80rpx;
|
||||
border-radius: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.5);
|
||||
border: 1rpx solid rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border: 6rpx solid rgba(255, 215, 0, 0.2);
|
||||
border-top: 6rpx solid #FFD700;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* =========== 动画定义 =========== */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(30rpx); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes goldPulse {
|
||||
0%, 100% { transform: scale(1); opacity: 0.9; }
|
||||
50% { transform: scale(1.05); opacity: 1; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ========== 🆕 称号标签展示区 ========== */
|
||||
/* ========== 🆕 称号标签展示区 ========== */
|
||||
.badge-area {
|
||||
width: 100%;
|
||||
margin: 20rpx 0 30rpx 0;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.badge-scroll {
|
||||
width: 100%;
|
||||
max-height: 140rpx; /* 最多两行高度(52*2=104 + 间距36rpx) */
|
||||
overflow-y: auto; /* 超出自动滚动 */
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.badge-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
padding: 6rpx 0;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.badge-item {
|
||||
flex-shrink: 0;
|
||||
/* 不设宽度限制,由组件自身宽度决定 */
|
||||
}
|
||||
|
||||
.other-icon-img {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
103
miniprogram/pages/dashouguize/dashouguize.js
Normal file
@@ -0,0 +1,103 @@
|
||||
Page({
|
||||
onShow() {
|
||||
// 原有代码...
|
||||
|
||||
// 🆕 注册通知组件
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
// 🆕 新增:注册通知组件
|
||||
registerNotificationComponent() {
|
||||
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()
|
||||
};
|
||||
}
|
||||
},
|
||||
data: {
|
||||
// 图片URL(带时间戳避免缓存)
|
||||
imageUrl: '',
|
||||
// 容器高度(初始为100vh,加载后调整为图片高度)
|
||||
containerHeight: 0
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 初始设置容器高度为屏幕高度
|
||||
this.getSystemInfo()
|
||||
// 构建图片URL(带时间戳)
|
||||
this.buildImageUrl()
|
||||
},
|
||||
|
||||
// 获取系统信息
|
||||
getSystemInfo() {
|
||||
const res = wx.getSystemInfoSync()
|
||||
this.setData({
|
||||
containerHeight: res.windowHeight
|
||||
})
|
||||
},
|
||||
|
||||
// 构建图片URL(关键:加时间戳避免缓存)
|
||||
buildImageUrl() {
|
||||
const app = getApp()
|
||||
const ossImageUrl = app.globalData.ossImageUrl || ''
|
||||
const dashouguize = app.globalData.dashouguize || ''
|
||||
|
||||
// 加上时间戳参数,强制每次重新加载
|
||||
const timestamp = new Date().getTime()
|
||||
const fullImageUrl = ossImageUrl + dashouguize + '?t=' + timestamp
|
||||
|
||||
//console.log('规则图片URL(带时间戳):', fullImageUrl)
|
||||
|
||||
this.setData({
|
||||
imageUrl: fullImageUrl
|
||||
})
|
||||
},
|
||||
|
||||
// 图片加载成功
|
||||
onImageLoad(e) {
|
||||
//console.log('图片加载成功,原始尺寸:', e.detail.width, 'x', e.detail.height)
|
||||
|
||||
// 根据图片实际高度设置容器高度
|
||||
// 图片宽度为屏幕宽度,高度按比例计算
|
||||
const systemInfo = wx.getSystemInfoSync()
|
||||
const screenWidth = systemInfo.screenWidth // 单位:px
|
||||
|
||||
// 图片的实际像素尺寸
|
||||
const imageWidth = e.detail.width
|
||||
const imageHeight = e.detail.height
|
||||
|
||||
// 计算在小程序中显示的高度(保持比例)
|
||||
// 图片在小程序中宽度为100%屏幕宽度,高度按比例缩放
|
||||
const displayHeight = (imageHeight / imageWidth) * screenWidth
|
||||
|
||||
//console.log('计算后显示高度:', displayHeight, 'px')
|
||||
|
||||
// 设置容器高度为图片高度
|
||||
this.setData({
|
||||
containerHeight: displayHeight
|
||||
})
|
||||
},
|
||||
|
||||
// 图片加载失败
|
||||
onImageError() {
|
||||
console.error('图片加载失败')
|
||||
wx.showToast({
|
||||
title: '图片加载失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
// 重新构建图片URL(带新时间戳)
|
||||
this.buildImageUrl()
|
||||
// 停止下拉刷新
|
||||
wx.stopPullDownRefresh()
|
||||
}
|
||||
})
|
||||
11
miniprogram/pages/dashouguize/dashouguize.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
"navigationBarTitleText": "用户规则",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "dark"
|
||||
}
|
||||
12
miniprogram/pages/dashouguize/dashouguize.wxml
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- 打手规则页面 - 图片完整覆盖 -->
|
||||
<view class="container" style="height:{{containerHeight}}px">
|
||||
<image
|
||||
src="{{ imageUrl }}"
|
||||
mode="widthFix"
|
||||
class="rule-image"
|
||||
bindload="onImageLoad"
|
||||
binderror="onImageError"
|
||||
style="width:100%;height:auto"
|
||||
></image>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
15
miniprogram/pages/dashouguize/dashouguize.wxss
Normal file
@@ -0,0 +1,15 @@
|
||||
/* 页面容器 */
|
||||
.container {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 规则图片样式 - 绝对定位,从顶部开始 */
|
||||
.rule-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* 统一排行榜(打手/管事/组长/商家)
|
||||
* 统一排行榜(打手/管事/组长/商家)— 对齐星雀 fighter-rank,排序按收益/流水
|
||||
* POST /yonghu/phbhqsj
|
||||
*/
|
||||
import request from '../../utils/request.js'
|
||||
import { resolveAvatarUrl, getDefaultAvatarUrl } from '../../utils/avatar.js'
|
||||
import { getPaihangImgUrls } from '../../utils/page-assets.js'
|
||||
|
||||
const RANK_API = '/yonghu/phbhqsj'
|
||||
|
||||
@@ -32,8 +33,8 @@ const ROLE_META = {
|
||||
mainType: 'money',
|
||||
metrics: [
|
||||
{ field: 'jiedan_zongliang', label: '接单量', type: 'int' },
|
||||
{ field: 'chengjiao_zongliang', label: '成交量', type: 'int' },
|
||||
{ field: 'jiedan_zonge', label: '接单额', type: 'money' },
|
||||
{ field: 'chengjiao_zongliang', label: '成交量', type: 'int' },
|
||||
],
|
||||
},
|
||||
guanshi: {
|
||||
@@ -71,7 +72,7 @@ const ROLE_META = {
|
||||
|
||||
function buildAvatar(path, app) {
|
||||
const def = getDefaultAvatarUrl(app)
|
||||
if (!path) return def
|
||||
if (!path || path === 'null' || path === 'undefined') return def
|
||||
return resolveAvatarUrl(path, app) || def
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ Page({
|
||||
currentRole: 'dashou',
|
||||
currentDate: '今日',
|
||||
roleMeta: ROLE_META.dashou,
|
||||
sortLabel: '成交总额',
|
||||
sortLabel: '收益',
|
||||
isLoading: true,
|
||||
rankList: [],
|
||||
topThree: [],
|
||||
@@ -95,34 +96,34 @@ Page({
|
||||
onLoad(options) {
|
||||
const type = options.type || options.rankType || 'dashou'
|
||||
const validRole = ROLE_META[type] ? type : 'dashou'
|
||||
this._onConfigReady = () => this.initPageAssets()
|
||||
getApp().on('configReady', this._onConfigReady)
|
||||
this.initPageAssets()
|
||||
this.applyRole(validRole, false)
|
||||
this.fetchRankList()
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
if (this._onConfigReady) {
|
||||
getApp().off('configReady', this._onConfigReady)
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.initPageAssets()
|
||||
this.fetchRankList().finally(() => wx.stopPullDownRefresh())
|
||||
},
|
||||
|
||||
initPageAssets() {
|
||||
this.syncDefaultAvatar()
|
||||
const g = getApp().globalData
|
||||
const oss = (g.ossImageUrl || '').replace(/\/$/, '')
|
||||
const assets = g.paihangAssets || {}
|
||||
const url = (p) => (p ? `${oss}/${String(p).replace(/^\//, '')}` : '')
|
||||
this.setData({
|
||||
imgUrls: {
|
||||
pageBg: url(assets.pageBg) || `${oss}/beijing/paihangbang/page-bg.jpg`,
|
||||
cardBg: url(assets.cardBg) || `${oss}/beijing/paihangbang/list-card-bg.png`,
|
||||
emptyIcon: url(assets.emptyIcon) || `${oss}/beijing/paihangbang/empty.png`,
|
||||
refreshIcon: url(assets.refreshIcon) || `${oss}/beijing/paihangbang/icon-refresh.png`,
|
||||
},
|
||||
})
|
||||
this.setData({ imgUrls: getPaihangImgUrls() })
|
||||
},
|
||||
|
||||
syncDefaultAvatar() {
|
||||
const def = getDefaultAvatarUrl()
|
||||
if (def) this.setData({ defaultAvatar: def })
|
||||
if (def && def !== this.data.defaultAvatar) {
|
||||
this.setData({ defaultAvatar: def })
|
||||
}
|
||||
},
|
||||
|
||||
ensureAvatars() {
|
||||
@@ -134,7 +135,8 @@ Page({
|
||||
this.setData({ defaultAvatar: def })
|
||||
return
|
||||
}
|
||||
const isValid = (url) => url && typeof url === 'string' && (url.startsWith('http') || (oss && url.indexOf(oss) === 0))
|
||||
const isValid = (url) =>
|
||||
url && typeof url === 'string' && (url.startsWith('http') || (oss && url.indexOf(oss) === 0))
|
||||
const fix = (item) => ({ ...item, avatar: isValid(item.avatar) ? item.avatar : def })
|
||||
this.setData({
|
||||
defaultAvatar: def,
|
||||
@@ -165,6 +167,7 @@ Page({
|
||||
},
|
||||
|
||||
onRefreshTap() {
|
||||
this.initPageAssets()
|
||||
this.fetchRankList()
|
||||
},
|
||||
|
||||
@@ -256,6 +259,7 @@ Page({
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.initPageAssets()
|
||||
this.syncDefaultAvatar()
|
||||
this.ensureAvatars()
|
||||
this.registerNotificationComponent()
|
||||
|
||||
@@ -75,6 +75,7 @@ page {
|
||||
font-weight: 600;
|
||||
background: rgba(107, 163, 247, 0.45);
|
||||
border-color: rgba(107, 163, 247, 0.65);
|
||||
box-shadow: 0 0 20rpx rgba(107, 163, 247, 0.2);
|
||||
}
|
||||
|
||||
.date-grid {
|
||||
@@ -183,16 +184,19 @@ page {
|
||||
.badge-first {
|
||||
color: #fff;
|
||||
background: linear-gradient(145deg, #6BA3F7, #4A7FD4);
|
||||
box-shadow: 0 4rpx 16rpx rgba(107, 163, 247, 0.4);
|
||||
}
|
||||
|
||||
.badge-silver {
|
||||
color: #fff;
|
||||
background: linear-gradient(145deg, #A8B4C4, #7A8899);
|
||||
box-shadow: 0 4rpx 12rpx rgba(168, 180, 196, 0.25);
|
||||
}
|
||||
|
||||
.badge-bronze {
|
||||
color: #fff;
|
||||
background: linear-gradient(145deg, #C4A882, #9A7858);
|
||||
box-shadow: 0 4rpx 12rpx rgba(196, 168, 130, 0.25);
|
||||
}
|
||||
|
||||
.p-avatar-wrap {
|
||||
@@ -201,7 +205,12 @@ page {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.wrap-first { padding: 5rpx; background: linear-gradient(145deg, #8EB8FF, #5B8FD8); }
|
||||
.wrap-first {
|
||||
padding: 5rpx;
|
||||
background: linear-gradient(145deg, #8EB8FF, #5B8FD8);
|
||||
box-shadow: 0 6rpx 24rpx rgba(107, 163, 247, 0.3);
|
||||
}
|
||||
|
||||
.wrap-silver { background: linear-gradient(145deg, #C8D0DA, #909AA8); }
|
||||
.wrap-bronze { background: linear-gradient(145deg, #D4B896, #A88860); }
|
||||
|
||||
@@ -222,6 +231,7 @@ page {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.p-name-first { font-size: 28rpx; color: #fff; }
|
||||
@@ -270,6 +280,16 @@ page {
|
||||
.p-pedestal {
|
||||
width: 88%;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-pedestal::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 10%; right: 10%;
|
||||
height: 2rpx;
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
.pedestal-first {
|
||||
@@ -335,6 +355,7 @@ page {
|
||||
align-items: center;
|
||||
padding: 20rpx 22rpx;
|
||||
background: rgba(12, 16, 28, 0.92);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.lc-rank {
|
||||
|
||||
684
miniprogram/pages/dashouxiaoxi/dashouxiaoxi.js
Normal file
@@ -0,0 +1,684 @@
|
||||
// pages/cfss/cfss.js
|
||||
import request from '../../utils/request.js'
|
||||
const app = getApp()
|
||||
const MEIYE_TIAOSHU = 5
|
||||
|
||||
Page({
|
||||
data: {
|
||||
zongshu: 0,
|
||||
daichuli: 0,
|
||||
yichuli: 0,
|
||||
shenfen: 0,
|
||||
chufaList: [],
|
||||
dangqianye: 1,
|
||||
haiyougengduo: true,
|
||||
jiazhaozhong: false,
|
||||
jiazhaigengduo: false,
|
||||
scrollHeight: 0,
|
||||
showXiangqing: false,
|
||||
xuanzhongChufa: {},
|
||||
showShensuModal: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
shensuTupianUrls: [],
|
||||
shangchuanJindu: 0,
|
||||
shangchuanZongshu: 0,
|
||||
jinduWidth: '0%',
|
||||
isShangchuanzhong: false,
|
||||
ossImageUrl: '',
|
||||
fangdouTimer: null
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.registerNotificationComponent()
|
||||
this.initOSSUrl()
|
||||
this.jisuanGaodu()
|
||||
this.chushihuaShuju()
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
onShow() {
|
||||
this.registerNotificationComponent()
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer)
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
this.shanglaShuaxin()
|
||||
},
|
||||
|
||||
registerNotificationComponent() {
|
||||
const notificationComp = this.selectComponent('#global-notification')
|
||||
if (notificationComp && notificationComp.showNotification) {
|
||||
app.globalData.globalNotification = {
|
||||
show: (data) => notificationComp.showNotification(data),
|
||||
hide: () => notificationComp.hideNotification()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initOSSUrl() {
|
||||
const ossImageUrl = app.globalData.ossImageUrl || ''
|
||||
this.setData({ ossImageUrl })
|
||||
},
|
||||
|
||||
jisuanGaodu() {
|
||||
const systemInfo = wx.getSystemInfoSync()
|
||||
const windowHeight = systemInfo.windowHeight
|
||||
const height = windowHeight - 200 - 100 - 40
|
||||
this.setData({
|
||||
scrollHeight: height > 0 ? height : 400
|
||||
})
|
||||
},
|
||||
|
||||
chushihuaShuju() {
|
||||
this.setData({
|
||||
dangqianye: 1,
|
||||
chufaList: [],
|
||||
haiyougengduo: true
|
||||
})
|
||||
this.jiazhuoquTongji()
|
||||
this.jiazhuoquChufaList()
|
||||
},
|
||||
|
||||
async jiazhuoquTongji() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/dshqcfjl',
|
||||
method: 'POST',
|
||||
data: { qingqiu_tongji: true }
|
||||
})
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data
|
||||
this.setData({
|
||||
zongshu: data.zongshu || 0,
|
||||
daichuli: data.daichuli || 0,
|
||||
yichuli: data.yichuli || 0
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计信息失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
async jiazhuoquChufaList(isLoadMore = false) {
|
||||
if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return
|
||||
if (isLoadMore) {
|
||||
if (!this.data.haiyougengduo) return
|
||||
this.setData({ jiazhaigengduo: true })
|
||||
} else {
|
||||
this.setData({ jiazhaozhong: true })
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
page: this.data.dangqianye,
|
||||
page_size: MEIYE_TIAOSHU
|
||||
}
|
||||
if (this.data.shenfen === 0) {
|
||||
params.zhuangtai = 0
|
||||
} else {
|
||||
params.zhuangtai = 1
|
||||
}
|
||||
|
||||
const res = await request({
|
||||
url: '/yonghu/dshqcfjl',
|
||||
method: 'POST',
|
||||
data: params
|
||||
})
|
||||
|
||||
//console.log('🔴【后端返回的数据】:', res.data)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data
|
||||
|
||||
if (data.list && Array.isArray(data.list)) {
|
||||
// 🔴【关键修复】在数据加载时就处理好显示字段
|
||||
const processedList = data.list.map(item => {
|
||||
// 处理显示时间
|
||||
let displayTime = '--'
|
||||
if (item.create_time) {
|
||||
if (typeof item.create_time === 'number') {
|
||||
displayTime = String(item.create_time)
|
||||
} else if (typeof item.create_time === 'string') {
|
||||
displayTime = item.create_time
|
||||
} else {
|
||||
displayTime = String(item.create_time)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理显示状态
|
||||
let displayStatus = '未知状态'
|
||||
let statusClass = 'zhuangtai-weizhi'
|
||||
const statusNum = Number(item.sqzhuangtai)
|
||||
|
||||
if (!isNaN(statusNum)) {
|
||||
if (statusNum === 0) {
|
||||
displayStatus = '待处罚'
|
||||
statusClass = 'zhuangtai-daichuli'
|
||||
} else if (statusNum === 1) {
|
||||
displayStatus = '已处罚'
|
||||
statusClass = 'zhuangtai-yichufa'
|
||||
} else if (statusNum === 2) {
|
||||
displayStatus = '已驳回'
|
||||
statusClass = 'zhuangtai-yibohui'
|
||||
} else if (statusNum === 3) {
|
||||
displayStatus = '申诉中'
|
||||
statusClass = 'zhuangtai-shensuzhong'
|
||||
}
|
||||
}
|
||||
|
||||
// 处理图片URL(在数据加载时就拼接完整URL)
|
||||
const fullZhengjuTupian = (item.zhengju_tupian || []).map(url => this.getFullImageUrl(url))
|
||||
const fullShensuTupian = (item.shensu_tupian || []).map(url => this.getFullImageUrl(url))
|
||||
|
||||
return {
|
||||
...item,
|
||||
display_time: displayTime, // 🔴 处理好的显示时间
|
||||
display_status: displayStatus, // 🔴 处理好的显示状态
|
||||
status_class: statusClass, // 🔴 处理好的状态样式类
|
||||
zhengju_tupian: item.zhengju_tupian || [],
|
||||
shensu_tupian: item.shensu_tupian || [],
|
||||
full_zhengju_tupian: fullZhengjuTupian, // 🔴 完整图片URL
|
||||
full_shensu_tupian: fullShensuTupian // 🔴 完整图片URL
|
||||
}
|
||||
})
|
||||
|
||||
//console.log('🔴【处理后的数据】:', processedList)
|
||||
|
||||
let newList
|
||||
if (isLoadMore) {
|
||||
newList = [...this.data.chufaList, ...processedList]
|
||||
} else {
|
||||
newList = processedList
|
||||
}
|
||||
|
||||
const hasMore = data.has_more === true
|
||||
this.setData({
|
||||
chufaList: newList,
|
||||
haiyougengduo: hasMore,
|
||||
dangqianye: this.data.dangqianye + 1
|
||||
})
|
||||
} else {
|
||||
this.setData({ haiyougengduo: false })
|
||||
}
|
||||
} else {
|
||||
throw new Error(res.data?.msg || '加载处罚记录失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载处罚记录失败:', error)
|
||||
wx.showToast({
|
||||
title: error.message || '加载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({ haiyougengduo: false })
|
||||
} finally {
|
||||
if (isLoadMore) {
|
||||
this.setData({ jiazhaigengduo: false })
|
||||
} else {
|
||||
this.setData({ jiazhaozhong: false })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
qiehuanShenfen(e) {
|
||||
const type = parseInt(e.currentTarget.dataset.type)
|
||||
if (this.data.shenfen === type) return
|
||||
this.setData({
|
||||
shenfen: type,
|
||||
dangqianye: 1,
|
||||
chufaList: [],
|
||||
haiyougengduo: true
|
||||
})
|
||||
this.jiazhuoquChufaList()
|
||||
},
|
||||
|
||||
shanglaShuaxin() {
|
||||
if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer)
|
||||
const timer = setTimeout(() => {
|
||||
if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return
|
||||
if (!this.data.haiyougengduo) return
|
||||
this.jiazhuoquChufaList(true)
|
||||
}, 300)
|
||||
this.setData({ fangdouTimer: timer })
|
||||
},
|
||||
|
||||
chakanXiangqing(e) {
|
||||
const item = e.currentTarget.dataset.item
|
||||
this.setData({
|
||||
xuanzhongChufa: item,
|
||||
showXiangqing: true
|
||||
})
|
||||
},
|
||||
|
||||
guanbiXiangqing() {
|
||||
this.setData({
|
||||
showXiangqing: false,
|
||||
xuanzhongChufa: {}
|
||||
})
|
||||
},
|
||||
|
||||
openShensuModal() {
|
||||
if (this.data.xuanzhongChufa.ssliyou ||
|
||||
(this.data.xuanzhongChufa.shensu_tupian && this.data.xuanzhongChufa.shensu_tupian.length > 0)) {
|
||||
wx.showToast({
|
||||
title: '您已经提交过申诉,请等待处理结果',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
showShensuModal: true,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
shensuTupianUrls: [],
|
||||
shangchuanJindu: 0,
|
||||
shangchuanZongshu: 0,
|
||||
jinduWidth: '0%'
|
||||
})
|
||||
},
|
||||
|
||||
closeShensuModal() {
|
||||
this.setData({
|
||||
showShensuModal: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
shensuTupianUrls: []
|
||||
})
|
||||
},
|
||||
|
||||
onShensuLiyouInput(e) {
|
||||
const value = e.detail.value.slice(0, 500)
|
||||
this.setData({ shensuLiyou: value })
|
||||
},
|
||||
|
||||
chooseShensuTupian() {
|
||||
if (this.data.isShangchuanzhong) return
|
||||
const shengyu = 9 - this.data.shensuTupian.length
|
||||
if (shengyu <= 0) {
|
||||
wx.showToast({
|
||||
title: '最多只能上传9张图片',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
wx.chooseMedia({
|
||||
count: shengyu,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
const newTupian = [...this.data.shensuTupian, ...res.tempFiles.map(file => file.tempFilePath)]
|
||||
this.setData({ shensuTupian: newTupian.slice(0, 9) })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
deleteShensuTupian(e) {
|
||||
const index = e.currentTarget.dataset.index
|
||||
const newTupian = [...this.data.shensuTupian]
|
||||
newTupian.splice(index, 1)
|
||||
this.setData({ shensuTupian: newTupian })
|
||||
},
|
||||
|
||||
yulanShensuTupian(e) {
|
||||
const index = e.currentTarget.dataset.index
|
||||
const urls = this.data.shensuTupian
|
||||
if (!urls[index]) return
|
||||
wx.previewImage({
|
||||
current: urls[index],
|
||||
urls: urls
|
||||
})
|
||||
},
|
||||
|
||||
lianxiKefu() {
|
||||
const kefuLink = app.globalData.kefuConfig?.link || '';
|
||||
const corpId = app.globalData.kefuConfig?.enterpriseId || '';
|
||||
|
||||
if (!kefuLink || !corpId) {
|
||||
wx.showToast({
|
||||
title: '客服配置未加载',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: { url: kefuLink },
|
||||
corpId: corpId,
|
||||
success: (res) => {
|
||||
console.log('跳转客服成功', res);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('跳转客服失败', err);
|
||||
wx.showToast({
|
||||
title: '暂时无法连接客服',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async getCOSZhengshu() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/dingdan/dsscpz',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdan_id: this.data.xuanzhongChufa.dingdan_id,
|
||||
yongtu: 'chufa'
|
||||
}
|
||||
})
|
||||
if (res.statusCode === 200 && res.data.code === 0 && res.data.data) {
|
||||
return res.data.data
|
||||
} else {
|
||||
throw new Error(res?.data?.msg || '获取上传凭证失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取COS凭证失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
initCOSClient(tokenData) {
|
||||
const COS = require('../../utils/cos-wx-sdk-v5.js')
|
||||
const credentials = tokenData.credentials || tokenData
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063'
|
||||
const region = tokenData.region || 'ap-shanghai'
|
||||
const cos = new COS({
|
||||
SimpleUploadMethod: 'putObject',
|
||||
getAuthorization: async (options, callback) => {
|
||||
const authParams = {
|
||||
TmpSecretId: credentials.tmpSecretId,
|
||||
TmpSecretKey: credentials.tmpSecretKey,
|
||||
SecurityToken: credentials.sessionToken || '',
|
||||
StartTime: tokenData.startTime || Math.floor(Date.now() / 1000),
|
||||
ExpiredTime: tokenData.expiredTime || (Math.floor(Date.now() / 1000) + 7200)
|
||||
}
|
||||
callback(authParams)
|
||||
}
|
||||
})
|
||||
return { cos, bucket, region }
|
||||
},
|
||||
|
||||
async shangchuanDanZhangTupian(filePath, cosKey, index, cosInstance, bucket, region) {
|
||||
return new Promise((resolve) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
resolve({ status: 'optimistic_success', key: cosKey })
|
||||
}, 2000)
|
||||
cosInstance.uploadFile({
|
||||
Bucket: bucket,
|
||||
Region: region,
|
||||
Key: cosKey,
|
||||
FilePath: filePath,
|
||||
onProgress: (progressInfo) => {
|
||||
const percent = Math.round(progressInfo.percent * 100)
|
||||
if (percent === 100) {
|
||||
clearTimeout(timeoutId)
|
||||
resolve({ status: 'optimistic_success', key: cosKey })
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
async piliangShangchuanShensuTupian() {
|
||||
const total = this.data.shensuTupian.length
|
||||
if (total === 0) return []
|
||||
this.setData({
|
||||
shangchuanZongshu: total,
|
||||
shangchuanJindu: 0,
|
||||
jinduWidth: '0%',
|
||||
isShangchuanzhong: true
|
||||
})
|
||||
let yonghuid = ''
|
||||
|
||||
// 方案1:直接从全局缓存获取
|
||||
const uid = wx.getStorageSync('uid')
|
||||
if (uid) {
|
||||
yonghuid = String(uid).padStart(7, '0')
|
||||
//console.log('🔴【从uid获取yonghuid】', yonghuid)
|
||||
}
|
||||
|
||||
// 方案2:如果还没有,从用户信息获取
|
||||
if (!yonghuid && app.globalData.userInfo && app.globalData.userInfo.yonghuid) {
|
||||
yonghuid = String(app.globalData.userInfo.yonghuid).padStart(7, '0')
|
||||
//console.log('🔴【从userInfo获取yonghuid】', yonghuid)
|
||||
}
|
||||
|
||||
// 方案3:如果还没有,直接提示并返回
|
||||
if (!yonghuid) {
|
||||
wx.showToast({
|
||||
title: '请先登录',
|
||||
icon: 'none'
|
||||
})
|
||||
this.setData({ isShangchuanzhong: false })
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
|
||||
const preGeneratedUrls = []
|
||||
for (let i = 0; i < total; i++) {
|
||||
const timestamp = Date.now() + i
|
||||
const random = Math.floor(Math.random() * 10000)
|
||||
const fileExt = this.getFileExtension(this.data.shensuTupian[i])
|
||||
const fileName = `${yonghuid}_${timestamp}_${random}${fileExt}`
|
||||
const cosKey = `a_long/chfajltp/dssstp/${fileName}`
|
||||
preGeneratedUrls.push(cosKey)
|
||||
}
|
||||
|
||||
let cosClient, bucket, region
|
||||
try {
|
||||
const tokenData = await this.getCOSZhengshu()
|
||||
const { cos, bucket: cosBucket, region: cosRegion } = this.initCOSClient(tokenData)
|
||||
cosClient = cos
|
||||
bucket = cosBucket
|
||||
region = cosRegion
|
||||
} catch (error) {
|
||||
wx.showToast({ title: '上传初始化失败', icon: 'none' })
|
||||
this.setData({ isShangchuanzhong: false })
|
||||
return []
|
||||
}
|
||||
|
||||
const uploadTasks = []
|
||||
for (let i = 0; i < total; i++) {
|
||||
const task = this.shangchuanDanZhangTupian(
|
||||
this.data.shensuTupian[i],
|
||||
preGeneratedUrls[i],
|
||||
i,
|
||||
cosClient,
|
||||
bucket,
|
||||
region
|
||||
).then((result) => {
|
||||
const currentDone = i + 1
|
||||
const jinduPercent = ((currentDone / total) * 100).toFixed(0)
|
||||
this.setData({
|
||||
shangchuanJindu: currentDone,
|
||||
jinduWidth: `${jinduPercent}%`
|
||||
})
|
||||
return result
|
||||
})
|
||||
uploadTasks.push(task)
|
||||
}
|
||||
await Promise.all(uploadTasks)
|
||||
this.setData({ isShangchuanzhong: false })
|
||||
return preGeneratedUrls
|
||||
},
|
||||
|
||||
getFileExtension(filePath) {
|
||||
const lastDotIndex = filePath.lastIndexOf('.')
|
||||
if (lastDotIndex === -1) return '.jpg'
|
||||
const ext = filePath.substring(lastDotIndex).toLowerCase()
|
||||
const allowedExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
|
||||
return allowedExts.includes(ext) ? ext : '.jpg'
|
||||
},
|
||||
|
||||
async submitShensu() {
|
||||
if (!this.data.shensuLiyou.trim()) {
|
||||
wx.showToast({ title: '请输入申诉理由', icon: 'none' }); return
|
||||
}
|
||||
if (this.data.shensuTupian.length === 0) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '未上传任何图片,确定要提交申诉吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) await this.tijiaoShensuData([])
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
wx.showToast({ title: '开始上传图片...', icon: 'none' })
|
||||
try {
|
||||
const tupianUrls = await this.piliangShangchuanShensuTupian()
|
||||
if (!tupianUrls || tupianUrls.length === 0) throw new Error('图片上传失败')
|
||||
await this.tijiaoShensuData(tupianUrls)
|
||||
} catch (error) {
|
||||
console.error('提交申诉失败:', error)
|
||||
wx.showToast({ title: error.message || '提交失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
async tijiaoShensuData(tupianUrls) {
|
||||
wx.showToast({ title: '提交申诉中...', icon: 'none' })
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/dscfss',
|
||||
method: 'POST',
|
||||
data: {
|
||||
chufa_id: this.data.xuanzhongChufa.id,
|
||||
shensu_liyou: this.data.shensuLiyou,
|
||||
shensu_tupian_urls: tupianUrls
|
||||
}
|
||||
})
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
this.updateChufaRecord({
|
||||
sqzhuangtai: 3,
|
||||
ssliyou: this.data.shensuLiyou,
|
||||
shensu_tupian: tupianUrls
|
||||
})
|
||||
this.setData({
|
||||
showShensuModal: false,
|
||||
showXiangqing: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
xuanzhongChufa: {}
|
||||
})
|
||||
wx.showToast({ title: '申诉提交成功,请等待处理', icon: 'success' })
|
||||
setTimeout(() => { this.chushihuaShuju() }, 500)
|
||||
} else {
|
||||
throw new Error(res.data?.msg || '提交申诉失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交申诉数据失败:', error)
|
||||
wx.showToast({ title: error.message || '提交失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
updateChufaRecord(newData) {
|
||||
const newList = this.data.chufaList.map(item => {
|
||||
if (item.id === this.data.xuanzhongChufa.id) {
|
||||
const updatedItem = { ...item, ...newData }
|
||||
// 🔴 更新显示字段
|
||||
if (newData.sqzhuangtai === 3) {
|
||||
updatedItem.display_status = '申诉中'
|
||||
updatedItem.status_class = 'zhuangtai-shensuzhong'
|
||||
}
|
||||
if (newData.shensu_tupian) {
|
||||
updatedItem.full_shensu_tupian = newData.shensu_tupian.map(url => this.getFullImageUrl(url))
|
||||
}
|
||||
return updatedItem
|
||||
}
|
||||
return item
|
||||
})
|
||||
this.setData({ chufaList: newList })
|
||||
},
|
||||
|
||||
// 🔴【图片URL拼接 - 确保返回完整URL】
|
||||
getFullImageUrl(relativeUrl) {
|
||||
//console.log('🔴【图片拼接】输入:', relativeUrl)
|
||||
|
||||
if (!relativeUrl) return ''
|
||||
if (relativeUrl.startsWith('http')) {
|
||||
//console.log('🔴【已经是完整URL】返回:', relativeUrl)
|
||||
return relativeUrl
|
||||
}
|
||||
|
||||
const ossUrl = this.data.ossImageUrl
|
||||
if (!ossUrl) {
|
||||
//console.log('🔴【OSS地址为空】返回原始URL:', relativeUrl)
|
||||
return relativeUrl
|
||||
}
|
||||
|
||||
let fullUrl
|
||||
if (ossUrl.endsWith('/') && relativeUrl.startsWith('/')) {
|
||||
fullUrl = ossUrl + relativeUrl.substring(1)
|
||||
} else if (!ossUrl.endsWith('/') && !relativeUrl.startsWith('/')) {
|
||||
fullUrl = ossUrl + '/' + relativeUrl
|
||||
} else {
|
||||
fullUrl = ossUrl + relativeUrl
|
||||
}
|
||||
|
||||
// console.log('🔴【拼接后URL】:', fullUrl)
|
||||
return fullUrl
|
||||
},
|
||||
|
||||
yulanTupian(e) {
|
||||
const currentUrl = e.currentTarget.dataset.url
|
||||
const urls = e.currentTarget.dataset.urls || []
|
||||
if (!currentUrl) return
|
||||
|
||||
const fullCurrentUrl = currentUrl.startsWith('http') ? currentUrl : this.getFullImageUrl(currentUrl)
|
||||
const fullUrls = urls.map(url => url.startsWith('http') ? url : this.getFullImageUrl(url))
|
||||
|
||||
wx.previewImage({
|
||||
current: fullCurrentUrl,
|
||||
urls: fullUrls.length > 0 ? fullUrls : [fullCurrentUrl]
|
||||
})
|
||||
},
|
||||
|
||||
fuzhiWenben(e) {
|
||||
const text = e.currentTarget.dataset.text
|
||||
if (!text) return
|
||||
|
||||
wx.setClipboardData({
|
||||
data: text,
|
||||
success: () => {
|
||||
wx.showToast({ title: '复制成功', icon: 'success' })
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({ title: '复制失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 🔴【保留原函数但已不再使用(用于向后兼容)】
|
||||
formatShijian(shijian) {
|
||||
if (!shijian) return '--'
|
||||
return String(shijian)
|
||||
},
|
||||
|
||||
getZhuangtaiText(zhuangtai) {
|
||||
const statusNum = Number(zhuangtai)
|
||||
if (statusNum === 0) return '待处罚'
|
||||
if (statusNum === 1) return '已处罚'
|
||||
if (statusNum === 2) return '已驳回'
|
||||
if (statusNum === 3) return '申诉中'
|
||||
return '未知状态'
|
||||
},
|
||||
|
||||
getZhuangtaiClass(zhuangtai) {
|
||||
const statusNum = Number(zhuangtai)
|
||||
if (statusNum === 0) return 'zhuangtai-daichuli'
|
||||
if (statusNum === 1) return 'zhuangtai-yichufa'
|
||||
if (statusNum === 2) return 'zhuangtai-yibohui'
|
||||
if (statusNum === 3) return 'zhuangtai-shensuzhong'
|
||||
return 'zhuangtai-weizhi'
|
||||
}
|
||||
})
|
||||
10
miniprogram/pages/dashouxiaoxi/dashouxiaoxi.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
},
|
||||
"navigationBarTitleText": "处罚记录",
|
||||
"navigationBarBackgroundColor": "#0a0a12",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0a0a12",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
316
miniprogram/pages/dashouxiaoxi/dashouxiaoxi.wxml
Normal file
@@ -0,0 +1,316 @@
|
||||
<!-- pages/cfss/cfss.wxml -->
|
||||
<view class="cfss-container">
|
||||
<!-- 顶部统计卡片 -->
|
||||
<view class="tongji-card">
|
||||
<view class="tongji-title">处罚统计</view>
|
||||
<view class="tongji-content">
|
||||
<view class="tongji-item">
|
||||
<text class="tongji-label">总记录</text>
|
||||
<text class="tongji-value">{{ zongshu }}</text>
|
||||
</view>
|
||||
<view class="tongji-item">
|
||||
<text class="tongji-label">待处理</text>
|
||||
<text class="tongji-value daichuli-value">{{ daichuli }}</text>
|
||||
</view>
|
||||
<view class="tongji-item">
|
||||
<text class="tongji-label">已处理</text>
|
||||
<text class="tongji-value yichuli-value">{{ yichuli }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 类型切换 -->
|
||||
<view class="leixing-qiehuan">
|
||||
<view
|
||||
class="leixing-item {{ shenfen === 0 ? 'leixing-active' : '' }}"
|
||||
bindtap="qiehuanShenfen"
|
||||
data-type="0"
|
||||
>
|
||||
<text class="leixing-text">待处理</text>
|
||||
<text class="leixing-count">{{ daichuli }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="leixing-item {{ shenfen === 1 ? 'leixing-active' : '' }}"
|
||||
bindtap="qiehuanShenfen"
|
||||
data-type="1"
|
||||
>
|
||||
<text class="leixing-text">已处理</text>
|
||||
<text class="leixing-count">{{ yichuli }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 处罚记录列表 -->
|
||||
<scroll-view
|
||||
class="chufa-list"
|
||||
scroll-y
|
||||
enable-back-to-top
|
||||
scroll-with-animation
|
||||
style="height: {{ scrollHeight }}px"
|
||||
bindscrolltolower="shanglaShuaxin"
|
||||
>
|
||||
<!-- 加载中 -->
|
||||
<view wx:if="{{ jiazhaozhong && chufaList.length === 0 }}" class="jiazai-zhong">
|
||||
<view class="loading-animation">
|
||||
<view class="loading-dot"></view>
|
||||
<view class="loading-dot"></view>
|
||||
<view class="loading-dot"></view>
|
||||
</view>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 🔴【关键修改】处罚记录卡片列表 - 直接使用处理好的字段 -->
|
||||
<block wx:for="{{ chufaList }}" wx:key="index">
|
||||
<view
|
||||
class="chufa-card"
|
||||
bindtap="chakanXiangqing"
|
||||
data-item="{{ item }}"
|
||||
>
|
||||
<!-- 卡片顶部:时间和状态 -->
|
||||
<view class="card-top">
|
||||
<!-- 🔴 直接使用display_time -->
|
||||
<text class="shijian">{{ item.display_time }}</text>
|
||||
<!-- 🔴 直接使用display_status和status_class -->
|
||||
<view class="zhuangtai-badge {{ item.status_class }}">
|
||||
{{ item.display_status }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请人信息 -->
|
||||
<view class="qingqiu-row">
|
||||
<text class="qingqiu-label">申请人:</text>
|
||||
<text class="qingqiu-value">{{ item.qingqiuid || '--' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 处罚理由 -->
|
||||
<view class="liyou-row">
|
||||
<text class="liyou-label">处罚理由:</text>
|
||||
<text class="liyou-value">{{ item.cfliyou || '无' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单编号 -->
|
||||
<view class="dingdan-row">
|
||||
<text class="dingdan-label">订单编号:</text>
|
||||
<text class="dingdan-value">{{ item.dingdan_id || '--' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 卡片边框效果 -->
|
||||
<view class="card-border"></view>
|
||||
<view class="card-glow"></view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view wx:if="{{ jiazhaigengduo }}" class="jiazai-gengduo">
|
||||
<view class="scan-line"></view>
|
||||
<text>加载更多...</text>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多数据 -->
|
||||
<view wx:if="{{ !haiyougengduo && chufaList.length > 0 }}" class="meiyou-gengduo">
|
||||
<text>—— 没有更多记录了 ——</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view wx:if="{{ !jiazhaozhong && chufaList.length === 0 }}" class="kong-zhuangtai">
|
||||
<view class="empty-icon">⚡</view>
|
||||
<text class="empty-text">暂无处罚记录</text>
|
||||
<text class="empty-subtext">所有事务已处理完毕</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 处罚详情弹窗 -->
|
||||
<view wx:if="{{ showXiangqing }}" class="xiangqing-modal">
|
||||
<view class="modal-mask" bindtap="guanbiXiangqing"></view>
|
||||
<view class="modal-content">
|
||||
<!-- 弹窗头部 -->
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">处罚详情</text>
|
||||
<view class="modal-close" bindtap="guanbiXiangqing">
|
||||
<text class="close-icon">×</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 🔴【弹窗主体 - 确保可以滚动】 -->
|
||||
<scroll-view class="modal-body" scroll-y style="max-height: 1000rpx;">
|
||||
<!-- 基本信息 -->
|
||||
<view class="detail-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">订单编号:</text>
|
||||
<view class="detail-value-container">
|
||||
<text class="detail-value">{{ xuanzhongChufa.dingdan_id || '--' }}</text>
|
||||
<text class="fuzhi-btn" bindtap="fuzhiWenben" data-text="{{ xuanzhongChufa.dingdan_id }}">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">申请人ID:</text>
|
||||
<view class="detail-value-container">
|
||||
<text class="detail-value">{{ xuanzhongChufa.qingqiuid || '--' }}</text>
|
||||
<text class="fuzhi-btn" bindtap="fuzhiWenben" data-text="{{ xuanzhongChufa.qingqiuid }}">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<!-- 🔴 直接使用display_time -->
|
||||
<text class="detail-label">创建时间:</text>
|
||||
<text class="detail-value">{{ xuanzhongChufa.display_time || '--' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 处罚信息 -->
|
||||
<view class="detail-section">
|
||||
<view class="section-title">处罚信息</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">处罚状态:</text>
|
||||
<!-- 🔴 直接使用display_status和status_class -->
|
||||
<view class="zhuangtai-badge-inline {{ xuanzhongChufa.status_class }}">
|
||||
{{ xuanzhongChufa.display_status }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-row full-row">
|
||||
<text class="detail-label">处罚理由:</text>
|
||||
<text class="detail-value liyou-full">{{ xuanzhongChufa.cfliyou || '无' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 🔴 证据图片展示 - 直接使用full_zhengju_tupian -->
|
||||
<view wx:if="{{ xuanzhongChufa.full_zhengju_tupian && xuanzhongChufa.full_zhengju_tupian.length > 0 }}" class="detail-row full-row">
|
||||
<text class="detail-label">证据图片:</text>
|
||||
<view class="tupian-grid">
|
||||
<block wx:for="{{ xuanzhongChufa.full_zhengju_tupian }}" wx:key="index">
|
||||
<view class="tupian-item" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_zhengju_tupian }}">
|
||||
<image
|
||||
src="{{ item }}"
|
||||
class="tupian-image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉信息 -->
|
||||
<view wx:if="{{ xuanzhongChufa.ssliyou || (xuanzhongChufa.full_shensu_tupian && xuanzhongChufa.full_shensu_tupian.length > 0) }}" class="detail-section">
|
||||
<view class="section-title">申诉信息</view>
|
||||
<view wx:if="{{ xuanzhongChufa.ssliyou }}" class="detail-row full-row">
|
||||
<text class="detail-label">申诉理由:</text>
|
||||
<text class="detail-value liyou-full">{{ xuanzhongChufa.ssliyou }}</text>
|
||||
</view>
|
||||
<!-- 🔴 申诉图片展示 - 直接使用full_shensu_tupian -->
|
||||
<view wx:if="{{ xuanzhongChufa.full_shensu_tupian && xuanzhongChufa.full_shensu_tupian.length > 0 }}" class="detail-row full-row">
|
||||
<text class="detail-label">申诉图片:</text>
|
||||
<view class="tupian-grid">
|
||||
<block wx:for="{{ xuanzhongChufa.full_shensu_tupian }}" wx:key="index">
|
||||
<view class="tupian-item" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_shensu_tupian }}">
|
||||
<image
|
||||
src="{{ item }}"
|
||||
class="tupian-image"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 弹窗底部按钮 -->
|
||||
<view class="modal-footer">
|
||||
<view class="btn-group">
|
||||
<!-- 联系客服按钮(始终显示) -->
|
||||
<view class="btn btn-kefu" bindtap="lianxiKefu">
|
||||
<text>联系客服</text>
|
||||
</view>
|
||||
<!-- 申诉按钮(只有待处罚状态可点击) -->
|
||||
<view
|
||||
class="btn {{ xuanzhongChufa.sqzhuangtai === 0 ? 'btn-shensu' : 'btn-shensu-disabled' }}"
|
||||
bindtap="{{ xuanzhongChufa.sqzhuangtai === 0 ? 'openShensuModal' : '' }}"
|
||||
>
|
||||
<text>申诉</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉弹窗 -->
|
||||
<view wx:if="{{ showShensuModal }}" class="shensu-modal">
|
||||
<view class="modal-mask" bindtap="closeShensuModal"></view>
|
||||
<view class="modal-content small-modal">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">提交申诉</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<!-- 申诉理由输入 -->
|
||||
<view class="input-group">
|
||||
<view class="input-label">申诉理由</view>
|
||||
<textarea
|
||||
class="shensu-textarea"
|
||||
value="{{ shensuLiyou }}"
|
||||
bindinput="onShensuLiyouInput"
|
||||
placeholder="请输入申诉理由(最多500字)..."
|
||||
maxlength="500"
|
||||
auto-height
|
||||
/>
|
||||
<view class="word-count">{{ shensuLiyou.length }}/500</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉图片上传 -->
|
||||
<view class="input-group">
|
||||
<view class="input-label">申诉图片</view>
|
||||
<view class="upload-tip">最多可上传9张图片</view>
|
||||
|
||||
<!-- 图片网格 -->
|
||||
<view class="tupian-grid-upload">
|
||||
<!-- 已上传图片 -->
|
||||
<block wx:for="{{ shensuTupian }}" wx:key="index">
|
||||
<view class="tupian-item-upload">
|
||||
<image
|
||||
src="{{ item }}"
|
||||
class="tupian-image-upload"
|
||||
mode="aspectFill"
|
||||
bindtap="yulanShensuTupian"
|
||||
data-index="{{ index }}"
|
||||
/>
|
||||
<view class="tupian-delete" bindtap="deleteShensuTupian" data-index="{{ index }}">
|
||||
<text>×</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 添加按钮 -->
|
||||
<view
|
||||
wx:if="{{ shensuTupian.length < 9 }}"
|
||||
class="tupian-add-btn"
|
||||
bindtap="chooseShensuTupian"
|
||||
>
|
||||
<view class="add-icon">+</view>
|
||||
<text class="add-text">添加图片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 上传进度 -->
|
||||
<view wx:if="{{ shangchuanZongshu > 0 }}" class="upload-progress">
|
||||
<view class="progress-title">上传进度</view>
|
||||
<view class="progress-bar">
|
||||
<view class="progress-inner" style="width: {{ jinduWidth }}"></view>
|
||||
</view>
|
||||
<view class="progress-text">{{ shangchuanJindu }}/{{ shangchuanZongshu }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<view class="btn-group">
|
||||
<view class="btn btn-quxiao" bindtap="closeShensuModal">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
<view class="btn btn-queren" bindtap="submitShensu">
|
||||
<text>提交申诉</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 全局通知组件 -->
|
||||
<global-notification id="global-notification" />
|
||||
</view>
|
||||
963
miniprogram/pages/dashouxiaoxi/dashouxiaoxi.wxss
Normal file
@@ -0,0 +1,963 @@
|
||||
/* pages/cfss/cfss.wxss */
|
||||
/* 🔴【恢复您原来的深色背景,但调淡一点】 */
|
||||
|
||||
:root {
|
||||
/* 🔴【颜色调淡方案】 */
|
||||
--cyber-blue: rgba(0, 243, 255, 0.7); /* 蓝色调淡70% */
|
||||
--cyber-pink: rgba(255, 0, 255, 0.7); /* 粉色调淡70% */
|
||||
--cyber-purple: rgba(157, 0, 255, 0.7); /* 紫色调淡70% */
|
||||
--cyber-green: rgba(0, 255, 157, 0.7); /* 绿色调淡70% */
|
||||
--cyber-red: rgba(255, 0, 102, 0.7); /* 红色调淡70% */
|
||||
|
||||
/* 🔴【背景改为淡灰色,不是深色,也不是白色】 */
|
||||
--cyber-bg: #2a2a3a; /* 深灰色背景,比原来的淡 */
|
||||
--cyber-card: #333344; /* 卡片:比背景稍深的灰色 */
|
||||
--cyber-border: rgba(255, 255, 255, 0.15); /* 边框:半透明白色 */
|
||||
--cyber-glow: rgba(0, 243, 255, 0.2); /* 光效:调淡 */
|
||||
|
||||
/* 文字颜色保持不变 */
|
||||
--cyber-text: #ffffff;
|
||||
--cyber-text-light: #e6e6e6;
|
||||
--cyber-text-lighter: #cccccc;
|
||||
}
|
||||
|
||||
.cfss-container {
|
||||
padding: 30rpx;
|
||||
background: var(--cyber-bg);
|
||||
min-height: 100vh;
|
||||
color: var(--cyber-text) !important;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 恢复网格背景 */
|
||||
.cfss-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 243, 255, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 243, 255, 0.05) 1px, transparent 1px);
|
||||
background-size: 40rpx 40rpx;
|
||||
pointer-events: none;
|
||||
opacity: 0.15;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* ====== 统计卡片 ====== */
|
||||
.tongji-card {
|
||||
background: rgba(255, 0, 102, 0.3); /* 🔴 调淡为30%透明度 */
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
border: 1rpx solid var(--cyber-border);
|
||||
box-shadow:
|
||||
0 0 20rpx rgba(255, 0, 102, 0.2),
|
||||
inset 0 0 10rpx rgba(0, 243, 255, 0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tongji-title {
|
||||
color: var(--cyber-blue);
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30rpx;
|
||||
text-shadow: 0 0 8rpx var(--cyber-blue);
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.tongji-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tongji-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tongji-label {
|
||||
color: var(--cyber-text-light) !important;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 15rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.tongji-value {
|
||||
color: var(--cyber-text) !important;
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 8rpx currentColor;
|
||||
}
|
||||
|
||||
.daichuli-value {
|
||||
color: var(--cyber-red) !important;
|
||||
text-shadow: 0 0 8rpx rgba(255, 0, 102, 0.3);
|
||||
}
|
||||
|
||||
.yichuli-value {
|
||||
color: var(--cyber-green) !important;
|
||||
text-shadow: 0 0 8rpx rgba(0, 255, 157, 0.3);
|
||||
}
|
||||
|
||||
/* ====== 类型切换 ====== */
|
||||
.leixing-qiehuan {
|
||||
display: flex;
|
||||
background: rgba(255, 0, 102, 0.3); /* 🔴 调淡为30%透明度 */
|
||||
border-radius: 16rpx;
|
||||
padding: 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
border: 1rpx solid var(--cyber-border);
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.leixing-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 25rpx 0;
|
||||
border-radius: 12rpx;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.leixing-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.leixing-active {
|
||||
background: linear-gradient(135deg, rgba(0, 243, 255, 0.15), rgba(157, 0, 255, 0.15));
|
||||
box-shadow: 0 0 15rpx rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.leixing-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8rpx;
|
||||
color: var(--cyber-text-light) !important;
|
||||
}
|
||||
|
||||
.leixing-active .leixing-text {
|
||||
color: var(--cyber-text) !important;
|
||||
text-shadow: 0 0 8rpx var(--cyber-blue);
|
||||
}
|
||||
|
||||
.leixing-count {
|
||||
font-size: 24rpx;
|
||||
color: var(--cyber-text-lighter) !important;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.leixing-active .leixing-count {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
}
|
||||
|
||||
/* ====== 处罚列表 ====== */
|
||||
.chufa-list {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 30rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.jiazai-zhong {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 60rpx 0;
|
||||
color: var(--cyber-blue);
|
||||
}
|
||||
|
||||
.loading-animation {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.loading-dot {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
background-color: var(--cyber-blue);
|
||||
margin: 0 10rpx;
|
||||
animation: pulse 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.loading-dot:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.loading-dot:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 🔴【关键:处罚卡片背景改为淡灰色】 */
|
||||
.chufa-card {
|
||||
background: rgba(50, 50, 65, 0.8); /* 🔴 淡灰色卡片,半透明 */
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
border: 1rpx solid var(--cyber-border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.chufa-card:active {
|
||||
transform: translateY(-4rpx);
|
||||
box-shadow:
|
||||
0 10rpx 30rpx rgba(0, 0, 0, 0.3),
|
||||
0 0 25rpx var(--cyber-glow);
|
||||
}
|
||||
|
||||
.card-border {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
border-radius: 21rpx;
|
||||
background: linear-gradient(45deg, rgba(255, 0, 255, 0.3), rgba(0, 243, 255, 0.3), rgba(255, 0, 255, 0.3), rgba(0, 243, 255, 0.3));
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.chufa-card:active .card-border {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 25rpx;
|
||||
}
|
||||
|
||||
/* 🔴【时间必须显示】 */
|
||||
.shijian {
|
||||
color: var(--cyber-text-light) !important;
|
||||
font-size: 26rpx;
|
||||
font-family: 'Courier New', monospace;
|
||||
letter-spacing: 1rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 🔴【状态标签 - 必须显示出来】 */
|
||||
.zhuangtai-badge {
|
||||
padding: 8rpx 25rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2rpx;
|
||||
border: 1rpx solid;
|
||||
box-shadow: 0 0 8rpx;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.zhuangtai-daichuli {
|
||||
background: rgba(255, 0, 102, 0.2);
|
||||
border-color: var(--cyber-red);
|
||||
box-shadow: 0 0 8rpx rgba(255, 0, 102, 0.2);
|
||||
}
|
||||
|
||||
.zhuangtai-yichufa {
|
||||
background: rgba(0, 255, 157, 0.2);
|
||||
border-color: var(--cyber-green);
|
||||
box-shadow: 0 0 8rpx rgba(0, 255, 157, 0.2);
|
||||
}
|
||||
|
||||
.zhuangtai-yibohui {
|
||||
background: rgba(102, 102, 102, 0.2);
|
||||
border-color: #666;
|
||||
box-shadow: 0 0 8rpx rgba(102, 102, 102, 0.2);
|
||||
}
|
||||
|
||||
.zhuangtai-shensuzhong {
|
||||
background: rgba(255, 165, 0, 0.2);
|
||||
border-color: #FFA500;
|
||||
box-shadow: 0 0 8rpx rgba(255, 165, 0, 0.2);
|
||||
}
|
||||
|
||||
.zhuangtai-weizhi {
|
||||
background: rgba(128, 0, 255, 0.2); /* 未知状态:紫色 */
|
||||
border-color: rgba(128, 0, 255, 0.5);
|
||||
box-shadow: 0 0 8rpx rgba(128, 0, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 其他文字内容 */
|
||||
.qingqiu-row, .liyou-row, .dingdan-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.qingqiu-label, .liyou-label, .dingdan-label {
|
||||
color: var(--cyber-text-light) !important;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
margin-right: 15rpx;
|
||||
opacity: 0.9;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.qingqiu-value, .liyou-value, .dingdan-value {
|
||||
color: var(--cyber-text) !important;
|
||||
font-size: 26rpx;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.liyou-value {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dingdan-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* 加载更多 */
|
||||
.jiazai-gengduo {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
color: var(--cyber-blue);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scan-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 2rpx;
|
||||
background: linear-gradient(90deg, transparent, var(--cyber-blue), transparent);
|
||||
animation: scan 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0% { left: -100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
/* 没有更多数据 */
|
||||
.meiyou-gengduo {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
color: var(--cyber-green);
|
||||
font-size: 28rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.kong-zhuangtai {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 100rpx 40rpx;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: var(--cyber-blue);
|
||||
margin-bottom: 30rpx;
|
||||
text-shadow: 0 0 15rpx var(--cyber-blue);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 34rpx;
|
||||
color: var(--cyber-text) !important;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.empty-subtext {
|
||||
font-size: 28rpx;
|
||||
color: var(--cyber-text-light) !important;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ====== 弹窗样式 ====== */
|
||||
.xiangqing-modal,
|
||||
.shensu-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* 🔴【关键:弹窗背景改为不透明的淡灰色】 */
|
||||
.modal-content {
|
||||
position: relative;
|
||||
background: #3a3a4a; /* 🔴 淡灰色,不透明 */
|
||||
border-radius: 30rpx;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1rpx solid var(--cyber-border);
|
||||
box-shadow: 0 0 35rpx rgba(0, 243, 255, 0.3);
|
||||
z-index: 1001;
|
||||
backdrop-filter: blur(0px); /* 去掉模糊效果 */
|
||||
}
|
||||
|
||||
.small-modal {
|
||||
max-height: 85vh;
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 40rpx 30rpx 30rpx 30rpx;
|
||||
border-bottom: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: var(--cyber-blue);
|
||||
text-shadow: 0 0 8rpx var(--cyber-blue);
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.modal-close:active {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
font-size: 40rpx;
|
||||
color: var(--cyber-blue);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 弹窗主体 */
|
||||
.modal-body {
|
||||
flex: 1;
|
||||
padding: 30rpx;
|
||||
overflow-y: auto;
|
||||
min-height: 200rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 详情区域 */
|
||||
.detail-section {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: var(--cyber-blue);
|
||||
margin-bottom: 25rpx;
|
||||
padding-bottom: 15rpx;
|
||||
border-bottom: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
text-shadow: 0 0 8rpx var(--cyber-blue);
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
margin-bottom: 25rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: var(--cyber-text-light) !important;
|
||||
font-size: 30rpx;
|
||||
min-width: 180rpx;
|
||||
margin-right: 20rpx;
|
||||
line-height: 1.5;
|
||||
opacity: 0.9;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-value-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: var(--cyber-text) !important;
|
||||
font-size: 30rpx;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
opacity: 0.95;
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.fuzhi-btn {
|
||||
color: var(--cyber-blue);
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fuzhi-btn:active {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.full-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.full-row .detail-label {
|
||||
margin-bottom: 10rpx;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
/* 弹窗内的状态标签 */
|
||||
.zhuangtai-badge-inline {
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1rpx;
|
||||
border: 1rpx solid;
|
||||
min-width: 120rpx;
|
||||
text-align: center;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* 理由方框 */
|
||||
.liyou-full {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 25rpx 30rpx;
|
||||
border-radius: 15rpx;
|
||||
line-height: 1.6;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
color: var(--cyber-text) !important;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
display: block;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
/* 图片网格样式 */
|
||||
.tupian-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10rpx;
|
||||
margin-left: -5rpx;
|
||||
margin-right: -5rpx;
|
||||
}
|
||||
|
||||
.tupian-item {
|
||||
width: calc(33.333% - 10rpx);
|
||||
margin: 5rpx;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.tupian-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 弹窗底部按钮 */
|
||||
.modal-footer {
|
||||
padding: 30rpx;
|
||||
border-top: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 15rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
transition: all 0.3s;
|
||||
border: 1rpx solid;
|
||||
box-shadow: 0 0 12rpx;
|
||||
color: var(--cyber-text) !important;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-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 {
|
||||
background: rgba(255, 165, 0, 0.1);
|
||||
border-color: #FFA500;
|
||||
box-shadow: 0 0 12rpx rgba(255, 165, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-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 {
|
||||
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 {
|
||||
background: rgba(0, 255, 157, 0.1);
|
||||
border-color: var(--cyber-green);
|
||||
box-shadow: 0 0 12rpx rgba(0, 255, 157, 0.2);
|
||||
}
|
||||
|
||||
/* 申诉弹窗输入样式 */
|
||||
.input-group {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
color: var(--cyber-text-light) !important;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shensu-textarea {
|
||||
width: 100%;
|
||||
min-height: 200rpx;
|
||||
padding: 25rpx 30rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 15rpx;
|
||||
font-size: 30rpx;
|
||||
line-height: 1.5;
|
||||
color: var(--cyber-text) !important;
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.shensu-textarea::placeholder {
|
||||
color: var(--cyber-text-lighter) !important;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.word-count {
|
||||
text-align: right;
|
||||
color: var(--cyber-text-lighter) !important;
|
||||
font-size: 24rpx;
|
||||
margin-top: 10rpx;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.upload-tip {
|
||||
color: var(--cyber-text-lighter) !important;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 20rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 上传图片网格 */
|
||||
.tupian-grid-upload {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-left: -5rpx;
|
||||
margin-right: -5rpx;
|
||||
}
|
||||
|
||||
.tupian-item-upload {
|
||||
width: calc(33.333% - 10rpx);
|
||||
margin: 5rpx;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.tupian-image-upload {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tupian-delete {
|
||||
position: absolute;
|
||||
top: 5rpx;
|
||||
right: 5rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 0, 102, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tupian-add-btn {
|
||||
width: calc(33.333% - 10rpx);
|
||||
margin: 5rpx;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2rpx dashed rgba(0, 243, 255, 0.4);
|
||||
color: var(--cyber-blue);
|
||||
}
|
||||
|
||||
.tupian-add-btn:active {
|
||||
border-color: var(--cyber-blue);
|
||||
background: rgba(0, 243, 255, 0.07);
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 60rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.add-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 上传进度 */
|
||||
.upload-progress {
|
||||
margin-top: 30rpx;
|
||||
padding: 25rpx 30rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 15rpx;
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.progress-title {
|
||||
color: var(--cyber-blue);
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 20rpx;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.progress-inner {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--cyber-blue), var(--cyber-purple));
|
||||
border-radius: 10rpx;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
color: var(--cyber-text-light) !important;
|
||||
font-size: 26rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 375px) {
|
||||
.tongji-value {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.shijian,
|
||||
.qingqiu-label,
|
||||
.liyou-label,
|
||||
.dingdan-label {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.qingqiu-value,
|
||||
.liyou-value,
|
||||
.dingdan-value {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 🔴【颜色调整】卡片和弹窗背景色稍微调淡一点 */
|
||||
:root {
|
||||
--cyber-bg: #2c2c3c; /* 从#2a2a3a调亮一点 */
|
||||
--cyber-card: #363648; /* 从#333344调亮一点 */
|
||||
}
|
||||
|
||||
/* 🔴【卡片背景色调整 - 更淡一点】 */
|
||||
.chufa-card {
|
||||
background: rgba(130, 141, 146, 0.8); /* 从rgba(50,50,65,0.8)调亮 */
|
||||
}
|
||||
|
||||
/* 🔴【弹窗背景色调整 - 更淡一点】 */
|
||||
.modal-content {
|
||||
|
||||
background: #acacda; /* 从#3a3a4a调亮 */
|
||||
}
|
||||
|
||||
/* 🔴【增加弹窗高度】 */
|
||||
.modal-content {
|
||||
max-height: 88vh; /* 从80vh增加 */
|
||||
}
|
||||
|
||||
.small-modal {
|
||||
max-height: 88vh; /* 从85vh调整 */
|
||||
}
|
||||
|
||||
/* 🔴【增加弹窗内部滚动区域高度】 */
|
||||
/* 修改wxml中对应的style */
|
||||
/* 需要将:<scroll-view class="modal-body" scroll-y style="max-height: 500rpx;"> */
|
||||
/* 改为:<scroll-view class="modal-body" scroll-y style="max-height: 600rpx;"> */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* pages/cfss/cfss.wxss 中新增样式 */
|
||||
|
||||
/* 🔴【关键修改】图片网格和图片样式 */
|
||||
.tupian-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12rpx;
|
||||
margin-top: 10rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tupian-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid rgba(0, 243, 255, 0.3);
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.tupian-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* 图片预览遮罩 */
|
||||
.tupian-preview {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.tupian-item:hover .tupian-preview {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.preview-text {
|
||||
color: white;
|
||||
font-size: 24rpx;
|
||||
background: rgba(0, 243, 255, 0.7);
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
422
miniprogram/pages/dashouxiugai/dashouxiugai.js
Normal file
@@ -0,0 +1,422 @@
|
||||
import request from '../../utils/request.js'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 头像相关
|
||||
touxiangUrl: '', // 完整头像URL
|
||||
touxiangRelative: '', // 头像相对URL(用于上传后更新)
|
||||
|
||||
// 打手信息字段
|
||||
dashouUid: '', // UID(不可修改)
|
||||
dashouNicheng: '', // 昵称
|
||||
dashouJieshao: '', // 个人介绍
|
||||
dashouDianhua: '', // 电话
|
||||
dashouWeixin: '', // 微信
|
||||
|
||||
// 页面状态
|
||||
isLoading: false, // 是否正在加载
|
||||
isDataFromApi: false, // 数据是否来自API(用于显示提示)
|
||||
isSubmitting: false, // 是否正在提交
|
||||
hasLoadedFromCache: false, // 是否已从缓存加载
|
||||
|
||||
// 验证状态
|
||||
nichengValid: true,
|
||||
dianhuaValid: true,
|
||||
weixinValid: true,
|
||||
jieshaoValid: true
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 页面加载时初始化数据
|
||||
this.initPageData()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次页面显示时检查头像是否需要更新
|
||||
this.registerNotificationComponent();
|
||||
this.checkAvatarUpdate()
|
||||
},
|
||||
// 🆕 新增:注册通知组件
|
||||
registerNotificationComponent() {
|
||||
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()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 初始化页面数据
|
||||
async initPageData() {
|
||||
this.setData({ isLoading: true })
|
||||
|
||||
try {
|
||||
// 1. 从缓存加载基本信息
|
||||
const cachedData = this.loadFromCache()
|
||||
|
||||
// 2. 检查是否需要从API加载
|
||||
const needApiLoad = this.checkNeedApiLoad(cachedData)
|
||||
|
||||
if (needApiLoad) {
|
||||
// 从API加载数据
|
||||
await this.loadFromApi()
|
||||
this.setData({ isDataFromApi: true })
|
||||
} else {
|
||||
// 使用缓存数据
|
||||
this.setDataFromCache(cachedData)
|
||||
this.setData({ isDataFromApi: false })
|
||||
}
|
||||
|
||||
// 3. 构建完整头像URL
|
||||
this.buildAvatarUrl()
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化页面数据失败:', error)
|
||||
wx.showToast({
|
||||
title: '数据加载失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
})
|
||||
} finally {
|
||||
this.setData({ isLoading: false, hasLoadedFromCache: true })
|
||||
}
|
||||
},
|
||||
|
||||
// 从缓存加载数据
|
||||
loadFromCache() {
|
||||
const app = getApp()
|
||||
|
||||
return {
|
||||
// 从缓存获取头像相对URL
|
||||
touxiang: wx.getStorageSync('touxiang') || '',
|
||||
|
||||
// 从缓存获取用户ID
|
||||
uid: wx.getStorageSync('uid') || '',
|
||||
|
||||
// 从缓存获取打手信息
|
||||
dsnc: wx.getStorageSync('dsnc') || '',
|
||||
dsjieshao: wx.getStorageSync('dsjieshao') || '',
|
||||
dsdianhua: wx.getStorageSync('dsdianhua') || '',
|
||||
dsweixin: wx.getStorageSync('dsweixin') || ''
|
||||
}
|
||||
},
|
||||
|
||||
// 检查是否需要从API加载数据
|
||||
checkNeedApiLoad(cachedData) {
|
||||
// 如果昵称或介绍有一个为空,则需要从API加载
|
||||
return !cachedData.dsnc || !cachedData.dsjieshao
|
||||
},
|
||||
|
||||
// 设置缓存数据到页面
|
||||
setDataFromCache(cachedData) {
|
||||
const app = getApp()
|
||||
|
||||
// 处理空值显示
|
||||
const formatEmptyValue = (value) => {
|
||||
return value && value.trim() !== '' ? value : '暂未填写'
|
||||
}
|
||||
|
||||
this.setData({
|
||||
dashouUid: cachedData.uid || '',
|
||||
dashouNicheng: cachedData.dsnc || '',
|
||||
dashouJieshao: cachedData.dsjieshao || '',
|
||||
dashouDianhua: formatEmptyValue(cachedData.dsdianhua),
|
||||
dashouWeixin: formatEmptyValue(cachedData.dsweixin),
|
||||
touxiangRelative: cachedData.touxiang || ''
|
||||
})
|
||||
},
|
||||
|
||||
// 从API加载数据
|
||||
async loadFromApi() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/dszjxxhq',
|
||||
method: 'POST'
|
||||
})
|
||||
|
||||
if (res && res.data.code === 200) {
|
||||
const data = res.data.data || {}
|
||||
|
||||
// 格式化空值
|
||||
const formatValue = (value) => {
|
||||
if (value === null || value === undefined || value.trim() === '') {
|
||||
return '暂未填写'
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// 更新页面数据
|
||||
this.setData({
|
||||
dashouNicheng: formatValue(data.nicheng),
|
||||
dashouJieshao: formatValue(data.jieshao),
|
||||
dashouDianhua: formatValue(data.dianhua),
|
||||
dashouWeixin: formatValue(data.wechat)
|
||||
})
|
||||
|
||||
// 更新缓存(即使为空也存储,避免重复请求)
|
||||
this.updateCache(data)
|
||||
|
||||
} else {
|
||||
throw new Error(res?.data?.msg || '获取信息失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('API加载失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 更新缓存
|
||||
updateCache(data) {
|
||||
// 存储到缓存,确保字段存在(即使为空)
|
||||
wx.setStorageSync('dsnc', data.nicheng || '')
|
||||
wx.setStorageSync('dsjieshao', data.jieshao || '')
|
||||
wx.setStorageSync('dsdianhua', data.dianhua || '')
|
||||
wx.setStorageSync('dsweixin', data.wechat || '')
|
||||
},
|
||||
|
||||
// 构建完整头像URL
|
||||
buildAvatarUrl() {
|
||||
const app = getApp()
|
||||
|
||||
// 获取头像相对路径
|
||||
const touxiangRelative = this.data.touxiangRelative || ''
|
||||
|
||||
// 获取全局配置
|
||||
const ossImageUrl = app.globalData.ossImageUrl || ''
|
||||
const morentouxiang = app.globalData.morentouxiang || ''
|
||||
|
||||
// 构建完整URL
|
||||
let fullUrl = ''
|
||||
|
||||
if (touxiangRelative && touxiangRelative.trim() !== '') {
|
||||
fullUrl = ossImageUrl + touxiangRelative
|
||||
} else {
|
||||
fullUrl = ossImageUrl + morentouxiang
|
||||
}
|
||||
|
||||
this.setData({ touxiangUrl: fullUrl })
|
||||
},
|
||||
|
||||
// 检查头像是否需要更新
|
||||
checkAvatarUpdate() {
|
||||
const app = getApp()
|
||||
const currentAvatar = wx.getStorageSync('touxiang') || ''
|
||||
|
||||
// 如果缓存中的头像与当前显示的不同,重新构建URL
|
||||
if (currentAvatar !== this.data.touxiangRelative) {
|
||||
this.setData({ touxiangRelative: currentAvatar })
|
||||
this.buildAvatarUrl()
|
||||
}
|
||||
},
|
||||
|
||||
// 事件处理函数
|
||||
onAvatarTap() {
|
||||
// 头像点击事件(后续可扩展为更换头像)
|
||||
wx.showToast({
|
||||
title: '头像上传功能待实现',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
},
|
||||
|
||||
onNichengInput(e) {
|
||||
const value = e.detail.value
|
||||
this.setData({
|
||||
dashouNicheng: value,
|
||||
nichengValid: value.length <= 20
|
||||
})
|
||||
},
|
||||
|
||||
onDianhuaInput(e) {
|
||||
const value = e.detail.value
|
||||
// 简单的手机号验证
|
||||
const isValid = /^1[3-9]\d{9}$/.test(value) || value === '' || value === '暂未填写'
|
||||
this.setData({
|
||||
dashouDianhua: value,
|
||||
dianhuaValid: isValid
|
||||
})
|
||||
},
|
||||
|
||||
onWeixinInput(e) {
|
||||
const value = e.detail.value
|
||||
this.setData({
|
||||
dashouWeixin: value,
|
||||
weixinValid: value.length <= 30
|
||||
})
|
||||
},
|
||||
|
||||
onJieshaoInput(e) {
|
||||
const value = e.detail.value
|
||||
this.setData({
|
||||
dashouJieshao: value,
|
||||
jieshaoValid: value.length <= 200
|
||||
})
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
validateForm() {
|
||||
const {
|
||||
dashouNicheng,
|
||||
dashouDianhua,
|
||||
dashouWeixin,
|
||||
dashouJieshao,
|
||||
nichengValid,
|
||||
dianhuaValid,
|
||||
weixinValid,
|
||||
jieshaoValid
|
||||
} = this.data
|
||||
|
||||
// 1. 验证昵称
|
||||
if (!dashouNicheng || dashouNicheng.trim() === '') {
|
||||
wx.showToast({
|
||||
title: '昵称不能为空',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (dashouNicheng.length > 20) {
|
||||
wx.showToast({
|
||||
title: '昵称不能超过20字',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. 验证手机号(非必填,但填写时必须正确)
|
||||
if (dashouDianhua && dashouDianhua !== '暂未填写') {
|
||||
if (!/^1[3-9]\d{9}$/.test(dashouDianhua)) {
|
||||
wx.showToast({
|
||||
title: '手机号格式不正确',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 验证微信号(非必填)
|
||||
if (dashouWeixin && dashouWeixin.length > 30) {
|
||||
wx.showToast({
|
||||
title: '微信号不能超过30字符',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
// 4. 验证个人介绍
|
||||
if (dashouJieshao && dashouJieshao.length > 200) {
|
||||
wx.showToast({
|
||||
title: '介绍不能超过200字',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
// 格式化提交数据
|
||||
formatSubmitData() {
|
||||
const {
|
||||
dashouNicheng,
|
||||
dashouDianhua,
|
||||
dashouWeixin,
|
||||
dashouJieshao
|
||||
} = this.data
|
||||
|
||||
// 将"暂未填写"转换为空字符串
|
||||
const formatValue = (value) => {
|
||||
return value === '暂未填写' ? '' : value.trim()
|
||||
}
|
||||
|
||||
return {
|
||||
nicheng: formatValue(dashouNicheng),
|
||||
dianhua: formatValue(dashouDianhua),
|
||||
wechat: formatValue(dashouWeixin),
|
||||
jieshao: formatValue(dashouJieshao)
|
||||
}
|
||||
},
|
||||
|
||||
// 提交修改
|
||||
async onSubmit() {
|
||||
// 防止重复提交
|
||||
if (this.data.isSubmitting) return
|
||||
|
||||
// 表单验证
|
||||
if (!this.validateForm()) return
|
||||
|
||||
this.setData({ isSubmitting: true })
|
||||
|
||||
// 准备提交数据
|
||||
const postData = this.formatSubmitData()
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/dsgxxx',
|
||||
method: 'POST',
|
||||
data: postData
|
||||
})
|
||||
|
||||
if (res && res.data.code === 200) {
|
||||
// 更新成功
|
||||
wx.showToast({
|
||||
title: '修改成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
// 更新本地缓存
|
||||
this.updateLocalCache(postData)
|
||||
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 1500)
|
||||
|
||||
} else {
|
||||
// 更新失败
|
||||
const errorMsg = res?.data?.msg || '修改失败,请重试'
|
||||
wx.showModal({
|
||||
title: '修改失败',
|
||||
content: errorMsg,
|
||||
showCancel: false
|
||||
})
|
||||
this.setData({ isSubmitting: false })
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交修改失败:', error)
|
||||
wx.showModal({
|
||||
title: '请求失败',
|
||||
content: error.message || '网络错误,请检查连接',
|
||||
showCancel: false
|
||||
})
|
||||
this.setData({ isSubmitting: false })
|
||||
}
|
||||
},
|
||||
|
||||
// 更新本地缓存
|
||||
updateLocalCache(data) {
|
||||
if (data.nicheng !== undefined) {
|
||||
wx.setStorageSync('dsnc', data.nicheng)
|
||||
}
|
||||
if (data.jieshao !== undefined) {
|
||||
wx.setStorageSync('dsjieshao', data.jieshao)
|
||||
}
|
||||
if (data.dianhua !== undefined) {
|
||||
wx.setStorageSync('dsdianhua', data.dianhua)
|
||||
}
|
||||
if (data.wechat !== undefined) {
|
||||
wx.setStorageSync('dsweixin', data.wechat)
|
||||
}
|
||||
}
|
||||
})
|
||||
11
miniprogram/pages/dashouxiugai/dashouxiugai.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
|
||||
"navigationBarTitleText": "服务者信息修改",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f8f9fa",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
}
|
||||
}
|
||||
103
miniprogram/pages/dashouxiugai/dashouxiugai.wxml
Normal file
@@ -0,0 +1,103 @@
|
||||
<!-- 打手信息修改页面 -->
|
||||
<view class="container">
|
||||
<!-- 头像区域 -->
|
||||
<view class="avatar-section">
|
||||
<view class="avatar-wrapper">
|
||||
<image
|
||||
src="{{ touxiangUrl }}"
|
||||
mode="aspectFill"
|
||||
class="avatar"
|
||||
bindtap="onAvatarTap"
|
||||
></image>
|
||||
<view class="avatar-halo"></view>
|
||||
<text class="avatar-hint">点击更换头像</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 信息展示与修改区域 -->
|
||||
<view class="info-section">
|
||||
<!-- UID(不可修改) -->
|
||||
<view class="info-item">
|
||||
<text class="info-label">服务者UID</text>
|
||||
<text class="info-value info-value-disabled">{{ dashouUid }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 昵称 -->
|
||||
<view class="info-item">
|
||||
<text class="info-label">服务昵称</text>
|
||||
<input
|
||||
value="{{ dashouNicheng }}"
|
||||
placeholder="请输入昵称"
|
||||
maxlength="20"
|
||||
bindinput="onNichengInput"
|
||||
class="info-input"
|
||||
/>
|
||||
<text class="char-count">{{ dashouNicheng.length || 0 }}/20</text>
|
||||
</view>
|
||||
|
||||
<!-- 电话 -->
|
||||
<view class="info-item">
|
||||
<text class="info-label">联系电话</text>
|
||||
<input
|
||||
value="{{ dashouDianhua }}"
|
||||
placeholder="请输入手机号"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
bindinput="onDianhuaInput"
|
||||
class="info-input"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 微信 -->
|
||||
<view class="info-item">
|
||||
<text class="info-label">微信号</text>
|
||||
<input
|
||||
value="{{ dashouWeixin }}"
|
||||
placeholder="请输入微信号"
|
||||
maxlength="30"
|
||||
bindinput="onWeixinInput"
|
||||
class="info-input"
|
||||
/>
|
||||
<text class="char-count">{{ dashouWeixin.length || 0 }}/30</text>
|
||||
</view>
|
||||
|
||||
<!-- 个人介绍 -->
|
||||
<view class="info-item info-item-textarea">
|
||||
<text class="info-label">个人介绍</text>
|
||||
<textarea
|
||||
value="{{ dashouJieshao }}"
|
||||
placeholder="请简单介绍一下自己..."
|
||||
maxlength="200"
|
||||
bindinput="onJieshaoInput"
|
||||
class="info-textarea"
|
||||
></textarea>
|
||||
<text class="char-count">{{ dashouJieshao.length || 0 }}/200</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态提示 -->
|
||||
<view class="status-section" wx:if="{{ isDataFromApi }}">
|
||||
<text class="status-text">提示:已从服务器加载最新信息</text>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="button-section">
|
||||
<button
|
||||
class="submit-btn"
|
||||
bindtap="onSubmit"
|
||||
disabled="{{ isSubmitting }}"
|
||||
loading="{{ isSubmitting }}"
|
||||
>
|
||||
{{ isSubmitting ? '提交中...' : '立即修改' }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 加载中提示 -->
|
||||
<view class="loading-wrapper" wx:if="{{ isLoading }}">
|
||||
<view class="loading-content">
|
||||
<image src="/images/loading.gif" class="loading-icon"></image>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||
197
miniprogram/pages/dashouxiugai/dashouxiugai.wxss
Normal file
@@ -0,0 +1,197 @@
|
||||
/* 页面容器 */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #fffacd 0%, #e6f7ff 100%);
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 头像区域 */
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40rpx 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 50%;
|
||||
border: 6rpx solid #ffffff;
|
||||
box-shadow: 0 0 30rpx rgba(255, 215, 0, 0.4);
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.avatar-halo {
|
||||
position: absolute;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(255, 215, 0, 0.2) 0%, transparent 70%);
|
||||
z-index: 1;
|
||||
animation: halo-pulse 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes halo-pulse {
|
||||
0%, 100% { opacity: 0.6; transform: scale(1); }
|
||||
50% { opacity: 0.8; transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.avatar-hint {
|
||||
margin-top: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 信息区域 */
|
||||
.info-section {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 40rpx;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 40rpx;
|
||||
position: relative;
|
||||
padding-bottom: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15rpx;
|
||||
}
|
||||
|
||||
.info-value-disabled {
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
background: #f9f9f9;
|
||||
padding: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.info-input {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
padding: 20rpx;
|
||||
background: #f9f9f9;
|
||||
border-radius: 10rpx;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.info-textarea {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
padding: 20rpx;
|
||||
background: #f9f9f9;
|
||||
border-radius: 10rpx;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
min-height: 200rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info-item-textarea {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 5rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 状态提示 */
|
||||
.status-section {
|
||||
background: #e8f4fd;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
border-left: 6rpx solid #1890ff;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 26rpx;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 按钮区域 */
|
||||
.button-section {
|
||||
padding: 20rpx 0 40rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background: linear-gradient(90deg, #52c41a, #73d13d);
|
||||
color: white;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
height: 90rpx;
|
||||
line-height: 70rpx;
|
||||
border-radius: 45rpx;
|
||||
border: none;
|
||||
box-shadow: 0 10rpx 20rpx rgba(82, 196, 26, 0.2);
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
background: #cccccc;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.submit-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 加载中 */
|
||||
.loading-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
287
miniprogram/pages/dingdan/dingdan.js
Normal file
@@ -0,0 +1,287 @@
|
||||
// pages/dingdan/dingdan.js
|
||||
const app = getApp()
|
||||
import request from '../../utils/request.js'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 订单状态tab配置
|
||||
dingdanZhuangtaiTabs: [
|
||||
{ name: '全部', key: 'all', zhuangtaiList: [1, 2, 3, 4, 5, 6, 7, 8] },
|
||||
{ name: '已下单', key: 'yixiadan', zhuangtaiList: [1, 7] },
|
||||
{ name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2] },
|
||||
{ name: '争议订单', key: 'zhengyi', zhuangtaiList: [4, 5, 6] },
|
||||
{ name: '已完成', key: 'yiwancheng', zhuangtaiList: [3] },
|
||||
{ name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8] }
|
||||
],
|
||||
|
||||
// 当前选中的tab索引
|
||||
xuanzhongTabIndex: 0,
|
||||
// 当前选中的tab的key
|
||||
currentTabKey: 'all',
|
||||
|
||||
// 每个tab对应的订单数据
|
||||
dingdanShuju: {
|
||||
all: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
yixiadan: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
zhengyi: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false },
|
||||
jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }
|
||||
},
|
||||
|
||||
// 个人中心传递过来的type参数
|
||||
gerenzhongxinType: '',
|
||||
|
||||
// 是否显示加载更多
|
||||
xianshiJiazaigengduo: false,
|
||||
|
||||
// 页面加载状态
|
||||
isLoading: true
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const type = options.type || ''
|
||||
|
||||
wx.setNavigationBarTitle({
|
||||
title: '我的订单'
|
||||
})
|
||||
|
||||
this.qingkongGerenzhongxinJishu(type)
|
||||
|
||||
const tabIndex = this.yingsheTypeToTabIndex(type)
|
||||
const tabKey = this.data.dingdanZhuangtaiTabs[tabIndex].key
|
||||
|
||||
this.setData({
|
||||
gerenzhongxinType: type,
|
||||
xuanzhongTabIndex: tabIndex,
|
||||
currentTabKey: tabKey,
|
||||
isLoading: false
|
||||
})
|
||||
|
||||
this.jiazaiDingdanShuju(tabIndex)
|
||||
},
|
||||
onShow() {
|
||||
// 原有代码...
|
||||
|
||||
// 🆕 注册通知组件
|
||||
this.registerNotificationComponent();
|
||||
},
|
||||
|
||||
// 🆕 新增:注册通知组件
|
||||
registerNotificationComponent() {
|
||||
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()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
qingkongGerenzhongxinJishu(type) {
|
||||
const jishuMap = {
|
||||
'daifuwu': 'daifuwu',
|
||||
'fuwuzhong': 'fuwuzhong',
|
||||
'yiwancheng': 'yiwancheng',
|
||||
'yituikuan': 'yituikuan'
|
||||
}
|
||||
|
||||
const jishuKey = jishuMap[type]
|
||||
if (jishuKey && app.globalData.dingdanTiaoshu) {
|
||||
app.globalData.dingdanTiaoshu[jishuKey] = 0
|
||||
}
|
||||
},
|
||||
|
||||
yingsheTypeToTabIndex(type) {
|
||||
const yingsheMap = {
|
||||
'daifuwu': 1,
|
||||
'fuwuzhong': 2,
|
||||
'yiwancheng': 4,
|
||||
'yituikuan': 3
|
||||
}
|
||||
|
||||
return yingsheMap[type] || 0
|
||||
},
|
||||
|
||||
switchDingdanTab(e) {
|
||||
const tabIndex = e.currentTarget.dataset.index
|
||||
if (tabIndex === this.data.xuanzhongTabIndex) return
|
||||
|
||||
const tabKey = this.data.dingdanZhuangtaiTabs[tabIndex].key
|
||||
|
||||
this.setData({
|
||||
xuanzhongTabIndex: tabIndex,
|
||||
currentTabKey: tabKey
|
||||
})
|
||||
|
||||
this.jiazaiDingdanShuju(tabIndex)
|
||||
},
|
||||
|
||||
async jiazaiDingdanShuju(tabIndex, isLoadMore = false) {
|
||||
const tab = this.data.dingdanZhuangtaiTabs[tabIndex]
|
||||
const tabKey = tab.key
|
||||
|
||||
const tabData = this.data.dingdanShuju[tabKey]
|
||||
|
||||
const page = isLoadMore ? tabData.page + 1 : 1
|
||||
|
||||
if (tabData.isLoading) return
|
||||
|
||||
// 重置逻辑:如果不是加载更多,清空已有数据
|
||||
if (!isLoadMore) {
|
||||
this.setData({
|
||||
[`dingdanShuju.${tabKey}.list`]: [],
|
||||
[`dingdanShuju.${tabKey}.page`]: 1
|
||||
})
|
||||
}
|
||||
|
||||
this.setData({
|
||||
[`dingdanShuju.${tabKey}.isLoading`]: true
|
||||
})
|
||||
|
||||
wx.showLoading({
|
||||
title: isLoadMore ? '加载更多...' : '加载订单...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
try {
|
||||
const requestData = {
|
||||
zhuangtai_list: tab.zhuangtaiList,
|
||||
page: page,
|
||||
page_size: 10
|
||||
}
|
||||
|
||||
|
||||
|
||||
const res = await request({
|
||||
url: '/dingdan/dingdanhuoqu',
|
||||
method: 'POST',
|
||||
data: requestData,
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
wx.hideLoading()
|
||||
|
||||
// ✅ 关键修正:res是直接返回的数据对象,不是res.data
|
||||
if (res && res.data.code === 0) {
|
||||
// ✅ 关键修正:直接使用res.data.list
|
||||
const newDingdanList = res.data.data.list || []
|
||||
const hasMore = res.data.data.has_more || false
|
||||
|
||||
|
||||
|
||||
// 处理图片URL,拼接完整路径
|
||||
const processedList = newDingdanList.map(item => {
|
||||
let tupianUrl = item.tupian || ''
|
||||
|
||||
// 如果图片URL不是完整路径,拼接ossImageUrl
|
||||
if (tupianUrl && !tupianUrl.startsWith('http')) {
|
||||
const ossImageUrl = app.globalData.ossImageUrl || ''
|
||||
tupianUrl = ossImageUrl + tupianUrl
|
||||
}
|
||||
|
||||
const zhuangtaiZh = this.getZhuangtaiZhongwen(item.zhuangtai)
|
||||
|
||||
|
||||
return {
|
||||
...item,
|
||||
tupian: tupianUrl,
|
||||
zhuangtaiZh: zhuangtaiZh
|
||||
}
|
||||
})
|
||||
|
||||
const currentList = this.data.dingdanShuju[tabKey].list
|
||||
const updatedList = isLoadMore
|
||||
? [...currentList, ...processedList]
|
||||
: processedList
|
||||
|
||||
this.setData({
|
||||
[`dingdanShuju.${tabKey}.list`]: updatedList,
|
||||
[`dingdanShuju.${tabKey}.page`]: page,
|
||||
[`dingdanShuju.${tabKey}.hasMore`]: hasMore,
|
||||
[`dingdanShuju.${tabKey}.isLoading`]: false,
|
||||
xianshiJiazaigengduo: hasMore
|
||||
})
|
||||
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: res ? (res.msg || '加载失败') : '请求失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
this.setData({
|
||||
[`dingdanShuju.${tabKey}.isLoading`]: false
|
||||
})
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载订单数据失败:', error)
|
||||
wx.hideLoading()
|
||||
wx.showToast({
|
||||
title: '网络请求失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
this.setData({
|
||||
[`dingdanShuju.${tabKey}.isLoading`]: false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
getZhuangtaiZhongwen(zhuangtai) {
|
||||
const zhuangtaiMap = {
|
||||
1: '已下单',
|
||||
2: '进行中',
|
||||
3: '已完成',
|
||||
4: '退款中',
|
||||
5: '已退款',
|
||||
6: '退款失败',
|
||||
7: '指定中',
|
||||
8: '结算中'
|
||||
}
|
||||
|
||||
return zhuangtaiMap[zhuangtai] || '未知状态'
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
const tabKey = this.data.currentTabKey
|
||||
const tabData = this.data.dingdanShuju[tabKey]
|
||||
|
||||
if (tabData.hasMore && !tabData.isLoading) {
|
||||
const tabIndex = this.data.xuanzhongTabIndex
|
||||
this.jiazaiDingdanShuju(tabIndex, true)
|
||||
}
|
||||
},
|
||||
|
||||
goToDingdanXiangqing(e) {
|
||||
const dingdanItem = e.currentTarget.dataset.item
|
||||
if (!dingdanItem) return
|
||||
|
||||
const dingdanDataStr = encodeURIComponent(JSON.stringify(dingdanItem))
|
||||
|
||||
wx.navigateTo({
|
||||
url: `/pages/dingdanxiangqing/dingdanxiangqing?dingdanData=${dingdanDataStr}`
|
||||
})
|
||||
},
|
||||
|
||||
onImageError(e) {
|
||||
const index = e.currentTarget.dataset.index
|
||||
const tabKey = this.data.currentTabKey
|
||||
|
||||
const defaultImage = app.globalData.lunbozhanwei || '/images/default.jpg'
|
||||
|
||||
this.setData({
|
||||
[`dingdanShuju.${tabKey}.list[${index}].tupian`]: defaultImage
|
||||
})
|
||||
}
|
||||
})
|
||||
5
miniprogram/pages/dingdan/dingdan.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"global-notification": "/components/global-notification/global-notification"
|
||||
}
|
||||
}
|
||||
111
miniprogram/pages/dingdan/dingdan.wxml
Normal file
@@ -0,0 +1,111 @@
|
||||
<!--pages/dingdan/dingdan.wxml-->
|
||||
<view class="dingdan-page">
|
||||
|
||||
<!-- 订单状态tab区域 -->
|
||||
<scroll-view
|
||||
class="dingdan-tabs-scroll"
|
||||
scroll-x="true"
|
||||
scroll-with-animation="true"
|
||||
>
|
||||
<view class="dingdan-tabs-container">
|
||||
<block wx:for="{{dingdanZhuangtaiTabs}}" wx:key="key">
|
||||
<view
|
||||
class="dingdan-tab-item {{index === xuanzhongTabIndex ? 'tab-active' : ''}}"
|
||||
bindtap="switchDingdanTab"
|
||||
data-index="{{index}}"
|
||||
>
|
||||
<text class="tab-text">{{item.name}}</text>
|
||||
<view class="tab-active-line" wx:if="{{index === xuanzhongTabIndex}}"></view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 订单列表区域 -->
|
||||
<view class="dingdan-list-container">
|
||||
|
||||
<!-- 页面加载中 -->
|
||||
<view class="loading-container" wx:if="{{isLoading}}">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view
|
||||
class="empty-container"
|
||||
wx:if="{{!isLoading && dingdanShuju[currentTabKey].list.length === 0}}"
|
||||
>
|
||||
<image
|
||||
class="empty-image"
|
||||
src="/images/empty-order.png"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<text class="empty-text">暂无订单</text>
|
||||
<text class="empty-subtext">快去下单体验服务吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<block wx:if="{{!isLoading && dingdanShuju[currentTabKey].list.length > 0}}">
|
||||
<block wx:for="{{dingdanShuju[currentTabKey].list}}" wx:key="dingdanId">
|
||||
<view
|
||||
class="dingdan-card"
|
||||
bindtap="goToDingdanXiangqing"
|
||||
data-item="{{item}}"
|
||||
>
|
||||
<!-- 商品图片 -->
|
||||
<view class="dingdan-card-left">
|
||||
<image
|
||||
class="dingdan-image"
|
||||
src="{{item.tupian}}"
|
||||
mode="aspectFill"
|
||||
binderror="onImageError"
|
||||
data-index="{{index}}"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="dingdan-card-center">
|
||||
<view class="dingdan-jieshao">
|
||||
{{item.jieshao || '暂无描述'}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单状态和价格 -->
|
||||
<view class="dingdan-card-right">
|
||||
<!-- 订单状态 -->
|
||||
<view class="dingdan-zhuangtai">
|
||||
{{item.zhuangtaiZh || '未知状态'}}
|
||||
</view>
|
||||
|
||||
<!-- 订单价格 -->
|
||||
<view class="dingdan-jine">
|
||||
<text class="jine-symbol">¥</text>
|
||||
<text class="jine-number">{{item.jine || '0.00'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view
|
||||
class="load-more-container"
|
||||
wx:if="{{xianshiJiazaigengduo && !isLoading && dingdanShuju[currentTabKey].list.length > 0}}"
|
||||
>
|
||||
<view class="load-more-line"></view>
|
||||
<text class="load-more-text">上拉加载更多</text>
|
||||
<view class="load-more-line"></view>
|
||||
</view>
|
||||
|
||||
<!-- 没有更多提示 -->
|
||||
<view
|
||||
class="no-more-container"
|
||||
wx:if="{{!xianshiJiazaigengduo && !isLoading && dingdanShuju[currentTabKey].list.length > 0}}"
|
||||
>
|
||||
<text class="no-more-text">没有更多订单了</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<global-notification id="global-notification" />
|
||||