新增摸大红业务并修复后台链接按钮白字
支持分类筛选、搜索、下单支付建群与固定二维码;合并迁移种子数据;后台表格编辑/详情链接恢复主题色可见。
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Refresh } from '@element-plus/icons-vue'
|
||||
import {
|
||||
cancelAdminMohongOrder,
|
||||
completeAdminMohongOrder,
|
||||
fetchAdminMohongOrders,
|
||||
mohongOrderStatusLabel,
|
||||
type MohongOrder,
|
||||
} from '@/features/mohong/api/mohong'
|
||||
import { formatCent } from '@/shared/utils/money'
|
||||
import { formatDateTime } from '@/shared/utils/time'
|
||||
import { readError } from '@/shared/utils/error'
|
||||
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const items = ref<MohongOrder[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const keyword = ref('')
|
||||
const status = ref('')
|
||||
const detail = ref<MohongOrder | null>(null)
|
||||
|
||||
onMounted(load)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await fetchAdminMohongOrders({
|
||||
keyword: keyword.value || undefined,
|
||||
status: status.value || undefined,
|
||||
page: page.value,
|
||||
page_size: pageSize.value,
|
||||
})
|
||||
items.value = result.items || []
|
||||
total.value = result.total || 0
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '加载失败'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleComplete(row: MohongOrder) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确认将订单 ${row.order_no} 标记为已完成?`, '提示', {
|
||||
type: 'warning',
|
||||
})
|
||||
await completeAdminMohongOrder(row.id)
|
||||
ElMessage.success('已完成')
|
||||
await load()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') ElMessage.error(readError(error, '操作失败'))
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCancel(row: MohongOrder) {
|
||||
try {
|
||||
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消订单', {
|
||||
inputPlaceholder: '可选',
|
||||
confirmButtonText: '确认取消',
|
||||
cancelButtonText: '返回',
|
||||
})
|
||||
await cancelAdminMohongOrder(row.id, value || '后台取消')
|
||||
ElMessage.success('已取消')
|
||||
await load()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') ElMessage.error(readError(error, '操作失败'))
|
||||
}
|
||||
}
|
||||
|
||||
async function copyText(text: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
ElMessage.success('已复制')
|
||||
} catch {
|
||||
ElMessage.error('复制失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="admin-page">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h2>摸大红订单</h2>
|
||||
<p>查看订单、完成履约、复制用户订单信息</p>
|
||||
</div>
|
||||
<el-button :icon="Refresh" @click="load">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<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" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已取消" value="cancelled" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click=";(page = 1), load()">查询</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="items" border stripe>
|
||||
<el-table-column prop="order_no" label="订单号" min-width="170" />
|
||||
<el-table-column prop="product_title" label="商品" min-width="140" />
|
||||
<el-table-column label="数量" width="70" prop="quantity" />
|
||||
<el-table-column label="金额" width="100">
|
||||
<template #default="{ row }">¥{{ row.amount || formatCent(row.amount_cent) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="买家" min-width="140">
|
||||
<template #default="{ row }">
|
||||
{{ row.buyer_nickname || '-' }}
|
||||
<div class="sub">{{ row.buyer_phone || '' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">{{ mohongOrderStatusLabel(row.status) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下单时间" min-width="160">
|
||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="220" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="detail = row">详情</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'paid'"
|
||||
link
|
||||
type="success"
|
||||
@click="handleComplete(row)"
|
||||
>
|
||||
完成
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'pending_payment' || row.status === 'paid'"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleCancel(row)"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AdminTablePagination
|
||||
:current-page="page"
|
||||
:page-size="pageSize"
|
||||
:total="total"
|
||||
@update:current-page="page = $event"
|
||||
@update:page-size="pageSize = $event"
|
||||
@page-change="load"
|
||||
/>
|
||||
|
||||
<el-drawer
|
||||
:model-value="Boolean(detail)"
|
||||
size="420px"
|
||||
title="订单详情"
|
||||
destroy-on-close
|
||||
@update:model-value="(v: boolean) => !v && (detail = null)"
|
||||
>
|
||||
<template v-if="detail">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="订单号">{{ detail.order_no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
{{ mohongOrderStatusLabel(detail.status) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="商品">{{ detail.product_title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="数量">{{ detail.quantity }}</el-descriptions-item>
|
||||
<el-descriptions-item label="金额">
|
||||
¥{{ detail.amount || formatCent(detail.amount_cent) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="买家">
|
||||
{{ detail.buyer_nickname }} {{ detail.buyer_phone }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="会话ID">
|
||||
{{ detail.conversation_id || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-if="detail.copy_text" class="copy-box">
|
||||
<div class="copy-head">
|
||||
<strong>复制文案</strong>
|
||||
<el-button size="small" @click="copyText(detail.copy_text)">复制</el-button>
|
||||
</div>
|
||||
<pre>{{ detail.copy_text }}</pre>
|
||||
</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" />
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.admin-page {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
.page-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.page-head h2 {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
.page-head p,
|
||||
.sub {
|
||||
margin: 0;
|
||||
color: #6b7a90;
|
||||
font-size: 12px;
|
||||
}
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.copy-box,
|
||||
.qr-box {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.copy-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.copy-box pre {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
background: #f7f9fc;
|
||||
border-radius: 8px;
|
||||
white-space: pre-wrap;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.qr-box {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user