diff --git a/src/views/AccountView/AccountView.vue b/src/views/AccountView/AccountView.vue index da006cc..5066ae2 100644 --- a/src/views/AccountView/AccountView.vue +++ b/src/views/AccountView/AccountView.vue @@ -51,16 +51,60 @@
当前云媒体流量余额{{ balance }} GB

流量用于直播、回放与原始素材下载

-
-
订单概要云媒体流量订单订单创建后由管理员人工确认,金额以服务端订单为准。创建订单
+
+ +
+
+ 订单概要 + 云媒体流量订单 + 创建订单后发起微信支付;金额以服务端订单为准。 + 购买并支付 +

订单记录

共 {{ orderTotal }} 条
- + +

消费明细

共 {{ usageTotal }} 条
@@ -206,7 +250,10 @@ const deviceCount = ref('--') const deviceLoadFailed = ref(false) const resourceSummary = ref(null) const downloads = ref([]); const downloadTotal = ref(0) -const trafficAmount = ref(1) +const trafficPackages = ref([]) +const selectedTrafficPackageId = ref(0) +const trafficOrdering = ref(false) +const payingOrderId = ref(0) const sessions = ref([]) const invoiceProfiles = ref([]); const invoiceOrders = ref([]); const invoiceRequests = ref([]); const invoiceOrderId = ref(0) const invoiceForm = ref({ title: '', taxpayerNumber: '', email: '', isDefault: true }) @@ -223,7 +270,8 @@ const orderColumns = [ { colKey: 'unitPrice', title: '单价' }, { colKey: 'totalPrice', title: '金额' }, { colKey: 'payStatus', title: '状态' }, - { colKey: 'createdAt', title: '时间' } + { colKey: 'createdAt', title: '时间' }, + { colKey: 'actions', title: '操作' } ] const usageColumns = [ { colKey: 'sourceType', title: '来源' }, @@ -291,8 +339,31 @@ function applyPage(targetRef, page) { function fmtTime(value) { const date = new Date(value); return value && !Number.isNaN(date.getTime()) ? date.toLocaleString('zh-CN', { hour12: false }) : '--' } function fmtGb(bytes) { return bytes == null ? '--' : `${(Number(bytes) / 1024 / 1024 / 1024).toFixed(2)} GB` } function money(value) { return value == null ? '--' : `¥${Number(value).toFixed(2)}` } +function moneyFromFen(fen) { return fen == null ? '--' : `¥${(Number(fen) / 100).toFixed(2)}` } function sourceName(type) { return ({ live: '直播', replay: '回放', download: '下载' })[type] || type || '--' } -function payStatusName(status) { return ({ unpaid: '待人工确认', paid: '已入账' })[status] || status || '--' } +function payStatusName(status) { + return ({ + unpaid: '待支付', + paid: '已支付', + cancelled: '已取消', + closed: '已关闭' + })[status] || status || '--' +} +function describePayment(payment) { + if (!payment) return '支付已创建' + const amount = moneyFromFen(payment.totalFeeFen) + if (payment.providerPayload) return `支付已创建(${amount}),请按返回凭证完成支付` + return `支付已创建(${amount}),状态:${payment.status || '--'}` +} +async function createPayment(businessType, businessOrderId) { + return request.post(urls.PAYMENTS, { businessType, businessOrderId }) +} +function orderContentLabel(row) { + if (row?.amountGb != null) return `${row.amountGb} GB 流量` + if (row?.packageName) return row.packageName + if (row?.amountBytes != null) return fmtGb(row.amountBytes) + return '--' +} function rechargeStatusName(status) { return ({ pending: '处理中', success: '充值成功', failed: '充值失败' })[status] || status || '--' } function invoiceStatusName(status) { return ({ pending: '待人工处理', issued: '已开具', rejected: '已驳回', cancelled: '已取消' })[status] || status || '--' } function maskPhone(phone) { const value = String(phone || ''); return value.length > 7 ? `${value.slice(0, 3)}****${value.slice(-4)}` : value || '--' } @@ -311,6 +382,17 @@ async function loadOrders() { try { const page = await request.get(urls.TRAFFIC_ async function loadUsage() { try { const page = await request.get(urls.TRAFFIC_USAGE, { params: { pageNum: usagePageNum.value, pageSize: 10 } }); const meta = applyPage(usage, page); usageTotal.value = meta.total; usagePages.value = meta.pages } catch { usage.value = [] } } async function loadSim() { const [cards, logs] = await Promise.all([request.get(urls.SIM_CARDS, { params: { pageNum: simPageNum.value, pageSize: 10 } }), request.get(urls.SIM_RECHARGE_LOGS, { params: { pageNum: simRechargePageNum.value, pageSize: 10 } })]); const cardMeta = applyPage(simCards, cards); const logMeta = applyPage(simRechargeLogs, logs); simTotal.value = cardMeta.total; simPages.value = cardMeta.pages; simRechargeTotal.value = logMeta.total; simRechargePages.value = logMeta.pages } async function loadDeviceCount() { try { await deviceStore.load(); deviceCount.value = deviceStore.docks.length + deviceStore.drones.length } catch { deviceLoadFailed.value = true } } +async function loadTrafficPackages() { + try { + const list = await request.get(urls.TRAFFIC_PACKAGES) + trafficPackages.value = Array.isArray(list) ? list : [] + if (!selectedTrafficPackageId.value && trafficPackages.value.length) { + selectedTrafficPackageId.value = trafficPackages.value[0].id + } + } catch { + trafficPackages.value = [] + } +} async function load() { const results = await Promise.allSettled([ loadProfile(), @@ -319,11 +401,56 @@ async function load() { loadUsage(), loadSim(), loadDeviceCount(), - loadAccountExtensions() + loadAccountExtensions(), + loadTrafficPackages() ]) if (results.some((result) => result.status === 'rejected')) ui.toast('部分账户数据加载失败') } -async function createOrder() { try { const order = await request.post(urls.TRAFFIC_ORDERS, { amountGb: Number(trafficAmount.value) || 1 }); ui.toast(`订单已创建,金额 ${money(order.totalPrice)},请等待管理员确认`); orderPageNum.value = 1; await loadOrders() } catch (error) { ui.toast(error.message || '创建订单失败') } } +async function createTrafficOrderAndPay() { + if (!selectedTrafficPackageId.value) { + ui.toast('请选择流量套餐') + return + } + trafficOrdering.value = true + try { + const order = await request.post(urls.TRAFFIC_ORDERS, { + packageId: Number(selectedTrafficPackageId.value) + }) + const payment = await createPayment('traffic_order', order.id) + ui.toast(describePayment(payment)) + orderPageNum.value = 1 + await Promise.all([loadOrders(), request.get(urls.TRAFFIC_BALANCE).then((data) => { + balance.value = data?.balanceGb == null ? '--' : Number(data.balanceGb).toFixed(1) + })]) + } catch (error) { + ui.toast(error.message || '购买失败') + } finally { + trafficOrdering.value = false + } +} +async function payTrafficOrder(row) { + payingOrderId.value = row.id + try { + const payment = await createPayment('traffic_order', row.id) + ui.toast(describePayment(payment)) + await loadOrders() + } catch (error) { + ui.toast(error.message || '发起支付失败') + } finally { + payingOrderId.value = 0 + } +} +async function cancelTrafficOrder(id) { + const ok = await ui.confirm('取消该流量订单?', '仅未支付订单可取消。', ['订单将关闭', '可重新选择套餐下单']) + if (!ok) return + try { + await request.delete(urls.TRAFFIC_ORDER(id)) + ui.toast('订单已取消') + await loadOrders() + } catch (error) { + ui.toast(error.message || '取消失败') + } +} async function saveProfile() { saving.value = true; try { const updated = await request.put(urls.ACCOUNT_PROFILE, editForm.value); profile.value = updated; userStore.user = { ...(userStore.user || {}), ...updated }; localStorage.setItem('user', JSON.stringify(userStore.user)); editOpen.value = false; ui.toast('账户资料已保存') } catch (error) { ui.toast(error.message || '保存账户资料失败') } finally { saving.value = false } } onMounted(load)