From 9664b3d9545c78fb3fb66d9b848b54e6a88b7e53 Mon Sep 17 00:00:00 2001 From: Sisyphus Date: Tue, 7 Jul 2026 18:27:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor(F006-W17.4):=20=E5=AE=9E=E7=89=A9/?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=95=86=E5=93=81=E9=A1=B5=E5=A5=97=20PageHe?= =?UTF-8?q?ader+SearchBar+ListTabs+DataTable=20=E6=A0=87=E5=87=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户原话: 按照同样的列表页面的UI规范, 调整实物商品/服务商品的页面样式 实物商品 GoodListView (547→564 行): - 顶部 rr-page-header 改用 包导出/新增按钮 - 裸 el-form 搜索改用 - 裸 .rr-tabs-row/.rr-tabs 改用 (4 状态 tab, count pill) - 裸 el-table + el-pagination 改用 (:borderless + :page-sizes) - 操作列 5 个 el-button 改用 el-link + 分隔符, 不再换行 - 新增 checkStats reactive (pending/approved/rejected) 跟 tab 解耦 - 新增 loadStats() 并发拉 3 个状态的 total, stats 与 tab 解耦 - tabItems 全部 = pending+approved+rejected (跨 tab 一致) - onSearch/onReset/toggleEnabled 并发刷 list + stats 服务商品 ServiceGoodListView (308→352 行): - 已经有 PageHeader/SearchBar, 补 ListTabs + DataTable + el-link 操作列 - 同样的 checkStats + loadStats (跨 tab 一致) - 表格所有列改为 slot 透传 - 价格列 / 铺货 / 状态 都有 slot 保留业务独有: - 详情弹窗 (含 SKU 子表 for 实物商品) - 审核弹窗 (单个/批量) - 修改分类弹窗 (单个/批量) - 批量操作栏 (业务独有, 业务 page 才用) - el-table selection 列 (DataTable 内置支持) --- src/modules/supplier/good/GoodListView.vue | 192 ++++++++++++------ .../servicegood/ServiceGoodListView.vue | 180 ++++++++++------ 2 files changed, 251 insertions(+), 121 deletions(-) diff --git a/src/modules/supplier/good/GoodListView.vue b/src/modules/supplier/good/GoodListView.vue index 013926b..f752d4f 100644 --- a/src/modules/supplier/good/GoodListView.vue +++ b/src/modules/supplier/good/GoodListView.vue @@ -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(null) const selection = ref([]) +// 状态统计 (与 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()]))