|
|
|
@ -51,7 +51,7 @@ |
|
|
|
<div class="business-panel account-panel" :class="{ active: tab === 'traffic' }" style="padding:18px"> |
|
|
|
<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><input v-model.number="trafficAmount" type="number" min="1" /><b>GB</b></div></label></div> |
|
|
|
<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> |
|
|
|
<div class="account-subsection-title"><h3>订单记录</h3><span>共 {{ orderTotal }} 条</span></div> |
|
|
|
@ -77,11 +77,11 @@ |
|
|
|
|
|
|
|
<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 === '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><input v-model.trim="invoiceForm.title" maxlength="128" /></label><label><span>纳税人识别号</span><input v-model.trim="invoiceForm.taxpayerNumber" maxlength="64" /></label><label><span>接收邮箱</span><input v-model.trim="invoiceForm.email" type="email" /></label><button class="primary-command" @click="saveInvoiceProfile">保存默认发票资料</button></div><div class="traffic-order-summary"><span>可申请订单</span><select v-model.number="invoiceOrderId"><option :value="0">请选择已支付订单</option><option v-for="order in invoiceOrders" :key="order.id" :value="order.id">{{ order.id }} · {{ money(order.totalPrice) }}</option></select><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><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> |
|
|
|
|
|
|
|
<div class="modal-backdrop" :class="{ open: editOpen }" @click.self="editOpen = false"><div class="recharge-modal"><h3>编辑账户资料</h3><label>账户名称<input v-model.trim="editForm.name" maxlength="32" /></label><label>邮箱<input v-model.trim="editForm.email" type="email" /></label><div class="recharge-modal-footer"><button class="cancel" @click="editOpen = false">取消</button><button class="confirm" :disabled="saving" @click="saveProfile">{{ saving ? '保存中…' : '保存' }}</button></div></div></div> |
|
|
|
<div class="modal-backdrop" :class="{ open: passwordOpen }" @click.self="passwordOpen = false"><div class="recharge-modal"><h3>修改密码</h3><label>当前密码<input v-model="passwordForm.oldPassword" type="password" /></label><label>新密码<input v-model="passwordForm.newPassword" type="password" minlength="6" /></label><div class="recharge-modal-footer"><button class="cancel" @click="passwordOpen = false">取消</button><button class="confirm" @click="changePassword">确认修改</button></div></div></div> |
|
|
|
<div class="modal-backdrop" :class="{ open: editOpen }" @click.self="editOpen = false"><div class="recharge-modal"><h3>编辑账户资料</h3><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><div class="recharge-modal-footer"><button class="cancel" @click="editOpen = false">取消</button><button class="confirm" :disabled="saving" @click="saveProfile">{{ saving ? '保存中…' : '保存' }}</button></div></div></div> |
|
|
|
<div class="modal-backdrop" :class="{ open: passwordOpen }" @click.self="passwordOpen = false"><div class="recharge-modal"><h3>修改密码</h3><label><span>当前密码</span><t-input v-model="passwordForm.oldPassword" type="password" /></label><label><span>新密码</span><t-input v-model="passwordForm.newPassword" type="password" /></label><div class="recharge-modal-footer"><button class="cancel" @click="passwordOpen = false">取消</button><button class="confirm" @click="changePassword">确认修改</button></div></div></div> |
|
|
|
</section> |
|
|
|
</template> |
|
|
|
|
|
|
|
@ -123,6 +123,7 @@ const profileRole = computed(() => profile.value?.role === 'admin' ? '管理员' |
|
|
|
const accountStatus = computed(() => profile.value?.status === 1 ? '正常' : profile.value?.status === 0 ? '已禁用' : '--') |
|
|
|
const createdAt = computed(() => profile.value?.createdAt ? fmtTime(profile.value.createdAt) : '--') |
|
|
|
const profileInitial = computed(() => profileName.value === '--' ? '-' : profileName.value.charAt(0)) |
|
|
|
const invoiceOrderOptions = computed(() => [{ label: '请选择已支付订单', value: 0 }, ...invoiceOrders.value.map((order) => ({ label: `${order.id} · ${money(order.totalPrice)}`, value: order.id }))]) |
|
|
|
const activeSimCount = computed(() => simCards.value.filter((card) => simStatus(card).cls === 'normal').length) |
|
|
|
const warnSimCount = computed(() => simTotal.value - activeSimCount.value) |
|
|
|
|
|
|
|
@ -169,3 +170,59 @@ async function createOrder() { try { const order = await request.post(urls.TRAFF |
|
|
|
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) |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.traffic-amount-input { |
|
|
|
height: 46px; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
border: 1px solid #cfdae2; |
|
|
|
border-radius: 4px; |
|
|
|
overflow: hidden; |
|
|
|
} |
|
|
|
.traffic-amount-input :deep(.t-input-number) { |
|
|
|
flex: 1; |
|
|
|
width: auto; |
|
|
|
height: 100%; |
|
|
|
} |
|
|
|
.traffic-amount-input :deep(.t-input) { |
|
|
|
height: 100%; |
|
|
|
border: 0; |
|
|
|
border-radius: 0; |
|
|
|
box-shadow: none; |
|
|
|
font-size: 19px; |
|
|
|
} |
|
|
|
.traffic-amount-input :deep(.t-input:focus), |
|
|
|
.traffic-amount-input :deep(.t-is-focused) { |
|
|
|
box-shadow: none; |
|
|
|
border-color: transparent; |
|
|
|
} |
|
|
|
.traffic-amount-input b { |
|
|
|
padding: 0 13px; |
|
|
|
color: #6c7d88; |
|
|
|
font-size: 12px; |
|
|
|
font-weight: 500; |
|
|
|
flex-shrink: 0; |
|
|
|
} |
|
|
|
.traffic-amount-block :deep(.t-input), |
|
|
|
.traffic-amount-block :deep(.t-select), |
|
|
|
.traffic-order-summary :deep(.t-select), |
|
|
|
.recharge-modal :deep(.t-input) { |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
.traffic-amount-block > label { |
|
|
|
display: block; |
|
|
|
margin-bottom: 12px; |
|
|
|
} |
|
|
|
.traffic-order-summary :deep(.t-select) { |
|
|
|
margin: 10px 0 14px; |
|
|
|
} |
|
|
|
.recharge-modal label { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
gap: 8px; |
|
|
|
margin: 12px 0 0; |
|
|
|
color: #4e5f6b; |
|
|
|
font-size: 12px; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|