优化财务仪表盘UI与快捷时间筛选
This commit is contained in:
@@ -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">
|
||||
可用变量:{{order_no}} {{created_at}}
|
||||
{{items}}(多商品明细) {{product_title}}
|
||||
{{quantity}} {{amount}} {{buyer_name}}
|
||||
{{buyer_phone}} {{unit}}
|
||||
{{quantity}} {{amount}}
|
||||
{{buyer_name}} {{buyer_phone}}
|
||||
{{unit}}
|
||||
</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')
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user