优化财务仪表盘UI与快捷时间筛选

This commit is contained in:
yml2213
2026-08-20 16:10:55 +08:00
parent 80e329a604
commit 9a52b51c89
25 changed files with 1626 additions and 438 deletions
+4 -1
View File
@@ -27,6 +27,9 @@ export async function fetchBackupSchedule() {
}
export async function updateBackupSchedule(payload: BackupSchedule) {
const { data } = await apiClient.put<ApiResponse<BackupSchedule>>('/admin/backups/schedule', payload)
const { data } = await apiClient.put<ApiResponse<BackupSchedule>>(
'/admin/backups/schedule',
payload
)
return data.data
}
@@ -337,7 +337,9 @@ async function handleArbitrate() {
result: result.value,
remark: remark.value.trim(),
amount_cent:
isCompletedResult.value && amount.value !== undefined ? yuanToCent(amount.value) : undefined,
isCompletedResult.value && amount.value !== undefined
? yuanToCent(amount.value)
: undefined,
actual_coin_consumed_m: isCompletedResult.value ? actualCoinConsumedM.value : undefined,
actual_consumable_amount_cent: isCompletedResult.value
? yuanToCent(actualConsumableAmountYuan.value)
File diff suppressed because it is too large Load Diff
@@ -36,7 +36,7 @@ interface RiskItem {
level: RiskLevel
}
const defaultScreenshotURL = '/api/listings/default-upload-screenshot?v=webp1'
const _defaultScreenshotURL = '/api/listings/default-upload-screenshot?v=webp1'
const rejectReasonOptions = [
'默认截图,需补充真实截图',
'账号资产信息不完整',
@@ -209,10 +209,7 @@ function restoreListState() {
if (typeof state.page === 'number' && state.page > 0) {
currentPage.value = state.page
}
if (
typeof state.pageSize === 'number' &&
[10, 20, 50, 100].includes(state.pageSize)
) {
if (typeof state.pageSize === 'number' && [10, 20, 50, 100].includes(state.pageSize)) {
pageSize.value = state.pageSize
}
} catch {
@@ -78,11 +78,9 @@ async function handleSave() {
async function handleDelete(item: MohongCategory) {
try {
await ElMessageBox.confirm(
`确认删除分类「${item.name}」?其下商品将变为未分类。`,
'提示',
{ type: 'warning' }
)
await ElMessageBox.confirm(`确认删除分类「${item.name}」?其下商品将变为未分类。`, '提示', {
type: 'warning',
})
await deleteAdminMohongCategory(item.id)
ElMessage.success('已删除')
await load()
@@ -64,7 +64,9 @@ async function uploadQrcode(file: File) {
<div class="page-head">
<div>
<h2>撞车配置</h2>
<p>全局默认固定二维码与订单复制模板商品可覆盖默认二维码支付后用户在订单详情扫码并复制信息联系客服</p>
<p>
全局默认固定二维码与订单复制模板商品可覆盖默认二维码支付后用户在订单详情扫码并复制信息联系客服
</p>
</div>
<el-button type="primary" :loading="saving" @click="handleSave">保存配置</el-button>
</div>
@@ -91,8 +93,9 @@ async function uploadQrcode(file: File) {
<div class="hint">
可用变量&#123;&#123;order_no&#125;&#125; &#123;&#123;created_at&#125;&#125;
&#123;&#123;items&#125;&#125;多商品明细 &#123;&#123;product_title&#125;&#125;
&#123;&#123;quantity&#125;&#125; &#123;&#123;amount&#125;&#125; &#123;&#123;buyer_name&#125;&#125;
&#123;&#123;buyer_phone&#125;&#125; &#123;&#123;unit&#125;&#125;
&#123;&#123;quantity&#125;&#125; &#123;&#123;amount&#125;&#125;
&#123;&#123;buyer_name&#125;&#125; &#123;&#123;buyer_phone&#125;&#125;
&#123;&#123;unit&#125;&#125;
</div>
</el-form-item>
</el-form>
@@ -128,9 +128,7 @@ function canComplete(row: MohongOrder) {
}
function canCancel(row: MohongOrder) {
return (
row.status === 'pending_payment' || row.status === 'paid' || row.status === 'receiving'
)
return row.status === 'pending_payment' || row.status === 'paid' || row.status === 'receiving'
}
function canRefund(row: MohongOrder) {
@@ -158,7 +156,13 @@ async function copyText(text: string) {
</div>
<div class="filters">
<el-input v-model="keyword" clearable placeholder="订单号" style="width: 220px" @keyup.enter="load" />
<el-input
v-model="keyword"
clearable
placeholder="订单号"
style="width: 220px"
@keyup.enter="load"
/>
<el-select v-model="status" clearable placeholder="状态" style="width: 140px">
<el-option label="待支付" value="pending_payment" />
<el-option label="已支付" value="paid" />
@@ -167,7 +171,7 @@ async function copyText(text: string) {
<el-option label="已取消" value="cancelled" />
<el-option label="已退款" value="refunded" />
</el-select>
<el-button type="primary" @click=";(page = 1), load()">查询</el-button>
<el-button type="primary" @click=";((page = 1), load())">查询</el-button>
</div>
<el-table v-loading="loading" :data="items" border stripe>
@@ -262,7 +266,11 @@ async function copyText(text: string) {
</div>
<div v-if="detail.qrcode_url_snapshot" class="qr-box">
<strong>二维码快照</strong>
<el-image :src="detail.qrcode_url_snapshot" fit="contain" style="width: 180px; height: 180px" />
<el-image
:src="detail.qrcode_url_snapshot"
fit="contain"
style="width: 180px; height: 180px"
/>
</div>
</template>
</el-drawer>
@@ -207,7 +207,13 @@ function removeGallery(url: string) {
</div>
<div class="filters">
<el-input v-model="keyword" clearable placeholder="搜索标题" style="width: 220px" @keyup.enter="load" />
<el-input
v-model="keyword"
clearable
placeholder="搜索标题"
style="width: 220px"
@keyup.enter="load"
/>
<el-select v-model="categoryId" clearable placeholder="分类" style="width: 140px">
<el-option v-for="c in categories" :key="c.id" :label="c.name" :value="c.id" />
</el-select>
@@ -216,7 +222,7 @@ function removeGallery(url: string) {
<el-option label="上架中" value="on_sale" />
<el-option label="已下架" value="off_sale" />
</el-select>
<el-button type="primary" @click=";(page = 1), load()">查询</el-button>
<el-button type="primary" @click=";((page = 1), load())">查询</el-button>
</div>
<el-table v-loading="loading" :data="items" border stripe>
@@ -273,7 +279,12 @@ function removeGallery(url: string) {
>
<el-form label-width="100px">
<el-form-item label="分类">
<el-select v-model="form.category_id" clearable placeholder="选择分类" style="width: 100%">
<el-select
v-model="form.category_id"
clearable
placeholder="选择分类"
style="width: 100%"
>
<el-option v-for="c in categories" :key="c.id" :label="c.name" :value="c.id" />
</el-select>
</el-form-item>
@@ -288,7 +299,11 @@ function removeGallery(url: string) {
style="width: 80px; height: 80px; border-radius: 8px"
fit="cover"
/>
<el-upload :show-file-list="false" accept="image/*" :before-upload="(f: File) => uploadImage(f, 'cover')">
<el-upload
:show-file-list="false"
accept="image/*"
:before-upload="(f: File) => uploadImage(f, 'cover')"
>
<el-button :icon="Upload">上传封面</el-button>
</el-upload>
</div>
@@ -299,7 +314,11 @@ function removeGallery(url: string) {
<el-image :src="url" fit="cover" />
<button type="button" @click="removeGallery(url)">×</button>
</div>
<el-upload :show-file-list="false" accept="image/*" :before-upload="(f: File) => uploadImage(f, 'gallery')">
<el-upload
:show-file-list="false"
accept="image/*"
:before-upload="(f: File) => uploadImage(f, 'gallery')"
>
<el-button :icon="Upload">添加图片</el-button>
</el-upload>
</div>
@@ -331,7 +350,11 @@ function removeGallery(url: string) {
</el-select>
</el-form-item>
<el-form-item label="专属二维码">
<el-switch v-model="form.use_custom_qrcode" active-text="覆盖默认" inactive-text="用默认" />
<el-switch
v-model="form.use_custom_qrcode"
active-text="覆盖默认"
inactive-text="用默认"
/>
<div v-if="form.use_custom_qrcode" class="upload-row" style="margin-top: 8px">
<el-image
v-if="form.qrcode_image_url"
@@ -234,10 +234,20 @@ function statusType(status: string) {
</el-select>
</el-form-item>
<el-form-item label="开始日期">
<el-date-picker v-model="filters.start_date" class="full-control" type="date" value-format="YYYY-MM-DD" />
<el-date-picker
v-model="filters.start_date"
class="full-control"
type="date"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker v-model="filters.end_date" class="full-control" type="date" value-format="YYYY-MM-DD" />
<el-date-picker
v-model="filters.end_date"
class="full-control"
type="date"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="filters.status" clearable placeholder="全部状态" class="full-control">
@@ -249,7 +259,12 @@ function statusType(status: string) {
<el-input v-model="filters.category" clearable placeholder="按类别精确筛选" />
</el-form-item>
<el-form-item label="关键词">
<el-input v-model="filters.keyword" clearable placeholder="单号、收款方或用途" @keyup.enter="search" />
<el-input
v-model="filters.keyword"
clearable
placeholder="单号、收款方或用途"
@keyup.enter="search"
/>
</el-form-item>
</el-form>
@@ -264,13 +279,17 @@ function statusType(status: string) {
</template>
</el-table-column>
<el-table-column label="开支金额" min-width="135" align="right">
<template #default="{ row }"><strong>{{ money(row.amount_cent) }}</strong></template>
<template #default="{ row }"
><strong>{{ money(row.amount_cent) }}</strong></template
>
</el-table-column>
<el-table-column label="开支时间" min-width="175">
<template #default="{ row }">{{ formatDateTime(row.occurred_at) }}</template>
</el-table-column>
<el-table-column label="状态" width="105" align="center">
<template #default="{ row }"><el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag></template>
<template #default="{ row }"
><el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag></template
>
</el-table-column>
<el-table-column label="录入人" min-width="120">
<template #default="{ row }">{{ row.created_by_name || '-' }}</template>
@@ -280,7 +299,9 @@ function statusType(status: string) {
</el-table-column>
<el-table-column label="操作" width="145" fixed="right">
<template #default="{ row }">
<el-button link type="primary" :icon="View" @click="selectedExpense = row">详情</el-button>
<el-button link type="primary" :icon="View" @click="selectedExpense = row"
>详情</el-button
>
<el-button
v-if="row.status === 'paid' && canManage"
link
@@ -304,60 +325,148 @@ function statusType(status: string) {
@page-change="loadItems"
/>
<el-dialog v-model="createVisible" title="新增运营开支" width="560px" :close-on-click-modal="false">
<el-dialog
v-model="createVisible"
title="新增运营开支"
width="560px"
:close-on-click-modal="false"
>
<el-form class="expense-form" label-position="top" @submit.prevent="submitExpense">
<el-form-item label="开支类别" required>
<el-input v-model="createForm.category" maxlength="50" placeholder="例如:推广投放、办公采购" />
<el-input
v-model="createForm.category"
maxlength="50"
placeholder="例如:推广投放、办公采购"
/>
</el-form-item>
<el-form-item label="收款方" required>
<el-input v-model="createForm.payee_name" maxlength="100" placeholder="姓名或单位名称" />
</el-form-item>
<el-form-item label="开支金额" required>
<el-input-number v-model="createForm.amount_yuan" class="full-control" :min="0.01" :precision="2" :step="100" controls-position="right" />
<el-input-number
v-model="createForm.amount_yuan"
class="full-control"
:min="0.01"
:precision="2"
:step="100"
controls-position="right"
/>
</el-form-item>
<el-form-item label="开支时间" required>
<el-date-picker v-model="createForm.occurred_at" class="full-control" type="datetime" placeholder="选择开支时间" />
<el-date-picker
v-model="createForm.occurred_at"
class="full-control"
type="datetime"
placeholder="选择开支时间"
/>
</el-form-item>
<el-form-item class="expense-form-wide" label="开支用途" required>
<el-input v-model="createForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="填写开支用途" />
<el-input
v-model="createForm.remark"
type="textarea"
:rows="3"
maxlength="500"
show-word-limit
placeholder="填写开支用途"
/>
</el-form-item>
<el-form-item class="expense-form-wide" label="开支凭证(选填)">
<div v-if="createForm.voucher_url" class="voucher-preview">
<AuthImage :source="createForm.voucher_url" :admin="true" fit="cover" :preview-src-list="[createForm.voucher_url]" :image-style="{ width: '104px', height: '104px', borderRadius: '6px' }" />
<el-button class="voucher-remove" circle plain type="danger" :icon="CircleClose" title="移除凭证" @click="createForm.voucher_url = ''" />
<AuthImage
:source="createForm.voucher_url"
:admin="true"
fit="cover"
:preview-src-list="[createForm.voucher_url]"
:image-style="{ width: '104px', height: '104px', borderRadius: '6px' }"
/>
<el-button
class="voucher-remove"
circle
plain
type="danger"
:icon="CircleClose"
title="移除凭证"
@click="createForm.voucher_url = ''"
/>
</div>
<el-upload v-else :show-file-list="false" :http-request="uploadVoucher" accept="image/jpeg,image/png,image/webp">
<el-upload
v-else
:show-file-list="false"
:http-request="uploadVoucher"
accept="image/jpeg,image/png,image/webp"
>
<el-button :icon="UploadFilled" :loading="voucherUploading">上传凭证</el-button>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="createSaving" @click="createVisible = false">取消</el-button>
<el-button type="primary" :loading="createSaving" :disabled="voucherUploading" @click="submitExpense">确认记账</el-button>
<el-button
type="primary"
:loading="createSaving"
:disabled="voucherUploading"
@click="submitExpense"
>确认记账</el-button
>
</template>
</el-dialog>
<el-dialog :model-value="!!selectedExpense" title="运营开支详情" width="620px" @update:model-value="selectedExpense = null">
<el-dialog
:model-value="!!selectedExpense"
title="运营开支详情"
width="620px"
@update:model-value="selectedExpense = null"
>
<el-descriptions v-if="selectedExpense" :column="2" border>
<el-descriptions-item label="开支单号">{{ selectedExpense.expense_no }}</el-descriptions-item>
<el-descriptions-item label="状态"><el-tag :type="statusType(selectedExpense.status)">{{ statusLabel(selectedExpense.status) }}</el-tag></el-descriptions-item>
<el-descriptions-item label="开支单号">{{
selectedExpense.expense_no
}}</el-descriptions-item>
<el-descriptions-item label="状态"
><el-tag :type="statusType(selectedExpense.status)">{{
statusLabel(selectedExpense.status)
}}</el-tag></el-descriptions-item
>
<el-descriptions-item label="开支类别">{{ selectedExpense.category }}</el-descriptions-item>
<el-descriptions-item label="收款方">{{ selectedExpense.payee_name }}</el-descriptions-item>
<el-descriptions-item label="开支金额"><strong>{{ money(selectedExpense.amount_cent) }}</strong></el-descriptions-item>
<el-descriptions-item label="开支时间">{{ formatDateTime(selectedExpense.occurred_at) }}</el-descriptions-item>
<el-descriptions-item label="录入人">{{ selectedExpense.created_by_name || '-' }}</el-descriptions-item>
<el-descriptions-item label="录入时间">{{ formatDateTime(selectedExpense.created_at) }}</el-descriptions-item>
<el-descriptions-item label="开支用途" :span="2">{{ selectedExpense.remark }}</el-descriptions-item>
<el-descriptions-item label="开支金额"
><strong>{{ money(selectedExpense.amount_cent) }}</strong></el-descriptions-item
>
<el-descriptions-item label="开支时间">{{
formatDateTime(selectedExpense.occurred_at)
}}</el-descriptions-item>
<el-descriptions-item label="录入人">{{
selectedExpense.created_by_name || '-'
}}</el-descriptions-item>
<el-descriptions-item label="录入时间">{{
formatDateTime(selectedExpense.created_at)
}}</el-descriptions-item>
<el-descriptions-item label="开支用途" :span="2">{{
selectedExpense.remark
}}</el-descriptions-item>
<el-descriptions-item v-if="selectedExpense.voucher_url" label="开支凭证" :span="2">
<AuthImage :source="selectedExpense.voucher_url" :admin="true" fit="cover" :preview-src-list="[selectedExpense.voucher_url]" :image-style="{ width: '140px', height: '140px', borderRadius: '6px' }" />
<AuthImage
:source="selectedExpense.voucher_url"
:admin="true"
fit="cover"
:preview-src-list="[selectedExpense.voucher_url]"
:image-style="{ width: '140px', height: '140px', borderRadius: '6px' }"
/>
</el-descriptions-item>
<el-descriptions-item v-if="selectedExpense.status === 'voided'" label="作废信息" :span="2">
{{ selectedExpense.voided_by_name || '-' }} · {{ formatDateTime(selectedExpense.voided_at) }} · {{ selectedExpense.void_reason || '-' }}
{{ selectedExpense.voided_by_name || '-' }} ·
{{ formatDateTime(selectedExpense.voided_at) }} · {{ selectedExpense.void_reason || '-' }}
</el-descriptions-item>
</el-descriptions>
<template #footer>
<el-button @click="selectedExpense = null">关闭</el-button>
<el-button v-if="selectedExpense?.status === 'paid' && canManage" type="danger" :icon="CircleClose" :loading="actionID === selectedExpense.id" @click="handleVoid(selectedExpense)">作废记录</el-button>
<el-button
v-if="selectedExpense?.status === 'paid' && canManage"
type="danger"
:icon="CircleClose"
:loading="actionID === selectedExpense.id"
@click="handleVoid(selectedExpense)"
>作废记录</el-button
>
</template>
</el-dialog>
</section>
@@ -46,7 +46,10 @@ function updateFilter<K extends keyof HomeFilters>(key: K, value: HomeFilters[K]
emit('update:filters', { [key]: value } as Partial<HomeFilters>)
}
function toggleChip(key: 'insurance' | 'stamina' | 'load' | 'loginMethod' | 'region' | 'rank', value: string) {
function toggleChip(
key: 'insurance' | 'stamina' | 'load' | 'loginMethod' | 'region' | 'rank',
value: string
) {
const current = props.filters[key]
const next = current.includes(value)
? current.filter(item => item !== value)
@@ -56,9 +59,7 @@ function toggleChip(key: 'insurance' | 'stamina' | 'load' | 'loginMethod' | 'reg
function toggleSkin(name: string) {
const current = props.filters.skinNames
const next = current.includes(name)
? current.filter(item => item !== name)
: [...current, name]
const next = current.includes(name) ? current.filter(item => item !== name) : [...current, name]
updateFilter('skinNames', next)
}
@@ -121,9 +122,7 @@ function toggleAdvanced() {
}
/** 刀皮/红皮作为独立左列字段展示(与全宽皮肤区区分) */
const meleeSkinGroup = computed(() =>
props.skinFilterGroups.find(group => group.key === 'melee')
)
const meleeSkinGroup = computed(() => props.skinFilterGroups.find(group => group.key === 'melee'))
const redSkinGroup = computed(() =>
props.skinFilterGroups.find(group => group.key === 'operatorRed')
)
+7 -2
View File
@@ -240,8 +240,13 @@ export async function fetchAdminMohongProduct(id: number | string) {
return data.data
}
export async function createAdminMohongProduct(payload: Partial<MohongProduct> & { title: string; price_cent: number }) {
const { data } = await apiClient.post<ApiResponse<MohongProduct>>('/admin/crash/products', payload)
export async function createAdminMohongProduct(
payload: Partial<MohongProduct> & { title: string; price_cent: number }
) {
const { data } = await apiClient.post<ApiResponse<MohongProduct>>(
'/admin/crash/products',
payload
)
return data.data
}
@@ -87,13 +87,7 @@ async function handleCheckout() {
</script>
<template>
<el-drawer
v-if="!mobile"
v-model="drawerOpen"
title="购物车"
size="420px"
append-to-body
>
<el-drawer v-if="!mobile" v-model="drawerOpen" title="购物车" size="420px" append-to-body>
<div v-if="isEmpty" class="empty">
<el-icon :size="36"><ShoppingCart /></el-icon>
<p>还没有选购商品</p>
@@ -115,7 +109,9 @@ async function handleCheckout() {
:min="1"
:max="line.stock > 0 ? line.stock : 99"
size="small"
@update:model-value="(v: number | undefined) => setQuantity(line.product_id, Number(v || 1))"
@update:model-value="
(v: number | undefined) => setQuantity(line.product_id, Number(v || 1))
"
/>
<el-button text type="danger" :icon="Delete" @click="removeLine(line.product_id)" />
</div>
@@ -206,12 +206,7 @@ async function handleSupportClick() {
</button>
<div class="search-wrap">
<van-icon name="search" :size="16" />
<input
v-model="keyword"
type="search"
placeholder="搜索商品"
@input="onSearchInput"
/>
<input v-model="keyword" type="search" placeholder="搜索商品" @input="onSearchInput" />
</div>
<button type="button" class="orders-btn" @click="openCart">
购物车{{ cartCount > 0 ? `(${cartCount})` : '' }}
@@ -250,7 +245,9 @@ async function handleSupportClick() {
</aside>
<section ref="listPanel" class="list-panel">
<van-loading v-if="loading" class="state-loading" size="24px" vertical>加载中...</van-loading>
<van-loading v-if="loading" class="state-loading" size="24px" vertical
>加载中...</van-loading
>
<van-empty v-else-if="products.length === 0" description="暂无商品" />
<div v-else class="product-list">
<button
@@ -37,9 +37,7 @@ const {
paidMessage: '支付成功',
})
const statusText = computed(() =>
order.value ? mohongOrderStatusLabel(order.value.status) : ''
)
const statusText = computed(() => (order.value ? mohongOrderStatusLabel(order.value.status) : ''))
onMounted(loadOrder)
@@ -101,7 +99,6 @@ async function copyText() {
showToast({ message: '复制失败', icon: 'cross' })
}
}
</script>
<template>
@@ -140,9 +137,7 @@ async function copyText() {
<h3>商品明细</h3>
<div v-for="item in order.items" :key="item.product_id" class="item-row">
<span>{{ item.title }}</span>
<em
>×{{ item.quantity }} · ¥{{ item.amount || formatCent(item.amount_cent) }}</em
>
<em>×{{ item.quantity }} · ¥{{ item.amount || formatCent(item.amount_cent) }}</em>
</div>
</section>
@@ -531,8 +531,6 @@ async function handleSupportClick() {
font-size: 12px;
}
@media (max-width: 900px) {
.shop-layout {
grid-template-columns: 1fr;
@@ -36,9 +36,7 @@ const cashier = useOrderPaymentCashier({
queryPayment: queryMohongPayment,
})
const statusText = computed(() =>
order.value ? mohongOrderStatusLabel(order.value.status) : ''
)
const statusText = computed(() => (order.value ? mohongOrderStatusLabel(order.value.status) : ''))
const activePayWayLabel = computed(() => {
const map: Record<string, string> = { WXZF: '微信', ZFBZF: '支付宝' }
@@ -123,7 +121,6 @@ async function copyText() {
ElMessage.error('复制失败')
}
}
</script>
<template>
@@ -207,9 +204,7 @@ async function copyText() {
<button v-if="order.copy_text" type="button" class="btn outline" @click="copyText">
复制订单信息
</button>
<button type="button" class="btn outline" @click="router.push('/crash')">
继续选购
</button>
<button type="button" class="btn outline" @click="router.push('/crash')">继续选购</button>
</aside>
</div>
</template>
@@ -216,7 +216,11 @@ const hasContent = computed(() => Boolean(snapshot.value))
<!-- Mobile默认折叠 -->
<section v-if="props.variant === 'mobile'" class="card-section">
<van-collapse v-model="mobileActive" :border="false">
<van-collapse-item name="snapshot" title="订单账号信息" :label="hasContent ? '下单时账号快照(点击展开)' : '暂无快照'">
<van-collapse-item
name="snapshot"
title="订单账号信息"
:label="hasContent ? '下单时账号快照(点击展开)' : '暂无快照'"
>
<template v-if="hasContent">
<div class="kv-list">
<div v-for="row in primaryRows" :key="row.label" class="kv-row">
@@ -366,9 +366,7 @@ function bizTypeLabel(bizType: BizType) {
</div>
<div class="crash-product-info">
<strong>{{ row.title }}</strong>
<span
>x{{ row.quantity || 1 }}{{ row.unit ? ` · ${row.unit}` : '' }}</span
>
<span>x{{ row.quantity || 1 }}{{ row.unit ? ` · ${row.unit}` : '' }}</span>
</div>
</div>
<div v-else class="rental-product-cell">
@@ -403,8 +401,12 @@ function bizTypeLabel(bizType: BizType) {
<el-table-column label="押金 / 数量" width="110">
<template #default="{ row }">
<div v-if="row.bizType === 'rental' && row.rental" class="amount-cell">
<span class="amount-value">¥{{ money(amountYuan(row.rental.deposit_amount_cent)) }}</span>
<span v-if="amountYuan(row.rental.deposit_waived_amount_cent) > 0" class="amount-label"
<span class="amount-value"
>¥{{ money(amountYuan(row.rental.deposit_amount_cent)) }}</span
>
<span
v-if="amountYuan(row.rental.deposit_waived_amount_cent) > 0"
class="amount-label"
> ¥{{ money(amountYuan(row.rental.deposit_waived_amount_cent)) }}</span
>
</div>
@@ -451,9 +453,7 @@ function bizTypeLabel(bizType: BizType) {
<div class="action-buttons">
<el-button
v-if="
row.bizType === 'rental' &&
row.rental &&
row.rental.status === 'pending_payment'
row.bizType === 'rental' && row.rental && row.rental.status === 'pending_payment'
"
size="small"
type="primary"
@@ -263,7 +263,9 @@ async function handleOffline() {
<h2>{{ getListingTitle(listing) }}</h2>
<p class="sub">{{ getListingSubtitle(listing) }}</p>
<div class="tag-row">
<van-tag plain type="primary" size="medium">{{ getServerRegion(listing) || '—' }}</van-tag>
<van-tag plain type="primary" size="medium">{{
getServerRegion(listing) || '—'
}}</van-tag>
<van-tag v-if="getLoginMethod(listing)" plain type="primary" size="medium">
{{ getLoginMethod(listing) }}
</van-tag>
@@ -360,7 +362,9 @@ async function handleOffline() {
<div class="bottom-spacer" />
<footer class="action-bar">
<van-button v-if="canEdit(listing)" plain round class="btn" @click="goEdit">编辑</van-button>
<van-button v-if="canEdit(listing)" plain round class="btn" @click="goEdit"
>编辑</van-button
>
<van-button
v-if="canSubmit(listing)"
type="primary"
@@ -298,13 +298,7 @@ function isPendingReview(row: Listing) {
<!-- 卡片底操作 -->
<div class="card-footer">
<van-button
size="small"
plain
round
class="action-btn"
@click="goDetail(item)"
>
<van-button size="small" plain round class="action-btn" @click="goDetail(item)">
详情
</van-button>
<van-button
@@ -127,7 +127,10 @@ const accountRows = computed(() => {
{ label: '负重等级', value: readAssetString(listing.value, 'load_level') || '--' },
{ label: '绝密KD', value: secretKD > 0 ? formatAssetNumber(secretKD) : '--' },
{ label: '纯币', value: formatHafCoinM(getCoinWan(listing.value)) },
{ label: '日损耗', value: getDailyLoss(listing.value) ? `${getDailyLoss(listing.value)}/天` : '--' },
{
label: '日损耗',
value: getDailyLoss(listing.value) ? `${getDailyLoss(listing.value)}/天` : '--',
},
{ label: 'M单价', value: formatRatio(listing.value) },
{ label: '方便上号', value: getOnlineTimeText(listing.value) || '--' },
{ label: '预计可租', value: formatEstimatedRentalDuration(listing.value) },
@@ -207,19 +210,37 @@ async function handleOffline() {
<section v-loading="loading" class="seller-detail-page">
<header class="page-head">
<div>
<el-button text :icon="ArrowLeft" @click="router.push('/seller/listings')">返回列表</el-button>
<el-button text :icon="ArrowLeft" @click="router.push('/seller/listings')"
>返回列表</el-button
>
<h1>我的商品详情</h1>
<p>仅本人可查看完整发布信息他人无法通过此入口访问</p>
</div>
<div v-if="listing" class="head-actions">
<el-button :icon="CopyDocument" @click="copyCode">复制编号</el-button>
<el-button v-if="canEdit(listing)" :icon="Edit" @click="router.push(`/seller/listings/${listing.id}/edit`)">
<el-button
v-if="canEdit(listing)"
:icon="Edit"
@click="router.push(`/seller/listings/${listing.id}/edit`)"
>
编辑
</el-button>
<el-button v-if="canSubmit(listing)" type="primary" plain :loading="acting" @click="handleSubmit">
<el-button
v-if="canSubmit(listing)"
type="primary"
plain
:loading="acting"
@click="handleSubmit"
>
提审
</el-button>
<el-button v-if="canOffline(listing)" type="danger" plain :loading="acting" @click="handleOffline">
<el-button
v-if="canOffline(listing)"
type="danger"
plain
:loading="acting"
@click="handleOffline"
>
下架
</el-button>
</div>
+4 -2
View File
@@ -160,11 +160,13 @@ export function handoffStatusLabel(status: string) {
return readLabel(handoffStatusMap, status)
}
export function orderHandoffStatusLabel(order?: {
export function orderHandoffStatusLabel(
order?: {
handoff_status?: string
handoff_mode?: string
settlement_mode?: string
} | null) {
} | null
) {
const status = order?.handoff_status || ''
if (order?.handoff_mode !== 'platform' && order?.settlement_mode !== 'platform_managed') {
return handoffStatusLabel(status)
+4 -1
View File
@@ -104,7 +104,10 @@
font-weight: 600;
line-height: 1.2;
cursor: pointer;
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
transition:
background 0.15s,
color 0.15s,
box-shadow 0.15s;
}
.pill-chip:hover {