admin_new/src/modules/other/groupbuy/GroupbuyListView.vue

147 lines
6.8 KiB
Vue

<script setup lang="ts">
/**
* F006 拼团活动 - W10 A3 标准化
*
* W10 A3: 套 PageHeader/SearchBar 标准化模板
*
* 沿用老 vue other/groupbuy.vue 116 行:
* - 搜索: 活动名称
* - 表格列: name/enterpriseName/groupBeginTime/groupEndTime/type/createDate (+ status 1启用2停用)
* - 操作: 查看 / 启用停用 / 删除
* - 批量删除
* - /other/groupbuy 后端 404 → FALLBACK mock
*/
import { ref, onMounted, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { httpGet, httpPost } from '@/utils/request'
import PageHeader from '@/components/PageHeader/PageHeader.vue'
import SearchBar from '@/components/SearchBar/SearchBar.vue'
interface Groupbuy {
id: string
name: string
enterpriseName: string
groupBeginTime: string
groupEndTime: string
type: string
status: number
createDate: string
raw: any
}
const STATUS_MAP: Record<number, { label: string; type: string }> = {
1: { label: '启用', type: 'success' },
2: { label: '停用', type: 'danger' }
}
const FALLBACK: Groupbuy[] = [
{ id: 'G001', name: '7月钢材拼团 - 三人成团', enterpriseName: '深圳科技股份有限公司', groupBeginTime: '2026-07-01 10:00:00', groupEndTime: '2026-07-15 18:00:00', type: '螺纹钢/盘螺/线材', status: 1, createDate: '2026-06-28 14:20:00', raw: {} },
{ id: 'G002', name: '水泥团购专场', enterpriseName: '上海制造企业', groupBeginTime: '2026-07-05 09:00:00', groupEndTime: '2026-07-20 18:00:00', type: '普通硅酸盐水泥 P.O 42.5', status: 1, createDate: '2026-06-30 11:15:00', raw: {} },
{ id: 'G003', name: '夏季建材促销', enterpriseName: '北京电子有限公司', groupBeginTime: '2026-06-15 00:00:00', groupEndTime: '2026-06-30 23:59:59', type: '砂石/水泥/砖块', status: 2, createDate: '2026-06-10 16:30:00', raw: {} },
{ id: 'G004', name: '装修材料拼团', enterpriseName: '广州工厂', groupBeginTime: '2026-07-10 10:00:00', groupEndTime: '2026-07-25 18:00:00', type: '板材/五金/涂料', status: 1, createDate: '2026-07-02 09:00:00', raw: {} },
{ id: 'G005', name: '企业大批量采购 - 钢材', enterpriseName: '杭州电子', groupBeginTime: '2026-08-01 10:00:00', groupEndTime: '2026-08-31 18:00:00', type: '热轧钢板', status: 1, createDate: '2026-07-03 14:45:00', raw: {} }
]
const list = ref<Groupbuy[]>([])
const total = ref(0)
const loading = ref(false)
const error = ref<string | null>(null)
const page = ref(1)
const limit = ref(10)
const filters = reactive({ name: '' })
const selection = ref<Groupbuy[]>([])
const viewVisible = ref(false)
const viewData = ref<Groupbuy | null>(null)
async function load() {
loading.value = true
error.value = null
try {
await new Promise(r => setTimeout(r, 200))
let rows = [...FALLBACK]
if (filters.name) rows = rows.filter(r => r.name.includes(filters.name))
list.value = rows
total.value = rows.length
} catch (e: any) {
error.value = e?.msg || e?.message || '加载失败'
list.value = FALLBACK
total.value = FALLBACK.length
} finally { loading.value = false }
}
function onSearch() { page.value = 1; load() }
function onReset() { filters.name = ''; load() }
function onPageChange(p: number) { page.value = p; load() }
function onLimitChange(l: number) { limit.value = l; load() }
function onSelectionChange(rows: Groupbuy[]) { selection.value = rows }
function onView(row: Groupbuy) { viewData.value = row; viewVisible.value = true }
function onToggleStatus(row: Groupbuy) {
const newStatus = row.status === 1 ? 2 : 1
ElMessageBox.confirm(`确认${newStatus === 1 ? '启用' : '停用'}「${row.name}」?`, '提示', { type: 'warning' }).then(async () => {
try { await httpPost('/other/groupbuy/update', { id: row.id, status: newStatus }); ElMessage.success(`${newStatus === 1 ? '启用' : '停用'}成功`); load() }
catch (e: any) { ElMessage.error(e?.msg || '操作失败') }
}).catch(() => {})
}
function onDelete(row: Groupbuy) {
ElMessageBox.confirm(`确认删除「${row.name}」?`, '提示', { type: 'warning' }).then(async () => {
try { await httpPost('/other/groupbuy/delete', { id: row.id }); ElMessage.success('已删除'); load() }
catch (e: any) { ElMessage.error(e?.msg || '删除失败') }
}).catch(() => {})
}
function onBatchDelete() {
if (selection.value.length === 0) { ElMessage.warning('请先选择要删除的活动'); return }
ElMessageBox.confirm(`确认批量删除选中的 ${selection.value.length} 个活动?`, '提示', { type: 'warning' }).then(async () => {
try { await httpPost('/other/groupbuy/delete', selection.value.map(s => s.id)); ElMessage.success(`已批量删除 ${selection.value.length} 个`); load() }
catch (e: any) { ElMessage.error(e?.msg || '批量删除失败') }
}).catch(() => {})
}
onMounted(load)
</script>
<template>
<div class="rr-page">
<PageHeader title="拼团活动" :sub="`拼团/团购活动管理 (共 ${total} 个)`">
<el-button type="danger" :disabled="selection.length === 0" @click="onBatchDelete">批量删除 ({{ selection.length }})</el-button>
</PageHeader>
<SearchBar
v-model:filters="filters"
:fields="[
{ key: 'name', label: '活动名称', type: 'text', placeholder: '请输入活动名称' }
]"
@search="onSearch"
@reset="onReset"
/>
<div class="rr-card">
<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>
<el-dialog v-model="viewVisible" title="活动详情" width="640px">
<el-descriptions v-if="viewData" :column="2" border>
<el-descriptions-item label="活动名称">{{ viewData.name }}</el-descriptions-item>
<el-descriptions-item label="活动商家">{{ viewData.enterpriseName }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ viewData.groupBeginTime }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ viewData.groupEndTime }}</el-descriptions-item>
<el-descriptions-item label="活动商品" :span="2">{{ viewData.type }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="(STATUS_MAP[viewData.status]?.type as any)" size="small">{{ STATUS_MAP[viewData.status]?.label }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ viewData.createDate }}</el-descriptions-item>
</el-descriptions>
<template #footer>
<el-button @click="viewVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.rr-page { max-width: 1400px; }
.rr-pagination { padding: 12px 20px; display: flex; justify-content: flex-end; border-top: 1px solid var(--border); }
</style>