From ffbbd5f11bcc60816b7a7244e927276ca5143846 Mon Sep 17 00:00:00 2001
From: xiaosi <2652281683@qq.com>
Date: Tue, 1 Sep 2026 18:13:18 +0800
Subject: [PATCH] feat: add traffic purchase intake tab
Show platform pool status and allow creating purchase batches.
---
src/views/TrafficOpsView/TrafficOpsView.vue | 306 +++++++++++++++++++-
1 file changed, 304 insertions(+), 2 deletions(-)
diff --git a/src/views/TrafficOpsView/TrafficOpsView.vue b/src/views/TrafficOpsView/TrafficOpsView.vue
index ac146b5..c931a06 100644
--- a/src/views/TrafficOpsView/TrafficOpsView.vue
+++ b/src/views/TrafficOpsView/TrafficOpsView.vue
@@ -69,7 +69,78 @@
-
采购入池占位
+
+
+ 尚未建立资源池,首次录入采购后自动创建
+
+
+
+ 资源池总量
+ {{ bytesToGbText(pool.totalBytes) }}
+ {{ pool.totalBytes ?? 0 }} bytes
+
+
+ 可用流量
+ {{ bytesToGbText(pool.availableBytes) }}
+ {{ pool.availableBytes ?? 0 }} bytes
+
+
+ 池状态
+ {{ pool.status === 'active' ? '有效' : (pool.status || '—') }}
+ {{ pool.status || '—' }}
+
+
+
+
+
+
+
+
+
+
+
+
账务流水占位
@@ -151,6 +222,77 @@
+
+
+
+
+ 取消
+ 确认录入
+
+
@@ -337,8 +479,143 @@ async function disablePackage(row) {
}
}
+const pool = ref(null)
+const poolMissing = ref(false)
+const purchaseList = ref([])
+const purchaseTotal = ref(0)
+const purchasePageNum = ref(1)
+const purchasePageSize = ref(10)
+const purchaseProvider = ref('')
+const purchaseStatus = ref('')
+const purchaseModalVisible = ref(false)
+const purchaseFormError = ref('')
+const purchaseForm = reactive({
+ batchNo: '',
+ provider: '',
+ totalGb: 1,
+ unitCost: 0,
+ totalCost: 0,
+ expiresAt: '',
+ remark: ''
+})
+
+const purchaseStatusOptions = [
+ { label: '全部状态', value: '' },
+ { label: '有效', value: 'active' },
+ { label: '失效', value: 'inactive' }
+]
+const purchaseColumns = [
+ { colKey: 'batchNo', title: '批次号', width: '12%' },
+ { colKey: 'provider', title: '供应商', width: '10%' },
+ { colKey: 'totalText', title: '总量', width: '10%' },
+ { colKey: 'costText', title: '成本', width: '10%' },
+ { colKey: 'expiresAt', title: '到期', width: '14%' },
+ { colKey: 'statusName', title: '状态', width: '8%' },
+ { colKey: 'remark', title: '备注', width: '14%', ellipsis: true },
+ { colKey: 'createdAt', title: '创建时间', width: '16%' }
+]
+
+async function loadPool() {
+ poolMissing.value = false
+ try {
+ pool.value = await request.get(urls.ADMIN_TRAFFIC_POOL)
+ } catch (e) {
+ if (e.status === 404 || /资源不存在|not found/i.test(e.message || '')) {
+ pool.value = null
+ poolMissing.value = true
+ return
+ }
+ ui.toast(e.message || '加载资源池失败')
+ }
+}
+
+async function loadPurchases() {
+ try {
+ const params = { pageNum: purchasePageNum.value, pageSize: purchasePageSize.value }
+ if (purchaseProvider.value) params.provider = purchaseProvider.value
+ if (purchaseStatus.value) params.status = purchaseStatus.value
+ const page = await request.get(urls.ADMIN_TRAFFIC_PURCHASES, { params })
+ purchaseTotal.value = page?.total || 0
+ purchaseList.value = (page?.records || []).map((p) => ({
+ id: String(p.id),
+ batchNo: p.batchNo || '—',
+ provider: p.provider || '—',
+ totalText: bytesToGbText(p.totalBytes),
+ costText: `¥${Number(p.totalCost || 0).toFixed(2)}`,
+ expiresAt: fmtTime(p.expiresAt),
+ statusName: p.status === 'active' ? '有效' : (p.status || '—'),
+ remark: p.remark || '',
+ createdAt: fmtTime(p.createdAt),
+ raw: p
+ }))
+ } catch (e) {
+ ui.toast(e.message || '加载采购列表失败')
+ }
+}
+
+async function loadPurchasesTab() {
+ await Promise.all([loadPool(), loadPurchases()])
+}
+
+function reloadPurchases() {
+ purchasePageNum.value = 1
+ loadPurchases()
+}
+
+function openPurchaseAdd() {
+ Object.assign(purchaseForm, {
+ batchNo: '',
+ provider: '',
+ totalGb: 1,
+ unitCost: 0,
+ totalCost: 0,
+ expiresAt: '',
+ remark: ''
+ })
+ purchaseFormError.value = ''
+ purchaseModalVisible.value = true
+}
+
+async function submitPurchase() {
+ purchaseFormError.value = ''
+ if (!purchaseForm.batchNo.trim() || !purchaseForm.provider.trim()) {
+ purchaseFormError.value = '请填写批次号和供应商'
+ return
+ }
+ if (!(Number(purchaseForm.totalGb) > 0)) {
+ purchaseFormError.value = '采购总量必须大于 0 GB'
+ return
+ }
+ const payload = {
+ batchNo: purchaseForm.batchNo.trim(),
+ provider: purchaseForm.provider.trim(),
+ totalBytes: Math.round(Number(purchaseForm.totalGb) * GB_BYTES),
+ unitCost: Number(purchaseForm.unitCost || 0),
+ totalCost: Number(purchaseForm.totalCost || 0),
+ remark: purchaseForm.remark || ''
+ }
+ if (purchaseForm.expiresAt) {
+ const expires = new Date(purchaseForm.expiresAt)
+ if (Number.isNaN(expires.getTime())) {
+ purchaseFormError.value = '到期时间格式无效'
+ return
+ }
+ payload.expiresAt = expires.toISOString()
+ }
+ try {
+ await request.post(urls.ADMIN_TRAFFIC_PURCHASES, payload)
+ ui.toast('采购已录入')
+ purchaseModalVisible.value = false
+ await loadPurchasesTab()
+ } catch (e) {
+ purchaseFormError.value = e.message || '录入失败'
+ }
+}
+
+
watch(tab, (v) => {
if (v === 'packages') loadPackages()
+ if (v === 'purchases') loadPurchasesTab()
})
onMounted(() => {
@@ -347,12 +624,37 @@ onMounted(() => {