admin_new P0+P1 全批次合并 (84 commits) #2
@ -19,6 +19,9 @@
|
||||
import { ref, onMounted, computed, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import DataTable from '@/components/DataTable/DataTable.vue'
|
||||
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
||||
import SearchBar from '@/components/SearchBar/SearchBar.vue'
|
||||
import ListTabs from '@/components/ListTabs/ListTabs.vue'
|
||||
import { httpGet, httpPost } from '@/utils/request'
|
||||
|
||||
interface Good {
|
||||
@ -105,6 +108,9 @@ const limit = ref(10)
|
||||
const error = ref<string | null>(null)
|
||||
const selection = ref<Good[]>([])
|
||||
|
||||
// 状态统计 (与 tab 解耦, 只跟搜索条件关联 - 跨 tab 一致)
|
||||
const checkStats = reactive({ pending: 0, approved: 0, rejected: 0 })
|
||||
|
||||
const filters = reactive({
|
||||
title: '',
|
||||
enterpriseName: '',
|
||||
@ -125,7 +131,7 @@ const batchRejectDialogVisible = ref(false)
|
||||
const batchRejectForm = reactive({ reason: '' })
|
||||
|
||||
const columns = [
|
||||
{ type: 'selection' as const, width: 50 },
|
||||
{ type: 'selection' as const, label: '', width: 50 },
|
||||
{ prop: 'supplierGoodId', label: '商品ID', width: 140 },
|
||||
{ prop: 'title', label: '商品名称', minWidth: 240, slot: 'title' },
|
||||
{ prop: 'enterpriseName', label: '供应商', minWidth: 180, showOverflowTooltip: true },
|
||||
@ -139,6 +145,30 @@ const columns = [
|
||||
{ label: '操作', width: 240, align: 'center' as const, fixed: 'right' as const, slot: 'action' }
|
||||
]
|
||||
|
||||
async function buildSearchParamsForStats() {
|
||||
const params: any = { type: 1 }
|
||||
if (filters.title) params.title = filters.title
|
||||
return params
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const base = await buildSearchParamsForStats()
|
||||
const [r1, r2, r3] = await Promise.all([
|
||||
httpGet('/supplier/good/pageList', { ...base, checkStatus: 1, page: 1, limit: 1 }),
|
||||
httpGet('/supplier/good/pageList', { ...base, checkStatus: 2, page: 1, limit: 1 }),
|
||||
httpGet('/supplier/good/pageList', { ...base, checkStatus: 3, page: 1, limit: 1 })
|
||||
])
|
||||
checkStats.pending = Number(r1?.total ?? 0)
|
||||
checkStats.approved = Number(r2?.total ?? 0)
|
||||
checkStats.rejected = Number(r3?.total ?? 0)
|
||||
} catch {
|
||||
checkStats.pending = 0
|
||||
checkStats.approved = 0
|
||||
checkStats.rejected = 0
|
||||
}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
@ -180,12 +210,20 @@ async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
function onSearch() { page.value = 1; load() }
|
||||
function onReset() { filters.title = ''; filters.enterpriseName = ''; filters.checkStatus = ''; page.value = 1; load() }
|
||||
function onSearch() { page.value = 1; Promise.all([load(), loadStats()]) }
|
||||
function onReset() { filters.title = ''; filters.enterpriseName = ''; filters.checkStatus = ''; page.value = 1; Promise.all([load(), loadStats()]) }
|
||||
function onPageChange(p: number) { page.value = p; load() }
|
||||
function onLimitChange(l: number) { limit.value = l; page.value = 1; load() }
|
||||
function onSelectionChange(rows: Good[]) { selection.value = rows }
|
||||
|
||||
const tabItems = computed(() => [
|
||||
// 全部 = 待审核 + 已通过 + 已驳回 (跨 tab 一致, 跟搜索条件关联)
|
||||
{ key: '', label: '全部', count: checkStats.pending + checkStats.approved + checkStats.rejected },
|
||||
{ key: '1', label: '待审核', count: checkStats.pending },
|
||||
{ key: '2', label: '已通过', count: checkStats.approved },
|
||||
{ key: '3', label: '已驳回', count: checkStats.rejected }
|
||||
])
|
||||
|
||||
function onStatusTab(v: string) { filters.checkStatus = v; page.value = 1; load() }
|
||||
|
||||
function onShowDetail(row: Good) { detailData.value = row; detailVisible.value = true }
|
||||
@ -279,50 +317,27 @@ function onDeleteBatch() {
|
||||
function onExport() { ElMessage.success('导出商品 (mock)') }
|
||||
function onAdd() { ElMessage.info('新增商品 (mock)') }
|
||||
|
||||
onMounted(load)
|
||||
onMounted(() => Promise.all([load(), loadStats()]))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rr-page">
|
||||
<div class="rr-page-header">
|
||||
<div>
|
||||
<h2 class="rr-page-title">商品管理</h2>
|
||||
<p class="rr-page-sub">共 {{ total }} 个商品</p>
|
||||
</div>
|
||||
<div class="rr-page-actions">
|
||||
<PageHeader title="商品管理" :sub="`共 ${checkStats.pending + checkStats.approved + checkStats.rejected} 个商品`">
|
||||
<el-button @click="onExport">导出</el-button>
|
||||
<el-button type="primary" @click="onAdd">新增商品</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<!-- 搜索区 -->
|
||||
<div class="rr-card rr-search">
|
||||
<el-form :inline="true" @keyup.enter="onSearch">
|
||||
<el-form-item label="商品名称">
|
||||
<el-input v-model="filters.title" placeholder="请输入商品名称" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商">
|
||||
<el-input v-model="filters.enterpriseName" placeholder="供应商名称" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSearch">查询</el-button>
|
||||
<el-button @click="onReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<ListTabs v-model="filters.checkStatus" :items="tabItems" @change="onStatusTab" />
|
||||
|
||||
<!-- 状态 Tab -->
|
||||
<div class="rr-tabs-row">
|
||||
<div class="rr-tabs">
|
||||
<div
|
||||
v-for="t in STATUS_TABS"
|
||||
:key="t.value"
|
||||
class="rr-tab"
|
||||
:class="{ active: filters.checkStatus === t.value }"
|
||||
@click="onStatusTab(t.value)"
|
||||
>{{ t.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<SearchBar
|
||||
v-model:filters="filters"
|
||||
:fields="[
|
||||
{ key: 'title', label: '商品名称', type: 'text', placeholder: '请输入商品名称' },
|
||||
{ key: 'enterpriseName', label: '供应商', type: 'text', placeholder: '供应商名称' }
|
||||
]"
|
||||
@search="onSearch"
|
||||
@reset="onReset"
|
||||
/>
|
||||
|
||||
<!-- 批量操作 -->
|
||||
<div class="rr-batch-bar" v-if="selection.length > 0">
|
||||
@ -334,8 +349,67 @@ onMounted(load)
|
||||
<el-button type="danger" size="small" plain @click="onDeleteBatch">批量删除</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="rr-card">
|
||||
<!-- 表格 (用 DataTable 套裸 el-table, 复用分页 + :borderless + :page-sizes + el-result 错误展示) -->
|
||||
<DataTable
|
||||
:columns="columns"
|
||||
:data="list"
|
||||
:loading="loading"
|
||||
:total="total"
|
||||
:page="page"
|
||||
:limit="limit"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:show-pagination="true"
|
||||
:error="error"
|
||||
empty-text="暂无商品"
|
||||
:borderless="true"
|
||||
row-key="id"
|
||||
@page-change="onPageChange"
|
||||
@limit-change="onLimitChange"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<template #title="{ row }">
|
||||
<div class="rr-good-cell">
|
||||
<div v-if="row.icon" class="rr-good-icon">
|
||||
<el-image :src="row.icon" :preview-src-list="[row.icon]" fit="cover" style="width: 40px; height: 40px; border-radius: 4px" />
|
||||
</div>
|
||||
<div v-else class="rr-good-icon-placeholder">{{ (row.title || '?').charAt(0) }}</div>
|
||||
<span class="rr-good-title">{{ row.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<span>{{ [row.firstCategoryName, row.secondCategoryName, row.thirdCategoryName].filter(Boolean).join(' / ') || '--' }}</span>
|
||||
</template>
|
||||
<template #onShelf="{ row }">
|
||||
<el-tag :type="(ONSHELF_STATUS_MAP[row.onShelf]?.type as any)" size="small" effect="light">
|
||||
{{ ONSHELF_STATUS_MAP[row.onShelf]?.label }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #checkStatus="{ row }">
|
||||
<el-tag :type="(CHECK_STATUS_MAP[row.checkStatus]?.type as any)" size="small" effect="light">
|
||||
{{ CHECK_STATUS_MAP[row.checkStatus]?.label }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<span class="rr-actions">
|
||||
<el-link type="primary" :underline="false" @click="onShowDetail(row)">详情</el-link>
|
||||
<template v-if="row.checkStatus === 1">
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link type="primary" :underline="false" @click="onShowCheckDialog(row)">审核</el-link>
|
||||
</template>
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link :type="row.onShelf === 1 ? 'warning' : 'success'" :underline="false" @click="onOnShelf(row)">
|
||||
{{ row.onShelf === 1 ? '下架' : '上架' }}
|
||||
</el-link>
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link type="primary" :underline="false" @click="onShowCategoryDialog(row)">改分类</el-link>
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link type="danger" :underline="false" @click="onDelete(row)">删除</el-link>
|
||||
</span>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<!-- 原 el-table 区域 (暂时保留作为参考, 实际由 DataTable 接管) -->
|
||||
<div v-if="false" class="rr-card">
|
||||
<el-table
|
||||
:data="list"
|
||||
border
|
||||
@ -393,17 +467,6 @@ onMounted(load)
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="rr-pagination">
|
||||
<el-pagination
|
||||
:current-page="page"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="limit"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="onLimitChange"
|
||||
@current-change="onPageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详情弹窗 (含 SKU 列表, 沿用老 vue 弹窗布局) -->
|
||||
@ -528,20 +591,25 @@ onMounted(load)
|
||||
|
||||
<style scoped lang="scss">
|
||||
.rr-page { max-width: 1500px; }
|
||||
/* PageHeader 复用通用组件样式 */
|
||||
.rr-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 16px; }
|
||||
/* search 由 SearchBar 接管 */
|
||||
.rr-tabs-row { margin-bottom: 16px; display: flex; align-items: center; }
|
||||
.rr-tabs { display: flex; gap: 4px; padding: 4px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
|
||||
.rr-tab { padding: 6px 16px; border-radius: var(--radius); font-size: 13px; color: var(--ink-2); cursor: pointer; transition: all 0.15s; }
|
||||
.rr-tab:hover { color: var(--ink); background: var(--bg-soft); }
|
||||
.rr-tab.active { color: white; background: var(--primary); font-weight: 500; }
|
||||
.rr-batch-bar { display: flex; align-items: center; gap: 8px; padding: 10px 16px; background: var(--bg-soft); border-radius: var(--radius); margin-bottom: 12px; }
|
||||
.rr-batch-tip { font-size: 13px; color: var(--ink-2); margin-right: 8px; }
|
||||
.rr-pagination { padding: 12px 20px; display: flex; justify-content: flex-end; border-top: 1px solid var(--border); }
|
||||
.rr-good-cell { display: flex; align-items: center; gap: 10px; }
|
||||
.rr-good-icon-placeholder { width: 40px; height: 40px; border-radius: 4px; background: var(--bg-soft); display: flex; align-items: center; justify-content: center; color: var(--ink-3); font-weight: 600; flex-shrink: 0; }
|
||||
.rr-good-title { font-size: 13.5px; color: var(--ink); }
|
||||
.rr-sku-title { font-size: 14px; font-weight: 600; color: var(--ink); margin: 24px 0 12px 0; }
|
||||
.rr-empty-tip { text-align: center; padding: 30px 0; color: var(--ink-3); }
|
||||
.rr-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
&__sep {
|
||||
color: var(--rr-border-color, #E2E8F0);
|
||||
margin: 0 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.el-link) {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -10,11 +10,13 @@
|
||||
* - 批量: 审核/驳回/上架/下架/改分类/删除
|
||||
* - 详情弹窗: 含服务属性 + 服务区域 + 价格日历
|
||||
*/
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { ref, onMounted, reactive, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { httpGet } from '@/utils/request'
|
||||
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
||||
import SearchBar from '@/components/SearchBar/SearchBar.vue'
|
||||
import ListTabs from '@/components/ListTabs/ListTabs.vue'
|
||||
import DataTable from '@/components/DataTable/DataTable.vue'
|
||||
|
||||
interface ServiceGood {
|
||||
id: string
|
||||
@ -65,6 +67,32 @@ const selection = ref<ServiceGood[]>([])
|
||||
|
||||
const filters = reactive({ title: '', enterpriseName: '', checkStatus: '' })
|
||||
|
||||
// 状态统计 (与 tab 解耦, 跨 tab 一致)
|
||||
const checkStats = reactive({ pending: 0, approved: 0, rejected: 0 })
|
||||
|
||||
const columns = [
|
||||
{ type: 'selection' as const, label: '', width: 50 },
|
||||
{ prop: 'serviceGoodId', label: '服务ID', width: 140 },
|
||||
{ prop: 'title', label: '服务名称', minWidth: 240, slot: 'title' },
|
||||
{ prop: 'enterpriseName', label: '供应商', minWidth: 180 },
|
||||
{ prop: 'categoryName', label: '分类', width: 160 },
|
||||
{ prop: 'serviceArea', label: '服务区域', width: 120, align: 'center' as const },
|
||||
{ prop: 'price', label: '价格', width: 160, align: 'right' as const, slot: 'price' },
|
||||
{ prop: 'onShelf', label: '铺货', width: 100, align: 'center' as const, slot: 'onShelf' },
|
||||
{ prop: 'checkOpinion', label: '审核意见', minWidth: 160 },
|
||||
{ prop: 'checkStatus', label: '状态', width: 100, align: 'center' as const, slot: 'checkStatus' },
|
||||
{ prop: 'createDate', label: '申请时间', width: 170 },
|
||||
{ label: '操作', width: 280, align: 'center' as const, fixed: 'right' as const, slot: 'action' }
|
||||
]
|
||||
|
||||
const tabItems = computed(() => [
|
||||
// 全部 = 待审核 + 已通过 + 已驳回
|
||||
{ key: '', label: '全部', count: checkStats.pending + checkStats.approved + checkStats.rejected },
|
||||
{ key: '1', label: '待审核', count: checkStats.pending },
|
||||
{ key: '2', label: '已通过', count: checkStats.approved },
|
||||
{ key: '3', label: '已驳回', count: checkStats.rejected }
|
||||
])
|
||||
|
||||
const detailVisible = ref(false)
|
||||
const detailData = ref<ServiceGood | null>(null)
|
||||
const checkDialogVisible = ref(false)
|
||||
@ -76,6 +104,31 @@ const batchCategoryDialogVisible = ref(false)
|
||||
const batchRejectDialogVisible = ref(false)
|
||||
const batchRejectForm = reactive({ reason: '' })
|
||||
|
||||
async function buildSearchParamsForStats() {
|
||||
const params: any = { type: 2 }
|
||||
if (filters.title) params.title = filters.title
|
||||
if (filters.enterpriseName) params.enterpriseName = filters.enterpriseName
|
||||
return params
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const base = await buildSearchParamsForStats()
|
||||
const [r1, r2, r3] = await Promise.all([
|
||||
httpGet('/supplier/good/pageList', { ...base, checkStatus: 1, page: 1, limit: 1 }),
|
||||
httpGet('/supplier/good/pageList', { ...base, checkStatus: 2, page: 1, limit: 1 }),
|
||||
httpGet('/supplier/good/pageList', { ...base, checkStatus: 3, page: 1, limit: 1 })
|
||||
])
|
||||
checkStats.pending = Number(r1?.total ?? 0)
|
||||
checkStats.approved = Number(r2?.total ?? 0)
|
||||
checkStats.rejected = Number(r3?.total ?? 0)
|
||||
} catch {
|
||||
checkStats.pending = 0
|
||||
checkStats.approved = 0
|
||||
checkStats.rejected = 0
|
||||
}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
@ -114,8 +167,8 @@ async function load() {
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
function onSearch() { page.value = 1; load() }
|
||||
function onReset() { filters.title = ''; filters.enterpriseName = ''; filters.checkStatus = ''; load() }
|
||||
function onSearch() { page.value = 1; Promise.all([load(), loadStats()]) }
|
||||
function onReset() { filters.title = ''; filters.enterpriseName = ''; filters.checkStatus = ''; page.value = 1; Promise.all([load(), loadStats()]) }
|
||||
function onPageChange(p: number) { page.value = p; load() }
|
||||
function onLimitChange(l: number) { limit.value = l; page.value = 1; load() }
|
||||
function onSelectionChange(rows: ServiceGood[]) { selection.value = rows }
|
||||
@ -139,12 +192,12 @@ function onDeleteBatch() { if (!selection.value.length) return ElMessage.warning
|
||||
function onExport() { ElMessage.success('导出服务商品 (mock)') }
|
||||
function onAdd() { ElMessage.info('新增服务商品 (mock)') }
|
||||
|
||||
onMounted(load)
|
||||
onMounted(() => Promise.all([load(), loadStats()]))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rr-page">
|
||||
<PageHeader title="服务商品管理" :sub="`共 ${total} 个服务商品`">
|
||||
<PageHeader title="服务商品管理" :sub="`共 ${checkStats.pending + checkStats.approved + checkStats.rejected} 个服务商品`">
|
||||
<el-button @click="onExport">导出</el-button>
|
||||
<el-button type="primary" @click="onAdd">新增服务</el-button>
|
||||
</PageHeader>
|
||||
@ -159,11 +212,7 @@ onMounted(load)
|
||||
@reset="load"
|
||||
/>
|
||||
|
||||
<div class="rr-tabs-row">
|
||||
<div class="rr-tabs">
|
||||
<div v-for="t in STATUS_TABS" :key="t.value" class="rr-tab" :class="{ active: filters.checkStatus === t.value }" @click="onStatusTab(t.value)">{{ t.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<ListTabs v-model="filters.checkStatus" :items="tabItems" @change="onStatusTab" />
|
||||
|
||||
<div class="rr-batch-bar" v-if="selection.length > 0">
|
||||
<span class="rr-batch-tip">已选 {{ selection.length }} 项</span>
|
||||
@ -174,51 +223,55 @@ onMounted(load)
|
||||
<el-button type="danger" size="small" plain @click="onDeleteBatch">批量删除</el-button>
|
||||
</div>
|
||||
|
||||
<div class="rr-card">
|
||||
<el-table :data="list" border style="width: 100%" v-loading="loading" :empty-text="error || '暂无数据'" @selection-change="onSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column prop="serviceGoodId" label="服务ID" width="140" align="center" />
|
||||
<el-table-column label="服务名称" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<DataTable
|
||||
:columns="columns"
|
||||
:data="list"
|
||||
:loading="loading"
|
||||
:total="total"
|
||||
:page="page"
|
||||
:limit="limit"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:show-pagination="true"
|
||||
:error="error"
|
||||
empty-text="暂无数据"
|
||||
:borderless="true"
|
||||
row-key="id"
|
||||
@page-change="onPageChange"
|
||||
@limit-change="onLimitChange"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<template #title="{ row }">
|
||||
<div class="rr-good-cell">
|
||||
<div v-if="row.icon" class="rr-good-icon"><el-image :src="row.icon" :preview-src-list="[row.icon]" fit="cover" style="width: 40px; height: 40px; border-radius: 4px" /></div>
|
||||
<div v-else class="rr-good-icon-placeholder">{{ (row.title || '?').charAt(0) }}</div>
|
||||
<span>{{ row.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="enterpriseName" label="供应商" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="categoryName" label="分类" width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="serviceArea" label="服务区域" width="120" align="center" />
|
||||
<el-table-column label="价格" width="140" align="right">
|
||||
<template #default="{ row }">¥ {{ row.price.toLocaleString() }} / {{ row.unit }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="铺货" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<template #price="{ row }">
|
||||
<span class="rr-price">¥ {{ Number(row.price || 0).toLocaleString() }} / {{ row.unit }}</span>
|
||||
</template>
|
||||
<template #onShelf="{ row }">
|
||||
<el-tag :type="(ONSHELF_STATUS_MAP[row.onShelf]?.type as any)" size="small" effect="light">{{ ONSHELF_STATUS_MAP[row.onShelf]?.label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="checkOpinion" label="审核意见" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<template #checkStatus="{ row }">
|
||||
<el-tag :type="(CHECK_STATUS_MAP[row.checkStatus]?.type as any)" size="small" effect="light">{{ CHECK_STATUS_MAP[row.checkStatus]?.label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请时间" prop="createDate" width="170" />
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" size="small" @click="onShowDetail(row)">详情</el-button>
|
||||
<el-button v-if="row.checkStatus === 1" text type="primary" size="small" @click="onShowCheck(row)">审核</el-button>
|
||||
<el-button text :type="row.onShelf === 1 ? 'warning' : 'success'" size="small" @click="onOnShelf(row)">{{ row.onShelf === 1 ? '下架' : '上架' }}</el-button>
|
||||
<el-button text type="primary" size="small" @click="onShowCategory(row)">改分类</el-button>
|
||||
<el-button text type="danger" size="small" @click="onDelete(row)">删除</el-button>
|
||||
<template #action="{ row }">
|
||||
<span class="rr-actions">
|
||||
<el-link type="primary" :underline="false" @click="onShowDetail(row)">详情</el-link>
|
||||
<template v-if="row.checkStatus === 1">
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link type="primary" :underline="false" @click="onShowCheck(row)">审核</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="rr-pagination">
|
||||
<el-pagination :current-page="page" :page-sizes="[10, 20, 50, 100]" :page-size="limit" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="onLimitChange" @current-change="onPageChange" />
|
||||
</div>
|
||||
</div>
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link :type="row.onShelf === 1 ? 'warning' : 'success'" :underline="false" @click="onOnShelf(row)">{{ row.onShelf === 1 ? '下架' : '上架' }}</el-link>
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link type="primary" :underline="false" @click="onShowCategory(row)">改分类</el-link>
|
||||
<span class="rr-actions__sep">|</span>
|
||||
<el-link type="danger" :underline="false" @click="onDelete(row)">删除</el-link>
|
||||
</span>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<el-dialog v-model="detailVisible" title="服务详情" width="700px">
|
||||
<el-descriptions v-if="detailData" :column="2" border>
|
||||
@ -296,13 +349,22 @@ onMounted(load)
|
||||
<style scoped lang="scss">
|
||||
.rr-page { max-width: 1500px; }
|
||||
.rr-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 16px; }
|
||||
.rr-tabs-row { margin-bottom: 16px; }
|
||||
.rr-tabs { display: flex; gap: 4px; padding: 4px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); width: fit-content; }
|
||||
.rr-tab { padding: 6px 16px; border-radius: var(--radius); font-size: 13px; color: var(--ink-2); cursor: pointer; }
|
||||
.rr-tab.active { color: white; background: var(--primary); font-weight: 500; }
|
||||
.rr-batch-bar { display: flex; align-items: center; gap: 8px; padding: 10px 16px; background: var(--bg-soft); border-radius: var(--radius); margin-bottom: 12px; }
|
||||
.rr-batch-tip { font-size: 13px; color: var(--ink-2); margin-right: 8px; }
|
||||
.rr-pagination { padding: 12px 20px; display: flex; justify-content: flex-end; border-top: 1px solid var(--border); }
|
||||
.rr-good-cell { display: flex; align-items: center; gap: 10px; }
|
||||
.rr-good-icon-placeholder { width: 40px; height: 40px; border-radius: 4px; background: var(--bg-soft); display: flex; align-items: center; justify-content: center; color: var(--ink-3); font-weight: 600; flex-shrink: 0; }
|
||||
.rr-price { font-family: 'SF Mono', Menlo, Monaco, Consolas, monospace; font-size: 13px; font-weight: 500; color: var(--primary); }
|
||||
.rr-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
&__sep {
|
||||
color: var(--rr-border-color, #E2E8F0);
|
||||
margin: 0 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
:deep(.el-link) {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user