|
|
@@ -1,362 +0,0 @@
|
|
|
-<template>
|
|
|
- <!-- 外层容器,保持默认的表格布局框架 -->
|
|
|
- <div class="default-main ba-table-box">
|
|
|
- <!-- 备注提示,当有 remark 时展示 -->
|
|
|
- <el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
|
|
-
|
|
|
- <!-- 顶部筛选与新增区域,左侧输入与下拉,右侧添加按钮 -->
|
|
|
- <!-- 表格顶部菜单 -->
|
|
|
- <TableHeader :buttons="['refresh', 'columnDisplay']"
|
|
|
- :quick-search-placeholder="t('Quick search placeholder', { fields: '产品名称' })">
|
|
|
- <!-- 套餐名称输入框,支持清空 -->
|
|
|
- <el-input
|
|
|
- class="filter-item"
|
|
|
- v-model="searchForm.name"
|
|
|
- placeholder="套餐名称"
|
|
|
- clearable
|
|
|
- @input="handleFilter"
|
|
|
- @clear="handleFilter"
|
|
|
- />
|
|
|
- <!-- 库存状态选择框,模拟原型的状态下拉 -->
|
|
|
- <el-select
|
|
|
- class="filter-item"
|
|
|
- v-model="searchForm.inventory_status"
|
|
|
- placeholder="库存状态"
|
|
|
- clearable
|
|
|
- @change="handleFilter"
|
|
|
- @clear="handleFilter"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="item in inventoryOptions"
|
|
|
- :key="item.value"
|
|
|
- :label="item.label"
|
|
|
- :value="item.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- <!-- 右侧添加按钮 -->
|
|
|
- <el-button type="primary" @click="onAdd">添加</el-button>
|
|
|
- </TableHeader>
|
|
|
- <!-- 表格主体,使用通用 Table 组件并自定义操作列 -->
|
|
|
- <Table>
|
|
|
- <!-- 自定义操作列插槽,呈现编辑/删除/详情 -->
|
|
|
- <template #operate>
|
|
|
- <el-table-column prop="operate" align="center" label="操作" min-width="180">
|
|
|
- <template #default="scope">
|
|
|
- <el-button type="primary" size="small" @click="onEdit(scope.row)">编辑</el-button>
|
|
|
- <el-button type="danger" size="small" @click="del(scope.row)">删除</el-button>
|
|
|
- <el-button style="color: #fff;" color="#9ca0fa" size="small" @click="viewDetail(scope.row)">套餐详情</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </template>
|
|
|
- </Table>
|
|
|
-
|
|
|
- <!-- 添加编辑弹框 -->
|
|
|
- <PopupForm ref="popupFormRef" @success="handleFormSuccess" />
|
|
|
-
|
|
|
- <!-- 套餐详情 / 库存调整弹框 -->
|
|
|
- <Detail ref="detailRef" @confirm="handleDetailConfirm" />
|
|
|
-
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script setup lang="ts">
|
|
|
-// 引入响应式工具与生命周期钩子
|
|
|
-import { onMounted, provide, reactive, ref } from 'vue'
|
|
|
-// 引入表格封装类
|
|
|
-import baTableClass from '/@/utils/baTable'
|
|
|
-// 引入通用表格组件
|
|
|
-import Table from '/@/components/table/index.vue'
|
|
|
-import TableHeader from '/@/components/table/header/index.vue'
|
|
|
-import PopupForm from './popupForm.vue'
|
|
|
-import Detail from './detail.vue'
|
|
|
-// 引入接口封装
|
|
|
-import { baTableApi } from '/@/api/common'
|
|
|
-// 引入国际化
|
|
|
-import { useI18n } from 'vue-i18n'
|
|
|
-// 引入消息提示
|
|
|
-import { ElMessage } from 'element-plus'
|
|
|
-
|
|
|
-// 组件名称定义
|
|
|
-defineOptions({
|
|
|
- name: 'application/combo',
|
|
|
-})
|
|
|
-
|
|
|
-// 国际化方法
|
|
|
-const { t } = useI18n()
|
|
|
-
|
|
|
-const popupFormRef = ref<InstanceType<typeof PopupForm>>()
|
|
|
-const detailRef = ref<InstanceType<typeof Detail>>()
|
|
|
-
|
|
|
-// 筛选表单数据,包含套餐名称与库存状态
|
|
|
-const searchForm = reactive({
|
|
|
- name: '', // 套餐名称关键词
|
|
|
- inventory_status: '', // 库存状态筛选值
|
|
|
-})
|
|
|
-
|
|
|
-// 库存状态选项,映射下拉显示
|
|
|
-const inventoryOptions = [
|
|
|
- { label: '充足', value: '1' },
|
|
|
- { label: '即将售罄', value: '2' },
|
|
|
- { label: '售罄', value: '3' },
|
|
|
-]
|
|
|
-
|
|
|
-// 静态模拟数据,来源于原型图 + 规格示例数据
|
|
|
-// 规格字段与 popupForm 中的 SpecificationItem 一致,方便后续联动编辑
|
|
|
-const baseData = [
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- name: '聚德神笔',
|
|
|
- inventory_status: '1',
|
|
|
- remaining_stock: 120,
|
|
|
- sold_quantity: 12,
|
|
|
- create_time: '2025-10-19',
|
|
|
- specificationsList: [
|
|
|
- {
|
|
|
- specName: '个人基础版',
|
|
|
- price: 99,
|
|
|
- stock: 80,
|
|
|
- packageType: '1', // 个人套餐
|
|
|
- chargeType: '1', // 按次数
|
|
|
- quantity: 10,
|
|
|
- remark: '适合个人日常使用',
|
|
|
- },
|
|
|
- {
|
|
|
- specName: '个人进阶版',
|
|
|
- price: 199,
|
|
|
- stock: 40,
|
|
|
- packageType: '1',
|
|
|
- chargeType: '2', // 按积分
|
|
|
- quantity: 100,
|
|
|
- remark: '附赠额外积分',
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- name: '棱镜',
|
|
|
- inventory_status: '2',
|
|
|
- remaining_stock: 12,
|
|
|
- sold_quantity: 88,
|
|
|
- create_time: '2025-10-19',
|
|
|
- specificationsList: [
|
|
|
- {
|
|
|
- specName: '团队标准版',
|
|
|
- price: 599,
|
|
|
- stock: 8,
|
|
|
- packageType: '2', // 团队套餐
|
|
|
- chargeType: '3', // 日卡
|
|
|
- quantity: undefined,
|
|
|
- remark: '适合小团队短期使用',
|
|
|
- },
|
|
|
- {
|
|
|
- specName: '团队增强版',
|
|
|
- price: 1599,
|
|
|
- stock: 4,
|
|
|
- packageType: '2',
|
|
|
- chargeType: '5', // 月卡
|
|
|
- quantity: undefined,
|
|
|
- remark: '按月计费,支持多人使用',
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- name: '套装示例',
|
|
|
- inventory_status: '3',
|
|
|
- remaining_stock: 0,
|
|
|
- sold_quantity: 120,
|
|
|
- create_time: '2025-10-19',
|
|
|
- specificationsList: [
|
|
|
- {
|
|
|
- specName: '体验版',
|
|
|
- price: 49,
|
|
|
- stock: 0,
|
|
|
- packageType: '1',
|
|
|
- chargeType: '1',
|
|
|
- quantity: 3,
|
|
|
- remark: '促销活动已售罄',
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
-]
|
|
|
-
|
|
|
-// 初始化表格实例,配置列信息
|
|
|
-const baTable = new baTableClass(
|
|
|
- new baTableApi('/admin/application.combo'),
|
|
|
- {
|
|
|
- column: [
|
|
|
- { label: '编号', prop: 'id', align: 'center', width: 70 },
|
|
|
- { label: '套餐名称', prop: 'name', align: 'center', minWidth: 140 },
|
|
|
- {
|
|
|
- label: '库存状态',
|
|
|
- prop: 'inventory_status',
|
|
|
- align: 'center',
|
|
|
- render: 'tag',
|
|
|
- custom: { '1': 'primary', '2': 'warning', '3': 'danger' },
|
|
|
- replaceValue: { '1': '充足', '2': '即将售罄', '3': '售罄' },
|
|
|
- effect: 'plain',
|
|
|
- minWidth: 120,
|
|
|
- },
|
|
|
- {
|
|
|
- label: '剩余库存',
|
|
|
- prop: 'remaining_stock',
|
|
|
- align: 'center',
|
|
|
- minWidth: 140,
|
|
|
- },
|
|
|
- { label: '已卖数量', prop: 'sold_quantity', align: 'center', minWidth: 100 },
|
|
|
- { label: '创建时间', prop: 'create_time', align: 'center', minWidth: 160 },
|
|
|
- { label: '操作', align: 'center', render: 'slot', slotName: 'operate', operator: false, minWidth: 180 },
|
|
|
- ],
|
|
|
- dblClickNotEditColumn: [undefined],
|
|
|
- },
|
|
|
- {
|
|
|
- defaultItems: {
|
|
|
- name: '',
|
|
|
- inventory_status: '1',
|
|
|
- remaining_stock: 0,
|
|
|
- sold_quantity: 0,
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- onTableDblclick: () => {
|
|
|
- return false
|
|
|
- },
|
|
|
- },
|
|
|
-)
|
|
|
-
|
|
|
-// 覆写获取数据方法,基于筛选条件过滤静态数据
|
|
|
-baTable.getData = () => {
|
|
|
- baTable.table.loading = true // 开启加载状态
|
|
|
- return new Promise<void>((resolve) => {
|
|
|
- setTimeout(() => {
|
|
|
- // 根据输入与下拉值过滤数据
|
|
|
- const filtered = baseData.filter((item) => {
|
|
|
- const matchName = searchForm.name ? item.name.includes(searchForm.name.trim()) : true
|
|
|
- const matchStatus = searchForm.inventory_status ? item.inventory_status === searchForm.inventory_status : true
|
|
|
- return matchName && matchStatus
|
|
|
- })
|
|
|
- baTable.table.data = filtered // 写入表格数据
|
|
|
- baTable.table.total = filtered.length // 写入总数
|
|
|
- baTable.table.loading = false // 关闭加载状态
|
|
|
- resolve() // 完成 Promise
|
|
|
- }, 200)
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 删除动作,展示提示
|
|
|
-const del = (row:TableRow) => {
|
|
|
- ElMessage.success(`已删除套餐:${row.name}`)
|
|
|
-}
|
|
|
-
|
|
|
-// 编辑动作:打开弹框并带入当前行
|
|
|
-const onEdit = (row:TableRow ) => {
|
|
|
- popupFormRef.value?.editDialog(row as any)
|
|
|
-}
|
|
|
-
|
|
|
-// 新增动作,展示提示
|
|
|
-const onAdd = () => {
|
|
|
- popupFormRef.value?.openDialog()
|
|
|
-}
|
|
|
-
|
|
|
-// 处理弹框提交成功(新增 / 编辑)
|
|
|
-const handleFormSuccess = (payload: {
|
|
|
- id: number | null
|
|
|
- type: 'add' | 'edit'
|
|
|
- name: string
|
|
|
- specificationsList: any[]
|
|
|
-}) => {
|
|
|
- const { id, type, name, specificationsList } = payload
|
|
|
-
|
|
|
- // 根据规格列表计算总库存
|
|
|
- const totalStock = (specificationsList || []).reduce(
|
|
|
- (sum, item) => sum + (Number(item.stock) || 0),
|
|
|
- 0
|
|
|
- )
|
|
|
-
|
|
|
- // 根据库存计算库存状态
|
|
|
- let inventoryStatus = '1' // 充足
|
|
|
- if (totalStock === 0) {
|
|
|
- inventoryStatus = '3' // 售罄
|
|
|
- } else if (totalStock < 10) {
|
|
|
- inventoryStatus = '2' // 即将售罄
|
|
|
- }
|
|
|
-
|
|
|
- if (type === 'edit' && id != null) {
|
|
|
- // 编辑:更新静态数据中的对应项
|
|
|
- const target = baseData.find((item) => item.id === id)
|
|
|
- if (target) {
|
|
|
- target.name = name
|
|
|
- target.remaining_stock = totalStock
|
|
|
- target.inventory_status = inventoryStatus
|
|
|
- // 把规格明细也一起带上,方便下次再编辑
|
|
|
- ;(target as any).specificationsList = specificationsList
|
|
|
- ElMessage.success(`已更新套餐:${name}`)
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 新增:简单提示(此处仍以静态数据为主,不追加行)
|
|
|
- ElMessage.success(`已添加套餐:${name}`)
|
|
|
- }
|
|
|
-
|
|
|
- // 重新根据筛选条件刷新一次表格
|
|
|
- baTable.getData()
|
|
|
-}
|
|
|
-
|
|
|
-// 套餐详情动作,弹出详情弹框,可从按钮或库存单元格进入
|
|
|
-const viewDetail = (row: Record<string, any>, fromStockClick = false) => {
|
|
|
- if (!row) return
|
|
|
- detailRef.value?.open(row as any)
|
|
|
- if (!fromStockClick) {
|
|
|
- ElMessage.success(`查看套餐详情:${row.name}`)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 详情弹框确认修改库存
|
|
|
-const handleDetailConfirm = (payload: {
|
|
|
- id: number
|
|
|
- specificationsList: any[]
|
|
|
- totalStock: number
|
|
|
-}) => {
|
|
|
- const target = baseData.find((item) => item.id === payload.id)
|
|
|
- if (!target) return
|
|
|
-
|
|
|
- // 更新规格明细
|
|
|
- ;(target as any).specificationsList = payload.specificationsList
|
|
|
-
|
|
|
- // 更新总库存
|
|
|
- target.remaining_stock = payload.totalStock
|
|
|
-
|
|
|
- // 根据总库存刷新库存状态
|
|
|
- if (payload.totalStock === 0) {
|
|
|
- target.inventory_status = '3'
|
|
|
- } else if (payload.totalStock < 10) {
|
|
|
- target.inventory_status = '2'
|
|
|
- } else {
|
|
|
- target.inventory_status = '1'
|
|
|
- }
|
|
|
-
|
|
|
- ElMessage.success(`已更新「${target.name}」的库存信息`)
|
|
|
- baTable.getData()
|
|
|
-}
|
|
|
-
|
|
|
-// 输入或选择变化时触发过滤
|
|
|
-const handleFilter = () => {
|
|
|
- baTable.getData()
|
|
|
-}
|
|
|
-
|
|
|
-// 注入表格实例供子组件使用
|
|
|
-provide('baTable', baTable)
|
|
|
-
|
|
|
-// 初始化挂载表格并拉取数据
|
|
|
-onMounted(() => {
|
|
|
- baTable.mount()
|
|
|
- baTable.getData()
|
|
|
-})
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped lang="scss">
|
|
|
-.filter-item {
|
|
|
- width: 180px;
|
|
|
- margin: 0 6px;
|
|
|
-}
|
|
|
-</style>
|
|
|
-
|