diff --git a/pages/fighter-order-detail/fighter-order-detail.js b/pages/fighter-order-detail/fighter-order-detail.js
index 624e051..9eed57b 100644
--- a/pages/fighter-order-detail/fighter-order-detail.js
+++ b/pages/fighter-order-detail/fighter-order-detail.js
@@ -2,7 +2,7 @@
const app = getApp()
import request from '../../utils/request.js'
import connectionManager from '../../utils/xiaoxilj.js'
-import { attachAutoSettleFields } from '../../utils/auto-settle-display.js'
+import { bindDetailAutoSettle, stopAutoSettleTicker } from '../../utils/auto-settle-display.js'
const COS = require('../../utils/cos-wx-sdk-v5.min.js')
Page({
@@ -23,6 +23,7 @@ Page({
shangjia_nicheng: '',
shangjia_liuyan: '',
tuikuan_liyou: '',
+ tuikuan_tupian: [],
chufa_liyou: '',
chufa_zhuangtai: 0,
chufa_jieguo: '',
@@ -34,6 +35,8 @@ Page({
},
autoSettleTip: '',
autoSettleExpireAt: '',
+ autoSettleClock: '',
+ autoSettleRemainText: '',
zhuangtaiYanseMap: {
1: '#E8F5E9', 2: '#E3F2FD', 3: '#F3E5F5', 4: '#FFF3E0',
5: '#FFEBEE', 6: '#EFEBE9', 7: '#E0F7FA', 8: '#FFF8E1'
@@ -70,6 +73,8 @@ Page({
team: null,
isLeader: false,
+ canSubmit: false,
+ canModify: false,
teamStatusText: '',
teamShareText: '',
showTeamForm: false,
@@ -89,14 +94,28 @@ Page({
this.jiexiTiaozhuanCanshu(options)
},
onHide() {
+ stopAutoSettleTicker(this);
const popupComp = this.selectComponent('#popupNotice');
if (popupComp && popupComp.cleanup) {
popupComp.cleanup();
}
},
+
+ onUnload() {
+ stopAutoSettleTicker(this);
+ },
onShow() {
this.registerNotificationComponent();
+ if (this.data._autoSettleEligible) {
+ bindDetailAutoSettle(this, {
+ zhuangtai: this.data.jibenShuju.zhuangtai,
+ auto_settle_eligible: this.data._autoSettleEligible,
+ auto_expire_at: this.data._autoExpireAt,
+ auto_expire_at_ts: this.data._autoExpireAtTs,
+ auto_settle_remain_seconds: this.data._autoSettleRemainBase,
+ });
+ }
},
registerNotificationComponent() {
@@ -189,6 +208,14 @@ Page({
'xiangxiShuju.shangjia_nicheng': data.shangjia_nicheng || '',
'xiangxiShuju.shangjia_liuyan': data.shangjia_liuyan || '',
'xiangxiShuju.tuikuan_liyou': data.tuikuan_liyou || '',
+ 'xiangxiShuju.tuikuan_tupian': (data.tuikuan_tupian || []).map((url) => {
+ if (url && !String(url).startsWith('http')) {
+ const oss = app.globalData.ossImageUrl || ''
+ const path = String(url).startsWith('/') ? String(url).substring(1) : String(url)
+ return oss + path
+ }
+ return url
+ }),
'xiangxiShuju.chufa_liyou': data.chufa_liyou || '',
'xiangxiShuju.chufa_zhuangtai': data.chufa_zhuangtai || 0,
'xiangxiShuju.chufa_jieguo': data.chufa_jieguo || '',
@@ -202,17 +229,14 @@ Page({
'liuyan': data.dashou_liuyan || '',
'jibenShuju.fadanpingtai': data.fadanpingtai || 0
})
- const settled = attachAutoSettleFields({
+ bindDetailAutoSettle(this, {
zhuangtai: data.zhuangtai,
auto_settle_eligible: data.auto_settle_eligible,
auto_expire_at: data.auto_expire_at,
+ auto_expire_at_ts: data.auto_expire_at_ts,
auto_settle_remain_seconds: data.auto_settle_remain_seconds,
})
- this.setData({
- autoSettleTip: settled.autoSettleTip || '',
- autoSettleExpireAt: settled.autoSettleExpireAt || '',
- })
- this.applyTeamData(data.team || null, data.fuwudashou_id || '')
+ this.applyTeamData(data.team || null, data.fuwudashou_id || '', data)
const currentStatus = data.zhuangtai || 0
const showRefund = [4,5,6].includes(currentStatus)
this.setData({ showRefundInfo: showRefund })
@@ -257,6 +281,12 @@ Page({
if (!currentUrl) return
wx.previewImage({ current: currentUrl, urls: urls.length > 0 ? urls : [currentUrl] })
},
+ previewTuikuanTupian(e) {
+ const currentUrl = e.currentTarget.dataset.url
+ const urls = this.data.xiangxiShuju.tuikuan_tupian || []
+ if (!currentUrl) return
+ wx.previewImage({ current: currentUrl, urls: urls.length > 0 ? urls : [currentUrl] })
+ },
getFileExtension(filePath) {
const lastDotIndex = filePath.lastIndexOf('.')
if (lastDotIndex === -1) return '.jpg'
@@ -348,38 +378,9 @@ Page({
})
},
- // 组队群:固定进 group_{订单号}(入队时已订阅)
+ // 组队沟通:复用抢单配对群,不另开 group_{订单}
goToTeamGroupChat() {
- const uid = wx.getStorageSync('uid')
- const orderId = this.data.jibenShuju.dingdan_id
- if (!uid || !orderId) {
- wx.showToast({ title: '信息不完整', icon: 'none' })
- return
- }
- const groupId = `group_${orderId}`
- const currentUser = {
- id: 'Ds' + uid,
- name: wx.getStorageSync('nicheng') || app.globalData.dashouNicheng || '打手',
- avatar: '',
- }
- const payload = encodeURIComponent(JSON.stringify({
- groupId,
- orderId,
- groupName: (this.data.jibenShuju.nicheng || '订单') + ' · 组队群',
- orderDesc: this.data.jibenShuju.jieshao || '',
- orderJine: this.data.jibenShuju.jine || '',
- orderZhuangtai: this.data.jibenShuju.zhuangtai,
- currentUserId: currentUser.id,
- currentUserName: currentUser.name,
- isCross: this.data.jibenShuju.fadanpingtai || 0,
- }))
- wx.navigateTo({
- url: `/pages/group-chat/group-chat?data=${payload}`,
- fail: (err) => {
- console.error('进入组队群失败', err)
- wx.showToast({ title: '无法打开群聊', icon: 'none' })
- },
- })
+ this.goToChatWithBoss()
},
async getCOSZhengshu() {
@@ -485,6 +486,10 @@ Page({
},
tijiaoDingdan() {
+ if (!this.data.canSubmit && !this.data.isLeader) {
+ wx.showToast({ title: '仅队长可提交交付', icon: 'none' })
+ return
+ }
this.showAnnouncementAndConfirm()
},
@@ -616,6 +621,10 @@ Page({
return fullUrl
},
enterModify() {
+ if (!this.data.canModify && !this.data.isLeader) {
+ wx.showToast({ title: '仅队长可修改交付', icon: 'none' })
+ return
+ }
const { dashou_jiaofu, dashou_liuyan } = this.data.xiangxiShuju
this.setData({
isModifying: true,
@@ -789,19 +798,32 @@ Page({
}
},
- applyTeamData(team, playerId) {
+ applyTeamData(team, playerId, detailData) {
const uid = String(wx.getStorageSync('uid') || '')
const statusMap = { 1: '招募中', 2: '已锁定', 3: '已结束', 4: '已取消', 5: '已作废' }
const pid = String(playerId || this.data.xiangxiShuju.fuwudashou_id || '')
+ const d = detailData || {}
let isLeader = false
- if (team) {
+ if (typeof d.is_team_leader === 'boolean') {
+ isLeader = d.is_team_leader
+ } else if (team) {
isLeader = String(team.leader_uid || '') === uid
} else {
isLeader = !!pid && pid === uid
}
+ const zt = Number(d.zhuangtai != null ? d.zhuangtai : this.data.jibenShuju.zhuangtai)
+ let canSubmit = typeof d.can_submit === 'boolean' ? d.can_submit : (isLeader && zt === 2)
+ let canModify = typeof d.can_modify === 'boolean' ? d.can_modify : (isLeader && (zt === 4 || zt === 8))
+ // 有组队时强制仅队长
+ if (team && !isLeader) {
+ canSubmit = false
+ canModify = false
+ }
this.setData({
team: team || null,
isLeader,
+ canSubmit,
+ canModify,
teamStatusText: team ? (statusMap[team.status] || '未知') : '',
teamShareText: team ? Number(team.display_share_amount || 0).toFixed(2) : '',
})
diff --git a/pages/fighter-order-detail/fighter-order-detail.wxml b/pages/fighter-order-detail/fighter-order-detail.wxml
index c157587..1935c70 100644
--- a/pages/fighter-order-detail/fighter-order-detail.wxml
+++ b/pages/fighter-order-detail/fighter-order-detail.wxml
@@ -100,9 +100,13 @@
{{zhuangtaiWenziMap[jibenShuju.zhuangtai] || '未知状态'}}
-
+
自动结算:
- {{autoSettleTip}}({{autoSettleExpireAt}})
+
+ {{autoSettleClock}}
+ {{autoSettleTip}}
+ 到期 {{autoSettleExpireAt}}
+
@@ -157,7 +161,7 @@
- 进入组队群聊
+ 进入订单群聊
关停招募
@@ -205,14 +209,28 @@
退款信息
-
+
退款理由:
{{xiangxiShuju.tuikuan_liyou}}
+
+ 相关图片:
+
+
+
+
+
+
@@ -254,7 +272,7 @@
- 我的交付
+ {{isLeader ? '订单交付' : '订单交付(只读)'}}
@@ -286,8 +304,8 @@
-
-
+
+
修改交付
@@ -353,8 +371,8 @@
-
-
+
+
提交订单
@@ -421,8 +439,8 @@
-
-
+
+
-
-
+
+
-
+
{{isSubmitting ? '提交中...' : '立即提交'}}
+
+ 组队中仅队长可提交交付,请等待队长操作
+
diff --git a/utils/deal-stat-display.js b/utils/deal-stat-display.js
new file mode 100644
index 0000000..31dbd38
--- /dev/null
+++ b/utils/deal-stat-display.js
@@ -0,0 +1,111 @@
+/**
+ * 成交指标展示:后端有样本就展示(含真实 0%),无样本不造假。
+ * - 成交率/罚款率:有 rate / rate_text 即展示
+ * - 均时:有 avg_deal_hours_text 才展示
+ */
+
+function parseRate(textOrNum) {
+ if (textOrNum == null || textOrNum === '') return null;
+ if (typeof textOrNum === 'number') {
+ return Number.isFinite(textOrNum) ? textOrNum : null;
+ }
+ const s = String(textOrNum).trim().replace(/%/g, '');
+ const n = Number(s);
+ return Number.isFinite(n) ? n : null;
+}
+
+function formatRateText(rate, fallbackText) {
+ if (fallbackText) return fallbackText;
+ const pct = parseRate(rate);
+ if (pct == null) return '';
+ const asPercent = pct > 1 ? pct : pct * 100;
+ return `${asPercent.toFixed(1)}%`;
+}
+
+function hasRateValue(rate, text) {
+ if (text != null && String(text).trim() !== '') return true;
+ return rate != null && rate !== '';
+}
+
+/**
+ * 把接口 data 收成列表顶栏可用结构;全无可展示则返回 null
+ */
+export function sanitizeMyDealStat(raw, role = 'merchant') {
+ if (!raw || typeof raw !== 'object') return null;
+ const out = { role: raw.role || role };
+ const chips = [];
+
+ const dealRate = raw.deal_rate != null ? raw.deal_rate : raw.deal_rate_text;
+ if (hasRateValue(raw.deal_rate, raw.deal_rate_text)) {
+ const pct = parseRate(dealRate);
+ out.deal_rate = pct == null ? 0 : (pct > 1 ? pct / 100 : pct);
+ out.deal_rate_text = formatRateText(dealRate, raw.deal_rate_text);
+ out.deal_label = '成交率';
+ chips.push({ k: 'deal', t: `成交率 ${out.deal_rate_text}` });
+ }
+
+ const fineKey = role === 'dashou' ? 'fined_rate' : 'fine_rate';
+ const fineTextKey = role === 'dashou' ? 'fined_rate_text' : 'fine_rate_text';
+ const fineRate = raw[fineKey] != null
+ ? raw[fineKey]
+ : (raw[fineTextKey] || raw.fine_rate_text || raw.fined_rate_text);
+ const fineText = raw[fineTextKey] || raw.fine_rate_text || raw.fined_rate_text;
+ if (hasRateValue(raw[fineKey] != null ? raw[fineKey] : raw.fine_rate, fineText)) {
+ const finePct = parseRate(fineRate);
+ out[fineKey] = finePct == null ? 0 : (finePct > 1 ? finePct / 100 : finePct);
+ out[fineTextKey] = formatRateText(fineRate, fineText);
+ out.fine_label = role === 'dashou' ? '被罚款率' : '罚款率';
+ chips.push({
+ k: 'fine',
+ t: `${out.fine_label} ${out[fineTextKey]}`,
+ });
+ }
+
+ if (raw.avg_deal_hours_text) {
+ out.avg_deal_hours_text = raw.avg_deal_hours_text;
+ out.avg_deal_hours = raw.avg_deal_hours;
+ chips.push({ k: 'avg', t: `均时 ${raw.avg_deal_hours_text}`, hl: true });
+ }
+
+ if (!chips.length) return null;
+ out.chips = chips;
+ out.order_count = raw.order_count;
+ out.completed_count = raw.completed_count;
+ return out;
+}
+
+/**
+ * 抢单池/组队卡片芯片:后端给了哪个字段就展示哪个(含 0%)
+ */
+export function buildDealStatChips(stat, opts = {}) {
+ if (!stat || typeof stat !== 'object') return [];
+ const dealLabel = opts.dealLabel || '成交率';
+ const fineLabel = opts.fineLabel || '罚款率';
+ const chips = [];
+
+ if (hasRateValue(stat.deal_rate, stat.deal_rate_text)) {
+ const text = formatRateText(
+ stat.deal_rate != null ? stat.deal_rate : stat.deal_rate_text,
+ stat.deal_rate_text,
+ );
+ chips.push({ k: 'deal', t: `${dealLabel} ${text}` });
+ }
+
+ const fineRaw = stat.fine_rate != null
+ ? stat.fine_rate
+ : (stat.fined_rate != null ? stat.fined_rate : (stat.fine_rate_text || stat.fined_rate_text));
+ const fineText = stat.fine_rate_text || stat.fined_rate_text;
+ if (hasRateValue(stat.fine_rate != null ? stat.fine_rate : stat.fined_rate, fineText)) {
+ chips.push({ k: 'fine', t: `${fineLabel} ${formatRateText(fineRaw, fineText)}` });
+ }
+
+ if (stat.avg_deal_hours_text) {
+ chips.push({ k: 'avg', t: `均时 ${stat.avg_deal_hours_text}`, hl: true });
+ }
+ return chips;
+}
+
+export const DEAL_STAT_HELP = {
+ merchant: '成交率=已结算÷有效单量;罚款率=已缴纳罚单÷有效单量;均时=平均结算耗时。有样本即展示(含 0%)。',
+ dashou: '成交率=已结算÷接单量;被罚款率=已缴纳罚单÷接单量。有样本即展示(含 0%)。',
+};