- 33 个 ListView.vue 加 :borderless=true + :page-sizes=[10,20,50,100]
覆盖: order/inquiry/finance/operateguide/sys/Dept|Menu|File|Notice|
Params|User|Role|Post|DictType|DictData|Product|Schedule|RoleGroup
user/Enterprise|enterpriselevel|enterprisescore|enterpriserating
supplier/promoter|couponscope|historylog|logistics|ordercomment|Activity
other/goodunit|label|textconfig
api/feedback|help|lunbo
openapi/AuditLog|CompanyAccount
- OrderListView: columns 抽到 script setup (computed) + :borderless +
:page-sizes + sub 动态显示选中数
- router/index.ts: /product/shop → redirect /supplier/good
(删 src/modules/product/shop/ShopListView.vue 旧占位, 后端 404 无意义)
- 沿用 W17 设计规范 v2 (PageHeader+SearchBar+DataTable+ListTabs)
119 lines
6.8 KiB
Vue
119 lines
6.8 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* F006 优惠券适用范围 - W16
|
|
*
|
|
* 沿用老 vue supplier/couponscope.vue 字段
|
|
*
|
|
* 端点: 4 URL 探测 (couponscope/page, /list, /search, /queryPage); 404 → FALLBACK seed
|
|
*/
|
|
import { ref, reactive, onMounted, 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 DataTable from '@/components/DataTable/DataTable.vue'
|
|
|
|
interface CouponScopeRow { id:string; couponId:string; supplierGoodId:string; sourceType:number; createTime:string }
|
|
const ENDPOINTS = [
|
|
'/supplier/couponscope/page',
|
|
'/supplier/couponscope/list',
|
|
'/supplier/couponscope/search',
|
|
'/supplier/couponscope/queryPage'
|
|
]
|
|
const FALLBACK: CouponScopeRow[] = [
|
|
{ id:'CS001', couponId:'C202501', supplierGoodId:'SG101', sourceType:1, createTime:'2026-04-15 09:30:00' },
|
|
{ id:'CS002', couponId:'C202502', supplierGoodId:'SG102', sourceType:1, createTime:'2026-04-22 14:22:00' },
|
|
{ id:'CS003', couponId:'C202503', supplierGoodId:'', sourceType:2, createTime:'2026-05-08 11:08:30' },
|
|
{ id:'CS004', couponId:'C202504', supplierGoodId:'SG104', sourceType:1, createTime:'2026-05-15 16:48:00' },
|
|
{ id:'CS005', couponId:'C202505', supplierGoodId:'', sourceType:2, createTime:'2026-05-22 09:15:00' },
|
|
{ id:'CS006', couponId:'C202506', supplierGoodId:'SG106', sourceType:1, createTime:'2026-06-03 10:42:00' },
|
|
{ id:'CS007', couponId:'C202507', supplierGoodId:'SG107', sourceType:1, createTime:'2026-06-12 13:18:00' },
|
|
{ id:'CS008', couponId:'C202508', supplierGoodId:'', sourceType:2, createTime:'2026-06-20 11:35:00' },
|
|
{ id:'CS009', couponId:'C202509', supplierGoodId:'SG109', sourceType:1, createTime:'2026-06-28 14:55:00' },
|
|
{ id:'CS010', couponId:'C202510', supplierGoodId:'SG110', sourceType:1, createTime:'2026-07-04 09:50:00' },
|
|
{ id:'CS011', couponId:'C202511', supplierGoodId:'', sourceType:2, createTime:'2026-07-08 16:08:00' },
|
|
{ id:'CS012', couponId:'C202512', supplierGoodId:'SG112', sourceType:1, createTime:'2026-07-12 11:42:00' }
|
|
]
|
|
type Col = { type?: 'status'|'text'|'selection'; prop?: string; label: string; width?: number; slot?: string; fixed?: 'right'|true }
|
|
const columns: Col[] = [
|
|
{ type:'selection', label:'', width:50 },
|
|
{ prop:'id', label:'范围ID', width:100, fixed:true },
|
|
{ prop:'couponId', label:'优惠券ID', width:130, slot:'couponId' },
|
|
{ prop:'supplierGoodId', label:'适用商品ID', width:140, slot:'goodId' },
|
|
{ prop:'sourceType', label:'卷来源', width:100, slot:'src' },
|
|
{ prop:'createTime', label:'创建时间', width:160, slot:'time' },
|
|
{ prop:'actions', label:'操作', width:200, slot:'actions', fixed:'right' }
|
|
]
|
|
const STATUS_DEFAULTS = [
|
|
{ value:'', label:'全部' },
|
|
{ value:'1', label:'供应商来源' },
|
|
{ value:'2', label:'平台来源' }
|
|
]
|
|
const list = ref<CouponScopeRow[]>([]); const total = ref(0); const loading = ref(false); const page = ref(1); const limit = ref(10); const filterKw = ref(''); const statusTab = ref('')
|
|
const dataSource = ref<'api'|'mock'|'loading'>('loading'); const apiError = ref<string>('')
|
|
async function load() {
|
|
loading.value = true; dataSource.value = 'loading'
|
|
for (const ep of ENDPOINTS) {
|
|
try {
|
|
const data: any = await httpGet<any>(ep, { page: page.value, limit: limit.value })
|
|
const rows = (data?.list || []).map((it: any) => ({ ...it, raw: it }))
|
|
list.value = rows; total.value = Number(data?.total || rows.length); dataSource.value = 'api'; apiError.value = ''; loading.value = false; return
|
|
} catch {}
|
|
}
|
|
list.value = [...FALLBACK]; total.value = FALLBACK.length; dataSource.value = 'mock'; apiError.value = '后端 API 暂未实装, 已加载前端示例数据'; loading.value = false
|
|
}
|
|
const filtered = computed(() => {
|
|
const kw = filterKw.value.trim().toLowerCase(); let rs = list.value
|
|
if (statusTab.value) rs = rs.filter(r => String(r.sourceType) === statusTab.value)
|
|
if (kw) rs = rs.filter(r => String(r.id).includes(kw) || String(r.couponId).includes(kw) || String(r.supplierGoodId).includes(kw))
|
|
return rs
|
|
})
|
|
const searchFields: Array<{ key: string; label: string; type: 'text' }> = [{ key:'id', label:'范围ID', type:'text' },{ key:'couponId', label:'优惠券ID', type:'text' },{ key:'supplierGoodId', label:'商品ID', type:'text' }]
|
|
const filters = reactive<Record<string, any>>({})
|
|
function onSearch() { filterKw.value = String(filters.id || filters.couponId || filters.supplierGoodId || ''); page.value = 1 }
|
|
function onStatusTab(v: string) { statusTab.value = v; page.value = 1 }
|
|
async function onDelete(row: CouponScopeRow) {
|
|
try { await ElMessageBox.confirm(`确认删除适用范围「${row.id}」?`, '提示', { type: 'warning' }) } catch { return }
|
|
ElMessage.success('已删除 (前端示例)')
|
|
}
|
|
function onView(row: CouponScopeRow) { ElMessage.info(`详情 ${row.id}`) }
|
|
function onExport() { ElMessage.success('已发起导出 (前端示例, 共 ' + filtered.value.length + ' 条)') }
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="rr-page">
|
|
<PageHeader title="优惠券适用范围" sub="优惠券可使用商品范围配置" :data-source="dataSource" :api-error="apiError">
|
|
<el-button type="primary" @click="onExport">导出明细</el-button>
|
|
</PageHeader>
|
|
<el-tabs v-model="statusTab" @tab-change="onStatusTab" class="rr-tabs">
|
|
<el-tab-pane v-for="t in STATUS_DEFAULTS" :key="t.value" :name="t.value" :label="t.label" />
|
|
</el-tabs>
|
|
<SearchBar v-model:filters="filters" :fields="searchFields" @search="onSearch" />
|
|
<DataTable :columns="columns" :data="filtered"
|
|
:borderless="true" :loading="loading" :page="page" :limit="limit" :total="total" row-key="id"
|
|
@page-change="(p: number) => { page = p; load() }" @limit-change="(l: number) => { limit = l; page = 1; load() }">
|
|
<template #couponId="{ row }">
|
|
<el-link v-if="row.couponId" type="primary" :underline="false" @click="onView(row)">{{ row.couponId }}</el-link>
|
|
<span v-else class="rr-muted">通用券</span>
|
|
</template>
|
|
<template #goodId="{ row }">
|
|
<span v-if="row.supplierGoodId">{{ row.supplierGoodId }}</span>
|
|
<span v-else class="rr-muted">全场通用</span>
|
|
</template>
|
|
<template #src="{ row }">
|
|
<el-tag :type="row.sourceType === 1 ? 'primary' : 'success'" size="small">{{ row.sourceType === 1 ? '供应商' : '平台' }}</el-tag>
|
|
</template>
|
|
<template #time="{ row }">{{ row.createTime }}</template>
|
|
<template #actions="{ row }">
|
|
<el-button type="primary" link @click="onView(row)">详情</el-button>
|
|
<el-button type="danger" link @click="onDelete(row)">删除</el-button>
|
|
</template>
|
|
</DataTable>
|
|
</div>
|
|
</template>
|
|
<style scoped>
|
|
.rr-page { padding: 16px; }
|
|
.rr-tabs { margin-bottom: 12px; }
|
|
</style>
|