Files
hfb_sys/frontend/src/features/admin/views/AdminDistributionOrdersView.vue
T
yml2213andCursor 768902f2f7 feat(distribution): 补齐退款加价回退、本站自卖与开通默认规则
退款时压缩卖方加价,人工/自动批次仅完结单可入账,号主收入进主站钱包;仲裁后重算释放并支持作废未入账 DS。开通时四种模式按默认生效,对外口径合并为 35/40/25,不再单独展示通道费。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 18:13:46 +08:00

253 lines
7.6 KiB
Vue

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import {
fetchDistributionOrders,
fetchDistributionSites,
type DistributionOrder,
type DistributionSite,
} from '@/features/admin/api/adminDistribution'
import {
DISTRIBUTION_MODES,
distributionModeLabel,
distributorReleaseLabel,
} from '@/features/admin/utils/distributionLabels'
import { formatCent } from '@/shared/utils/money'
import { formatDateTime } from '@/shared/utils/time'
import { orderStatusLabel, refundStatusLabel } from '@/shared/utils/statusLabels'
import { readError } from '@/shared/utils/error'
const loading = ref(false)
const sites = ref<DistributionSite[]>([])
const orders = ref<DistributionOrder[]>([])
const total = ref(0)
const query = reactive({
siteID: undefined as number | undefined,
mode: '',
status: '',
financialStatus: '',
page: 1,
pageSize: 20,
})
async function loadSites() {
try {
sites.value = await fetchDistributionSites()
} catch (err) {
ElMessage.error(readError(err, '获取分销站点失败'))
}
}
async function loadOrders() {
loading.value = true
try {
const res = await fetchDistributionOrders({
siteID: query.siteID,
mode: query.mode || undefined,
status: query.status || undefined,
financialStatus: query.financialStatus || undefined,
page: query.page,
pageSize: query.pageSize,
})
orders.value = res.items ?? []
total.value = res.total ?? 0
} catch (err) {
ElMessage.error(readError(err, '获取分销订单失败'))
} finally {
loading.value = false
}
}
function handleSearch() {
query.page = 1
loadOrders()
}
function handleReset() {
query.siteID = undefined
query.mode = ''
query.status = ''
query.financialStatus = ''
query.page = 1
loadOrders()
}
function getSiteName(siteId: number) {
const site = sites.value.find(s => s.id === siteId)
return site ? site.site_name : `站点 #${siteId}`
}
onMounted(async () => {
await loadSites()
await loadOrders()
})
</script>
<template>
<div class="distribution-orders-view">
<div class="page-header">
<div>
<h2 class="page-title">分销订单</h2>
<p class="page-subtitle">
查看分销订单的利润基数与冷却期释放状态。不要把订单履约结算和分销入账混为一谈。
</p>
</div>
</div>
<el-card shadow="never" class="filter-card">
<el-form :inline="true" :model="query">
<el-form-item label="分销站点">
<el-select v-model="query.siteID" clearable placeholder="全部分销站" style="width: 180px">
<el-option
v-for="site in sites"
:key="site.id"
:label="site.site_name"
:value="site.id"
/>
</el-select>
</el-form-item>
<el-form-item label="分销模式">
<el-select v-model="query.mode" clearable placeholder="全部模式" style="width: 180px">
<el-option
v-for="item in DISTRIBUTION_MODES"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="订单状态">
<el-select v-model="query.status" clearable placeholder="全部" style="width: 140px">
<el-option label="已完成" value="completed" />
<el-option label="使用中" value="renting" />
<el-option label="申诉中" value="disputing" />
<el-option label="已关闭" value="closed" />
</el-select>
</el-form-item>
<el-form-item label="释放状态">
<el-select
v-model="query.financialStatus"
clearable
placeholder="全部"
style="width: 140px"
>
<el-option label="待释放" value="pending" />
<el-option label="已释放入账" value="settled" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never" style="margin-top: 16px">
<el-table v-loading="loading" :data="orders" stripe style="width: 100%">
<el-table-column label="订单号" prop="order_no" min-width="160" />
<el-table-column label="分销站点" min-width="130">
<template #default="{ row }">
<el-tag size="small">{{ getSiteName(row.site_id) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="模式" min-width="140">
<template #default="{ row }">
{{ distributionModeLabel(row.distribution_mode) }}
</template>
</el-table-column>
<el-table-column label="净付款" min-width="100" align="right">
<template #default="{ row }">
<strong>{{ formatCent(row.net_paid_amount_cent) }}</strong>
</template>
</el-table-column>
<el-table-column label="分站加价" min-width="100" align="right">
<template #default="{ row }">
{{ formatCent(row.markup_amount_cent) }}
</template>
</el-table-column>
<el-table-column label="利润基数" min-width="100" align="right">
<template #default="{ row }">
{{ formatCent(row.split_base_amount_cent) }}
</template>
</el-table-column>
<el-table-column label="服务费" min-width="90" align="right">
<template #default="{ row }">
{{ formatCent(row.service_fee_cent) }}
</template>
</el-table-column>
<el-table-column label="订单状态" width="110" align="center">
<template #default="{ row }">
<el-tag :type="row.status === 'completed' ? 'success' : 'warning'" size="small">
{{ orderStatusLabel(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="退款" width="110" align="center">
<template #default="{ row }">
<span>{{ refundStatusLabel(row.refund_status) }}</span>
</template>
</el-table-column>
<el-table-column label="分销入账" min-width="120" align="center">
<template #default="{ row }">
<el-tag :type="distributorReleaseLabel(row).type" size="small">
{{ distributorReleaseLabel(row).label }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="下单时间" min-width="160">
<template #default="{ row }">
{{ formatDateTime(row.created_at) }}
</template>
</el-table-column>
</el-table>
<div class="pagination-bar">
<el-pagination
v-model:current-page="query.page"
v-model:page-size="query.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadOrders"
@size-change="loadOrders"
/>
</div>
</el-card>
</div>
</template>
<style scoped>
.distribution-orders-view {
padding: 20px;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 6px 0;
color: #1f2937;
}
.page-subtitle {
font-size: 13px;
color: #6b7280;
margin: 0;
max-width: 720px;
}
.filter-card {
padding-bottom: 0;
}
.pagination-bar {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
</style>