148 lines
6.9 KiB
Vue
148 lines
6.9 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* F006 秒杀活动 - W10 A3 标准化
|
|
*
|
|
* W10 A3: 套 PageHeader/SearchBar 标准化模板
|
|
*
|
|
* 沿用老 vue other/seckill.vue 113 行:
|
|
* - 搜索: 活动名称
|
|
* - 表格列: name/enterpriseName/beginTime/endTime/type/status(0启用1停用)/createDate
|
|
* - 操作: 查看 / 启用停用 / 删除
|
|
* - 批量删除
|
|
* - /other/seckill 后端 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 Seckill {
|
|
id: string
|
|
name: string
|
|
enterpriseName: string
|
|
beginTime: string
|
|
endTime: string
|
|
type: string
|
|
status: number
|
|
createDate: string
|
|
raw: any
|
|
}
|
|
|
|
const STATUS_MAP: Record<number, { label: string; type: string }> = {
|
|
0: { label: '启用', type: 'success' },
|
|
1: { label: '停用', type: 'danger' }
|
|
}
|
|
|
|
const FALLBACK: Seckill[] = [
|
|
{ id: 'S001', name: '整点秒杀 - 12点档', enterpriseName: '深圳科技股份有限公司', beginTime: '2026-07-10 12:00:00', endTime: '2026-07-10 13:00:00', type: '螺纹钢 100 件', status: 0, createDate: '2026-07-01 10:00:00', raw: {} },
|
|
{ id: 'S002', name: '整点秒杀 - 18点档', enterpriseName: '上海制造企业', beginTime: '2026-07-10 18:00:00', endTime: '2026-07-10 19:00:00', type: '水泥 50 吨', status: 0, createDate: '2026-07-01 11:00:00', raw: {} },
|
|
{ id: 'S003', name: '周末限时秒杀', enterpriseName: '北京电子有限公司', beginTime: '2026-07-12 00:00:00', endTime: '2026-07-13 23:59:59', type: '热轧钢板 30 吨', status: 0, createDate: '2026-07-02 14:00:00', raw: {} },
|
|
{ id: 'S004', name: '夏季特惠秒杀', enterpriseName: '广州工厂', beginTime: '2026-06-15 10:00:00', endTime: '2026-06-15 12:00:00', type: '盘螺 50 吨', status: 1, createDate: '2026-06-10 09:00:00', raw: {} },
|
|
{ id: 'S005', name: '建材爆款秒杀', enterpriseName: '杭州电子', beginTime: '2026-07-15 20:00:00', endTime: '2026-07-15 22:00:00', type: '砂石料 200 吨', status: 0, createDate: '2026-07-03 16:20:00', raw: {} },
|
|
{ id: 'S006', name: '工友福利秒杀', enterpriseName: '深圳科技股份有限公司', beginTime: '2026-07-20 09:00:00', endTime: '2026-07-20 10:00:00', type: '五金工具套装', status: 0, createDate: '2026-07-04 11:00:00', raw: {} }
|
|
]
|
|
|
|
const list = ref<Seckill[]>([])
|
|
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<Seckill[]>([])
|
|
|
|
const viewVisible = ref(false)
|
|
const viewData = ref<Seckill | 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: Seckill[]) { selection.value = rows }
|
|
function onView(row: Seckill) { viewData.value = row; viewVisible.value = true }
|
|
function onToggleStatus(row: Seckill) {
|
|
const newStatus = row.status === 0 ? 1 : 0
|
|
ElMessageBox.confirm(`确认${newStatus === 0 ? '启用' : '停用'}「${row.name}」?`, '提示', { type: 'warning' }).then(async () => {
|
|
try { await httpPost('/other/seckill/updateSeckill', { id: row.id, status: newStatus }); ElMessage.success(`${newStatus === 0 ? '启用' : '停用'}成功`); load() }
|
|
catch (e: any) { ElMessage.error(e?.msg || '操作失败') }
|
|
}).catch(() => {})
|
|
}
|
|
function onDelete(row: Seckill) {
|
|
ElMessageBox.confirm(`确认删除「${row.name}」?`, '提示', { type: 'warning' }).then(async () => {
|
|
try { await httpPost('/other/seckill/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/seckill/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.beginTime }}</el-descriptions-item>
|
|
<el-descriptions-item label="结束时间">{{ viewData.endTime }}</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>
|