@ -25,7 +25,7 @@
< div class = "tenant-identity" >
< span > { { profileInitial } } < / span >
< div > < strong > { { profileName } } < / strong > < small > 账户角色 · { { profileRole } } < / small > < / div >
< button class = "table-text-action " @ click = "editOpen = true" > 编辑 < / button >
< t -button variant = "text" theme = "primary " @ click = "editOpen = true" > 编辑 < / t - b u t t o n >
< / div >
< dl class = "account-details" >
< div > < dt > 账户名称 < / dt > < dd > { { profileName } } < / dd > < / div >
@ -43,7 +43,7 @@
< div > < span > 视频资产 < b > { { resourceSummary ? . videoCount ? ? '--' } } 个 / 本月下载 { { resourceSummary ? . monthDownloadCount ? ? '--' } } 次 < / b > < / span > < / div >
< div > < span > 本月媒体流量 < b > { { resourceSummary ? ` ${ Number ( resourceSummary . monthUsageGb ) . toFixed ( 2 ) } GB ` : '--' } } < / b > < / span > < / div >
< / div >
< div class = "account-card-actions" > < button class = "primary-command " @ click = "tab = 'traffic'" > 查看流量订单 < / button > < button class = "plain-command" @ click = "tab = 'sim'" > 管理通信卡 < / button > < / div >
< div class = "account-card-actions" > < t -button theme = "primary" @ click = "tab = 'traffic'" > 查看流量订单 < / t - b u t t o n > < t - b u t t o n v a r i a n t = " o u t l i n e " @ c l i c k = " t a b = ' s i m ' " > 管 理 通 信 卡 < / t - b u t t o n > < / d i v >
< / section >
< / div >
< / div >
@ -52,16 +52,16 @@
< div class = "traffic-balance-card" > < span > 当前云媒体流量余额 < / span > < strong > { { balance } } < small > GB < / small > < / strong > < p > 流量用于直播 、 回放与原始素材下载 < / p > < / div >
< div class = "traffic-purchase-layout" style = "margin-top:16px" >
< div class = "traffic-amount-block" > < label > < span > 购买流量 ( GB ) < / span > < div class = "traffic-amount-input" > < t -input -number v -model = " trafficAmount " theme = "normal" :min ="1" / > < b > GB < / b > < / div > < / label > < / div >
< div class = "traffic-order-summary" > < span > 订单概要 < / span > < strong > 云媒体流量订单 < / strong > < small > 订单创建后由管理员人工确认 , 金额以服务端订单为准 。 < / small > < button class = "primary-command " @click ="createOrder" > 创建订单 < / button > < / div >
< div class = "traffic-order-summary" > < span > 订单概要 < / span > < strong > 云媒体流量订单 < / strong > < small > 订单创建后由管理员人工确认 , 金额以服务端订单为准 。 < / small > < t -button theme = "primary" @click ="createOrder" > 创建订单 < / t -button > < / div >
< / div >
< div class = "account-subsection-title" > < h3 > 订单记录 < / h3 > < span > 共 { { orderTotal } } 条 < / span > < / div >
< div class = "account-order-table" > < div class = "order-head" > < span > 订单号 < / span > < span > 内容 < / span > < span > 单价 < / span > < span > 金额 < / span > < span > 状态 < / span > < span > 时间 < / span > < / div > < div v-for ="o in orders" :key="o.id"><span>{{ o.id }}</span><span>{{ o.amountGb }} GB 流量</span><span>{{ money(o.unitPrice) }}</span><strong>{{ money(o.totalPrice) }}</strong><em :class="{ processing: o.payStatus === 'unpaid' }" > {{ payStatusName ( o.payStatus ) }} < / em > < span > { { fmtTime ( o . createdAt ) } } < / span > < / div > < / div >
< div v-if ="!orders.length" class="table-empty" > 暂无流量订单 < / div >
< div class = "table-footer" v-if ="orderTotal" > < span > 第 { { orderPageNum } } / { { orderPages } } 页 < / span > < div > < button @ click = "goOrderPage(orderPageNum - 1)" > 上一页 < / button > < button @ click = "goOrderPage(orderPageNum + 1)" > 下一页 < / button > < / div > < / div >
< div class = "table-footer" v-if ="orderTotal" > < t -pagination v -model :current ="orderPageNum" :total ="orderTotal" :page-size ="10" @current-change ="loadOrders" / > < / div >
< div class = "account-subsection-title" > < h3 > 消费明细 < / h3 > < span > 共 { { usageTotal } } 条 < / span > < / div >
< div class = "account-order-table usage-table" > < div class = "order-head" > < span > 来源 < / span > < span > 对象 ID < / span > < span > 用量 < / span > < span > 扣减前 < / span > < span > 扣减后 < / span > < span > 时间 < / span > < / div > < div v-for ="u in usage" :key="u.id" > < span > { { sourceName ( u . sourceType ) } } < / span > < span > { { u . sourceId || '--' } } < / span > < strong > { { fmtGb ( u . bytesUsed ) } } < / strong > < span > { { fmtGb ( u . balanceBefore ) } } < / span > < span > { { fmtGb ( u . balanceAfter ) } } < / span > < span > { { fmtTime ( u . createdAt ) } } < / span > < / div > < / div >
< div v-if ="!usage.length" class="table-empty" > 暂无流量消费记录 < / div >
< div class = "table-footer" v-if ="usageTotal" > < span > 第 { { usagePageNum } } / { { usagePages } } 页 < / span > < div > < button @ click = "goUsagePage(usagePageNum - 1)" > 上一页 < / button > < button @ click = "goUsagePage(usagePageNum + 1)" > 下一页 < / button > < / div > < / div >
< div class = "table-footer" v-if ="usageTotal" > < t -pagination v -model :current ="usagePageNum" :total ="usageTotal" :page-size ="10" @current-change ="loadUsage" / > < / div >
< div class = "account-subsection-title" > < h3 > 下载记录 < / h3 > < span > 共 { { downloadTotal } } 条 < / span > < / div >
< div class = "account-order-table" > < div class = "order-head" > < span > 文件 < / span > < span > 无人机 < / span > < span > 执行记录 < / span > < span > 下载用量 < / span > < span > 时间 < / span > < / div > < div v-for ="item in downloads" :key="item.id"><strong>{{ item.fileName || '--' }}</strong><span>{{ item.droneSn || '--' }}</span><span>{{ item.executionId || '--' }}</span><span>{{ fmtGb(item.bytes) }}</span><span>{{ fmtTime(item.createdAt) }}</span></div></div><div v-if="!downloads.length" class="table-empty" > 暂无下载记录 < / div >
< / div >
@ -69,15 +69,15 @@
< div class = "business-panel account-panel" : class = "{ active: tab === 'sim' }" style = "padding:16px" >
< div class = "communication-status-grid" > < div > < strong > { { simTotal } } < / strong > < span > 通信卡总数 < / span > < / div > < div > < strong > { { activeSimCount } } < / strong > < span > 正常 < / span > < / div > < div > < strong > { { warnSimCount } } < / strong > < span > 异常或未同步 < / span > < / div > < / div >
< div class = "communication-table-wrap" > < table class = "communication-table" > < thead > < tr > < th > 运营商 < / th > < th > ICCID / 号码 < / th > < th > 绑定机巢 < / th > < th > 套餐用量 < / th > < th > 状态 < / th > < th > 最后同步 < / th > < th > 操作 < / th > < / tr > < / thead > < tbody > < tr v-for ="c in simCards" :key="c.id"><td><strong>{{ c.carrier || '--' }}</strong></td><td><strong>{{ c.iccid || '--' }}</strong><small>{{ c.phone || '--' }}</small></td><td>{{ c.dockId || '--' }}</td><td>{{ c.lastSyncAt ? `${Number(c.usedGb || 0).toFixed(1)} / ${c.planGb || 0} GB` : '暂无运营商同步数据' }}</td><td><span class="carrier-status" :class="simStatus(c).cls"><i></i>{{ simStatus(c).name }}</span></td><td>{{ fmtTime(c.lastSyncAt) }}</td><td><span>运营商通道未接入</span></td></tr></tbody></table><div v-if="!simCards.length" class="table-empty" > 暂无通信卡 < / div > < / div >
< div class = "table-footer" v-if ="simTotal" > < span > 第 { { simPageNum } } / { { simPages } } 页 < / span > < div > < button @ click = "goSimPage(simPageNum - 1)" > 上一页 < / button > < button @ click = "goSimPage(simPageNum + 1)" > 下一页 < / button > < / div > < / div >
< div class = "table-footer" v-if ="simTotal" > < t -pagination v -model :current ="simPageNum" :total ="simTotal" :page-size ="10" @current-change ="loadSim" / > < / div >
< div class = "account-subsection-title" > < h3 > 通信卡充值记录 < / h3 > < span > 共 { { simRechargeTotal } } 条 < / span > < / div >
< div class = "account-order-table sim-recharge-table" > < div class = "order-head" > < span > 通信卡 < / span > < span > 充值流量 < / span > < span > 金额 < / span > < span > 支付状态 < / span > < span > 充值状态 < / span > < span > 时间 < / span > < / div > < div v-for ="r in simRechargeLogs" :key="r.id"><span>{{ cardLabel(r.simCardId) }}</span><strong>{{ r.amountGb }} GB</strong><strong>{{ money(r.totalPrice) }}</strong><em>{{ payStatusName(r.payStatus) }}</em><em :class="{ processing: r.rechargeStatus === 'pending' }">{{ rechargeStatusName(r.rechargeStatus) }}</em><span>{{ fmtTime(r.createdAt) }}</span></div></div><div v-if="!simRechargeLogs.length" class="table-empty" > 暂无通信卡充值记录 < / div >
< div class = "table-footer" v-if ="simRechargeTotal" > < span > 第 { { simRechargePageNum } } / { { simRechargePages } } 页 < / span > < div > < button @ click = "goSimRechargePage(simRechargePageNum - 1)" > 上一页 < / button > < button @ click = "goSimRechargePage(simRechargePageNum + 1)" > 下一页 < / button > < / div > < / div >
< div class = "table-footer" v-if ="simRechargeTotal" > < t -pagination v -model :current ="simRechargePageNum" :total ="simRechargeTotal" :page-size ="10" @current-change ="loadSim" / > < / div >
< / div >
< div class = "business-panel account-panel" : class = "{ active: tab === 'security' }" style = "padding:18px" > < div class = "account-section-heading" > < h2 > 账号安全 < / h2 > < span > 修改密码后将退出所有设备 < / span > < / div > < div class = "security-list" > < div > < span > < strong > 登录手机号 < / strong > < small > { { maskPhone ( profilePhone ) } } < / small > < / span > < / div > < div > < span > < strong > 绑定邮箱 < / strong > < small > { { profileEmail } } < / small > < / span > < / div > < div > < span > < strong > 登录会话 < / strong > < small > 共 { { sessions . length } } 个有效会话 < / small > < / span > < button class = "plain-command " @click ="revokeOthers" > 退出其他设备 < / button > < / div > < / div > < div class = "account-order-table" > < div class = "order-head" > < span > 设备 < / span > < span > IP < / span > < span > 最近活跃 < / span > < span > 过期时间 < / span > < span > 操作 < / span > < / div > < div v-for ="session in sessions" :key="session.id"><span>{{ session.userAgent || '未知设备' }}{{ session.current ? '(当前)' : '' }}</span><span>{{ session.ip || '--' }}</span><span>{{ fmtTime(session.lastActiveAt) }}</span><span>{{ fmtTime(session.expiresAt) }}</span><button v-if="!session.current" class="table-text-action " @click="revokeSession(session.id)">退出</button><span v-else>当前会话</span></div></div><div v-if="!sessions.length" class="table-empty">暂无有效会话</div><div class="account-card-actions"><button class="primary-command " @click="passwordOpen = true" > 修改密码 < / button > < / div > < / div >
< div class = "business-panel account-panel" : class = "{ active: tab === 'security' }" style = "padding:18px" > < div class = "account-section-heading" > < h2 > 账号安全 < / h2 > < span > 修改密码后将退出所有设备 < / span > < / div > < div class = "security-list" > < div > < span > < strong > 登录手机号 < / strong > < small > { { maskPhone ( profilePhone ) } } < / small > < / span > < / div > < div > < span > < strong > 绑定邮箱 < / strong > < small > { { profileEmail } } < / small > < / span > < / div > < div > < span > < strong > 登录会话 < / strong > < small > 共 { { sessions . length } } 个有效会话 < / small > < / span > < t -button variant = "outline " @click ="revokeOthers" > 退出其他设备 < / t -button > < / div > < / div > < div class = "account-order-table" > < div class = "order-head" > < span > 设备 < / span > < span > IP < / span > < span > 最近活跃 < / span > < span > 过期时间 < / span > < span > 操作 < / span > < / div > < div v-for ="session in sessions" :key="session.id"><span>{{ session.userAgent || '未知设备' }}{{ session.current ? '(当前)' : '' }}</span><span>{{ session.ip || '--' }}</span><span>{{ fmtTime(session.lastActiveAt) }}</span><span>{{ fmtTime(session.expiresAt) }}</span><t-button v-if="!session.current" variant="text" theme="primary " @click="revokeSession(session.id)">退出</t- button><span v-else>当前会话</span></div></div><div v-if="!sessions.length" class="table-empty">暂无有效会话</div><div class="account-card-actions"><t-button theme="primary " @click="passwordOpen = true" > 修改密码 < / t -button > < / div > < / div >
< div class = "business-panel account-panel" : class = "{ active: tab === 'invoice' }" style = "padding:18px" > < div class = "account-section-heading" > < h2 > 发票资料与申请 < / h2 > < span > 仅对已人工确认的流量订单开放申请 < / span > < / div > < div class = "traffic-purchase-layout" > < div class = "traffic-amount-block" > < label > < span > 发票抬头 < / span > < t -input v -model .trim = " invoiceForm.title " :maxlength ="128" / > < / label > < label > < span > 纳税人识别号 < / span > < t -input v -model .trim = " invoiceForm.taxpayerNumber " :maxlength ="64" / > < / label > < label > < span > 接收邮箱 < / span > < t -input v -model .trim = " invoiceForm.email " type = "email" / > < / label > < button class = "primary-command " @click ="saveInvoiceProfile" > 保存默认发票资料 < / button > < / div > < div class = "traffic-order-summary" > < span > 可申请订单 < / span > < t -select v-model ="invoiceOrderId" :options="invoiceOrderOptions" style="width: 100%" /><button class="primary-command " :disabled="!invoiceOrderId" @click="createInvoiceRequest">提交开具发票</button></div></div><div class="account-subsection-title"><h3>发票记录</h3><span>仅展示已提交的开具申请</span></div><div class="account-order-table"><div class="order-head"><span>订单号</span><span>金额</span><span>抬头</span><span>状态</span><span>申请时间</span></div><div v-for="request in invoiceRequests" :key="request.id"><span>{{ request.orderId }}</span><strong>{{ money(request.amount) }}</strong><span>{{ request.title }}</span><em :class="{ processing: request.status === 'pending' }">{{ invoiceStatusName(request.status) }}</em><span>{{ fmtTime(request.requestedAt) }}</span></div></div><div v-if="!invoiceRequests.length" class="table-empty" > 暂无发票申请 < / div > < / div >
< div class = "business-panel account-panel" : class = "{ active: tab === 'invoice' }" style = "padding:18px" > < div class = "account-section-heading" > < h2 > 发票资料与申请 < / h2 > < span > 仅对已人工确认的流量订单开放申请 < / span > < / div > < div class = "traffic-purchase-layout" > < div class = "traffic-amount-block" > < label > < span > 发票抬头 < / span > < t -input v -model .trim = " invoiceForm.title " :maxlength ="128" / > < / label > < label > < span > 纳税人识别号 < / span > < t -input v -model .trim = " invoiceForm.taxpayerNumber " :maxlength ="64" / > < / label > < label > < span > 接收邮箱 < / span > < t -input v -model .trim = " invoiceForm.email " type = "email" / > < / label > < t -button theme = "primary" @click ="saveInvoiceProfile" > 保存默认发票资料 < / t -button > < / div > < div class = "traffic-order-summary" > < span > 可申请订单 < / span > < t -select v-model ="invoiceOrderId" :options="invoiceOrderOptions" style="width: 100%" /><t-button theme="primary " :disabled="!invoiceOrderId" @click="createInvoiceRequest">提交开具发票</t- button></div></div><div class="account-subsection-title"><h3>发票记录</h3><span>仅展示已提交的开具申请</span></div><div class="account-order-table"><div class="order-head"><span>订单号</span><span>金额</span><span>抬头</span><span>状态</span><span>申请时间</span></div><div v-for="request in invoiceRequests" :key="request.id"><span>{{ request.orderId }}</span><strong>{{ money(request.amount) }}</strong><span>{{ request.title }}</span><em :class="{ processing: request.status === 'pending' }">{{ invoiceStatusName(request.status) }}</em><span>{{ fmtTime(request.requestedAt) }}</span></div></div><div v-if="!invoiceRequests.length" class="table-empty" > 暂无发票申请 < / div > < / div >
< / div >
< t -dialog
@ -90,10 +90,8 @@
< label > < span > 账户名称 < / span > < t -input v -model .trim = " editForm.name " :maxlength ="32" / > < / label >
< label > < span > 邮箱 < / span > < t -input v -model .trim = " editForm.email " type = "email" / > < / label >
< template # footer >
< div class = "recharge-modal-footer" >
< button class = "cancel" @ click = "editOpen = false" > 取消 < / button >
< button class = "confirm" :disabled ="saving" @click ="saveProfile" > {{ saving ? ' 保存中 … ' : ' 保存 ' }} < / button >
< / div >
< t -button variant = "outline" @ click = "editOpen = false" > 取消 < / t - b u t t o n >
< t -button theme = "primary" :disabled ="saving" :loading ="saving" @click ="saveProfile" > {{ saving ? ' 保存中 … ' : ' 保存 ' }} < / t -button >
< / template >
< / t - d i a l o g >
< t -dialog
@ -106,10 +104,8 @@
< label > < span > 当前密码 < / span > < t -input v -model = " passwordForm.oldPassword " type = "password" / > < / label >
< label > < span > 新密码 < / span > < t -input v -model = " passwordForm.newPassword " type = "password" / > < / label >
< template # footer >
< div class = "recharge-modal-footer" >
< button class = "cancel" @ click = "passwordOpen = false" > 取消 < / button >
< button class = "confirm" @click ="changePassword" > 确认修改 < / button >
< / div >
< t -button variant = "outline" @ click = "passwordOpen = false" > 取消 < / t - b u t t o n >
< t -button theme = "primary" @click ="changePassword" > 确认修改 < / t -button >
< / template >
< / t - d i a l o g >
< / section >
@ -192,10 +188,6 @@ async function load() {
] )
if ( results . some ( ( result ) => result . status === 'rejected' ) ) ui . toast ( '部分账户数据加载失败' )
}
function goOrderPage ( page ) { if ( page < 1 || page > orderPages . value ) return ; orderPageNum . value = page ; loadOrders ( ) }
function goUsagePage ( page ) { if ( page < 1 || page > usagePages . value ) return ; usagePageNum . value = page ; loadUsage ( ) }
function goSimPage ( page ) { if ( page < 1 || page > simPages . value ) return ; simPageNum . value = page ; loadSim ( ) }
function goSimRechargePage ( page ) { if ( page < 1 || page > simRechargePages . value ) return ; simRechargePageNum . value = page ; loadSim ( ) }
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 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 )
@ -261,7 +253,9 @@ onMounted(load)
color : # 4 e5f6b ;
font - size : 12 px ;
}
. recharge - modal . t - dialog . recharge - modal - footer {
margin - top : 0 ;
. recharge - modal . t - dialog . t - dialog__footer {
display : flex ;
justify - content : flex - end ;
gap : 8 px ;
}
< / style >