fix: 组队进原订单群;成交率有样本含0%也展示

This commit is contained in:
XingQue committed 2026-07-27 09:21:52 +08:00
1 parent b9de9ea34e
commit 77fefbe8aa
3 files changed
+213 -56

No files matched your search

@@ -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) : '',
})
@@ -100,9 +100,13 @@
<text class="item-value zhuangtai-text">{{zhuangtaiWenziMap[jibenShuju.zhuangtai] || '未知状态'}}</text>
</view>
<view class="info-item-row" wx:if="{{autoSettleTip}}">
<view class="info-item-row auto-settle-row" wx:if="{{autoSettleTip || autoSettleClock}}">
<text class="item-label">自动结算:</text>
<text class="item-value auto-settle-text">{{autoSettleTip}}<text wx:if="{{autoSettleExpireAt}}">({{autoSettleExpireAt}})</text></text>
<view class="auto-settle-value">
<text class="auto-settle-clock" wx:if="{{autoSettleClock}}">{{autoSettleClock}}</text>
<text class="item-value auto-settle-text">{{autoSettleTip}}</text>
<text class="auto-settle-expire" wx:if="{{autoSettleExpireAt}}">到期 {{autoSettleExpireAt}}</text>
</view>
</view>
<!-- 发单平台信息 -->
@@ -157,7 +161,7 @@
</view>
<view class="team-actions">
<view class="team-chat-btn" bindtap="goToTeamGroupChat">进入组队群聊</view>
<view class="team-chat-btn" bindtap="goToTeamGroupChat">进入订单群聊</view>
<view class="team-action-btn ghost" wx:if="{{isLeader && team.status == 1 && jibenShuju.zhuangtai == 2}}" bindtap="lockTeam">关停招募</view>
</view>
</block>
@@ -205,14 +209,28 @@
<!-- 退款信息区域 -->
<view
class="tuikuan-area"
wx:if="{{showRefundInfo && xiangxiShuju.tuikuan_liyou}}"
wx:if="{{showRefundInfo && (xiangxiShuju.tuikuan_liyou || xiangxiShuju.tuikuan_tupian.length)}}"
>
<view class="info-title">退款信息</view>
<view class="info-item-row">
<view class="info-item-row" wx:if="{{xiangxiShuju.tuikuan_liyou}}">
<text class="item-label">退款理由:</text>
<text class="item-value">{{xiangxiShuju.tuikuan_liyou}}</text>
</view>
<view class="info-item-row" wx:if="{{xiangxiShuju.tuikuan_tupian.length}}" style="flex-direction:column;align-items:flex-start;">
<text class="item-label" style="margin-bottom:12rpx;">相关图片:</text>
<view style="display:flex;flex-wrap:wrap;">
<block wx:for="{{xiangxiShuju.tuikuan_tupian}}" wx:key="*this">
<image
src="{{item}}"
mode="aspectFill"
style="width:140rpx;height:140rpx;margin:0 12rpx 12rpx 0;border-radius:8rpx;"
bindtap="previewTuikuanTupian"
data-url="{{item}}"
/>
</block>
</view>
</view>
</view>
<!-- 处罚申请信息区域 -->
@@ -254,7 +272,7 @@
<!-- 接单员交付历史(只读模式) -->
<view class="dashou-jiaofu-area" wx:if="{{!isModifying && xiangxiShuju.dashou_jiaofu.length > 0}}">
<view class="info-title">我的交付</view>
<view class="info-title">{{isLeader ? '订单交付' : '订单交付(只读)'}}</view>
<!-- 接单员交付图片 -->
<view class="dashou-jiaofu-container">
@@ -286,8 +304,8 @@
</view>
</view>
<!-- 🆕 修改模式下的可编辑区域(当 isModifying 为 true 时显示) -->
<view class="dashou-jiaofu-area" wx:if="{{isModifying}}">
<!-- 修改模式(仅队长 canModify) -->
<view class="dashou-jiaofu-area" wx:if="{{isModifying && canModify}}">
<view class="info-title">修改交付</view>
<!-- 图片编辑区域 -->
@@ -353,8 +371,8 @@
</view>
</view>
<!-- 提交订单区域(仅状态=2时显示,保持不变) -->
<view class="tijiao-area" wx:if="{{jibenShuju.zhuangtai == 2}}">
<!-- 提交订单:仅队长(或非组队接单人) -->
<view class="tijiao-area" wx:if="{{jibenShuju.zhuangtai == 2 && canSubmit}}">
<view class="info-title">提交订单</view>
<!-- 选择图片 -->
@@ -421,8 +439,8 @@
<!-- ========== 底部按钮区域 ========== -->
<view class="bottom-btns-container" wx:if="{{!isLoading}}">
<!-- 🆕 修改模式下的按钮(取消/确认) -->
<block wx:if="{{isModifying}}">
<!-- 修改模式按钮(仅队长 canModify) -->
<block wx:if="{{isModifying && canModify}}">
<view class="btn-row">
<view
class="bottom-btn cancel-modify-btn"
@@ -471,8 +489,8 @@
</view>
</view>
<!-- 🆕 新增:修改按钮(仅当状态为4或8时显示) -->
<view class="btn-row" wx:if="{{jibenShuju.zhuangtai == 4 || jibenShuju.zhuangtai == 8}}">
<!-- 修改交付:仅队长 -->
<view class="btn-row" wx:if="{{canModify && (jibenShuju.zhuangtai == 4 || jibenShuju.zhuangtai == 8)}}">
<view
class="bottom-btn modify-btn full-width"
bindtap="enterModify"
@@ -482,15 +500,21 @@
</view>
</view>
<!-- 提交按钮(仅状态=2时显示) -->
<!-- 提交:仅队长 -->
<view
class="bottom-btn submit-btn"
wx:if="{{jibenShuju.zhuangtai == 2}}"
wx:if="{{canSubmit && jibenShuju.zhuangtai == 2}}"
bindtap="tijiaoDingdan"
>
<text class="btn-text">{{isSubmitting ? '提交中...' : '立即提交'}}</text>
<view class="btn-shine"></view>
</view>
<view
class="team-member-tip"
wx:if="{{team && !isLeader && jibenShuju.zhuangtai == 2}}"
>
<text>组队中仅队长可提交交付,请等待队长操作</text>
</view>
</block>
</view>
+111
View File
@@ -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%)。',
};