feat: 客服「成交与自动结算」配置页与指标列表

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-27 01:29:29 +08:00
parent db18eb4fa8
commit 5af0ee5f09
4 changed files with 186 additions and 2 deletions

View File

@@ -57,6 +57,7 @@
</template>
<el-menu-item v-if="canShow('order.platform')" index="/order/platform">平台订单</el-menu-item>
<el-menu-item v-if="canShow('order.merchant')" index="/order/merchant">商家订单</el-menu-item>
<el-menu-item v-if="canShow('order.deal-config')" index="/order/deal-config">成交与自动结算</el-menu-item>
</el-sub-menu>
<!-- 跨平台管理 -->
@@ -156,6 +157,7 @@
<el-menu-item v-if="canShow('miniapp.dashou-exam')" index="/miniapp/dashou-exam">打手接单考试</el-menu-item>
<el-menu-item v-if="canShow('miniapp.rank-reward')" index="/miniapp/rank-reward">排行榜奖励</el-menu-item>
<el-menu-item v-if="canShow('miniapp.recharge-copy')" index="/miniapp/recharge-copy">充值页文案</el-menu-item>
<el-menu-item v-if="canShow('miniapp.role-agreement')" index="/miniapp/role-agreement">角色协议</el-menu-item>
<el-menu-item v-if="canShow('miniapp.withdraw-settings')" index="/withdraw/settings">提现与收款设置</el-menu-item>
</el-sub-menu>

View File

@@ -0,0 +1,165 @@
<template>
<div class="page" v-loading="loading">
<ClubScopeHint />
<div class="header">
<h2>成交指标与自动结算</h2>
<el-button @click="load">刷新</el-button>
</div>
<el-alert
type="warning"
:closable="false"
show-icon
title="默认全关。打开自动结算后,仅「本次开启之后新进入结算中」的订单会计时并到期自动完成;历史结算中老单永远不会被自动结。"
style="margin-bottom: 16px"
/>
<div v-if="form.club_id" class="club">当前俱乐部{{ form.club_id }}</div>
<el-card shadow="never" class="card">
<template #header>自动结算</template>
<el-form label-width="160px">
<el-form-item label="启用自动结算">
<el-switch v-model="form.auto_settle_enabled" />
</el-form-item>
<el-form-item label="平台单超时(小时)">
<el-input-number v-model="form.hours_platform" :min="1" :max="720" />
</el-form-item>
<el-form-item label="商家单超时(小时)">
<el-input-number v-model="form.hours_merchant" :min="1" :max="720" />
</el-form-item>
<el-form-item v-if="form.auto_settle_enabled_at" label="本次开启时间">
<span>{{ form.auto_settle_enabled_at }}</span>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never" class="card">
<template #header>成交 / 罚款指标统计</template>
<el-form label-width="160px">
<el-form-item label="启用指标统计">
<el-switch v-model="form.stats_enabled" />
<span class="tip">从开启时刻起记账不算历史单</span>
</el-form-item>
<el-form-item v-if="form.stats_since" label="统计起点">
<span>{{ form.stats_since }}</span>
</el-form-item>
</el-form>
</el-card>
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
<el-card v-if="form.stats_enabled" shadow="never" class="card" style="margin-top:16px">
<template #header>商家指标本俱乐部</template>
<el-table :data="merchantList" size="small" max-height="360">
<el-table-column prop="merchant_uid" label="商家UID" width="120" />
<el-table-column prop="order_count" label="订单量" width="90" />
<el-table-column prop="deal_rate" label="成交率" width="100">
<template #default="{ row }">{{ pct(row.deal_rate) }}</template>
</el-table-column>
<el-table-column prop="fine_rate" label="罚款率" width="100">
<template #default="{ row }">{{ pct(row.fine_rate) }}</template>
</el-table-column>
<el-table-column prop="refund_rate" label="退款率" width="100">
<template #default="{ row }">{{ pct(row.refund_rate) }}</template>
</el-table-column>
<el-table-column prop="avg_deal_hours" label="平均成交时长(h)" min-width="120" />
</el-table>
</el-card>
<el-card v-if="form.stats_enabled" shadow="never" class="card">
<template #header>打手指标本俱乐部</template>
<el-table :data="dashouList" size="small" max-height="360">
<el-table-column prop="dashou_uid" label="打手UID" width="120" />
<el-table-column prop="order_count" label="接单量" width="90" />
<el-table-column prop="deal_rate" label="成交率" width="100">
<template #default="{ row }">{{ pct(row.deal_rate) }}</template>
</el-table-column>
<el-table-column prop="fined_rate" label="被罚款率" width="100">
<template #default="{ row }">{{ pct(row.fined_rate) }}</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'
import { JITUAN_API } from '@/utils/club-context'
import ClubScopeHint from '@/components/ClubScopeHint.vue'
const username = localStorage.getItem('username') || ''
const loading = ref(false)
const saving = ref(false)
const merchantList = ref([])
const dashouList = ref([])
const form = reactive({
club_id: '',
auto_settle_enabled: false,
auto_settle_enabled_at: '',
hours_platform: 48,
hours_merchant: 48,
stats_enabled: false,
stats_since: '',
})
function pct(v) {
const n = Number(v) || 0
return `${(n * 100).toFixed(2)}%`
}
async function load() {
loading.value = true
try {
const res = await request.post(JITUAN_API.orderDealConfig, { username, action: 'get' })
if (res.code === 0 || res.code === 200) {
Object.assign(form, res.data || {})
} else {
ElMessage.error(res.msg || '加载失败')
}
if (form.stats_enabled) {
const [m, d] = await Promise.all([
request.post(JITUAN_API.merchantDealStat, { username, action: 'list' }),
request.post(JITUAN_API.dashouDealStat, { username, action: 'list' }),
])
merchantList.value = m.data?.list || []
dashouList.value = d.data?.list || []
}
} finally {
loading.value = false
}
}
async function save() {
saving.value = true
try {
const res = await request.post(JITUAN_API.orderDealConfig, {
username,
action: 'save',
auto_settle_enabled: form.auto_settle_enabled,
hours_platform: form.hours_platform,
hours_merchant: form.hours_merchant,
stats_enabled: form.stats_enabled,
})
if (res.code === 0 || res.code === 200) {
Object.assign(form, res.data || {})
ElMessage.success('已保存')
load()
} else {
ElMessage.error(res.msg || '保存失败')
}
} finally {
saving.value = false
}
}
onMounted(load)
</script>
<style scoped>
.page { padding: 16px; }
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.club { margin-bottom: 12px; color: #64748b; font-size: 13px; }
.card { margin-bottom: 16px; }
.tip { margin-left: 12px; color: #94a3b8; font-size: 12px; }
</style>