소스 검색

应用模块编写完成

“陈博论” 10 달 전
부모
커밋
5c333706ae

+ 18 - 13
web/src/api/backend/package/package.ts

@@ -19,7 +19,7 @@ export function edit(data: anyObj) {
 }
 
 // 删除
-export function del(data:anyObj) {
+export function del(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/delete',
         method: 'post',
@@ -28,16 +28,21 @@ export function del(data:anyObj) {
 }
 
 // 更新产品状态
-export function batchStatus(data:anyObj) {
-    return createAxios({
-        url: '/admin/package.Package/batchStatus',
-        method: 'get',
-        params: data,
-    })
+export function batchStatus(data: anyObj) {
+    return createAxios(
+        {
+            url: '/admin/package.Package/batchStatus',
+            method: 'post',
+            data: data,
+        },
+        {
+            showSuccessMessage: true
+        }
+    )
 }
 
 // 详情
-export function detail(data:anyObj) {
+export function detail(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/detail',
         method: 'GET',
@@ -46,7 +51,7 @@ export function detail(data:anyObj) {
 }
 
 // 套餐统计信息
-export function statistics(data:anyObj) {
+export function statistics(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/statistics',
         method: 'GET',
@@ -55,7 +60,7 @@ export function statistics(data:anyObj) {
 }
 
 // 规格列表
-export function specList(data:anyObj) {
+export function specList(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/specList',
         method: 'get',
@@ -64,7 +69,7 @@ export function specList(data:anyObj) {
 }
 
 // 添加/编辑套餐规格
-export function editSpec(data:anyObj) {
+export function editSpec(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/editSpec',
         method: 'post',
@@ -73,7 +78,7 @@ export function editSpec(data:anyObj) {
 }
 
 //删除套餐规格
-export function deleteSpec(data:anyObj) {
+export function deleteSpec(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/deleteSpec',
         method: 'post',
@@ -82,7 +87,7 @@ export function deleteSpec(data:anyObj) {
 }
 
 //删除套餐规格
-export function batchUpdateStock(data:anyObj) {
+export function batchUpdateStock(data: anyObj) {
     return createAxios({
         url: '/admin/package.Package/batchUpdateStock',
         method: 'get',

+ 426 - 249
web/src/views/backend/package/package/detail.vue

@@ -2,156 +2,188 @@
     <el-dialog
         v-model="visible"
         class="ba-operate-dialog"
-        width="950px"
+        width="1200px"
         :close-on-click-modal="false"
         :destroy-on-close="true"
         @close="handleClose"
     >
         <template #header>
             <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
-                套餐详情 --- {{ detailData?.name || '-' }}
+                套餐详情 --- {{ packageInfo?.name || '-' }}
             </div>
         </template>
 
-        <el-scrollbar class="ba-table-form-scrollbar">
-            <div class="detail-wrapper">
-                <el-descriptions
-                    v-if="detailData"
-                    :column="3"
-                    border
-                    class="detail-base-info"
-                    size="small"
-                >
-                    <el-descriptions-item label="套餐名称">
-                        {{ detailData.name }}
-                    </el-descriptions-item>
-                    <el-descriptions-item label="当前库存状态">
-                        <el-tag :type="inventoryStatusTag" effect="plain">
-                            {{ inventoryStatusText }}
-                        </el-tag>
-                    </el-descriptions-item>
-                    <el-descriptions-item label="总剩余库存">
-                        {{ totalStock }}
-                    </el-descriptions-item>
-                    <el-descriptions-item label="已卖数量">
-                        {{ detailData.sold_quantity }}
-                    </el-descriptions-item>
-                    <el-descriptions-item label="创建时间">
-                        {{ detailData.create_time }}
-                    </el-descriptions-item>
-                </el-descriptions>
-
-                <!-- 规格明细表格 -->
-                <div class="spec-table-title">
-                    套餐规格明细
-                </div>
-                <el-table
-                    v-if="specList.length"
-                    :data="specList"
-                    border
-                    size="small"
-                    style="width: 100%"
-                >
-                    <el-table-column
-                        prop="specName"
-                        label="规格名称"
-                        align="center"
-                        min-width="120"
-                    />
-                    <el-table-column
-                        label="库存状态"
-                        align="center"
-                        min-width="120"
-                    >
-                        <template #default="{ row }">
-                            <el-tag :type="getRowStatusTag(row.stock)" effect="plain">
-                                {{ getRowStatusText(row.stock) }}
-                            </el-tag>
-                        </template>
-                    </el-table-column>
-                    <el-table-column
-                        label="剩余库存"
-                        align="center"
-                        min-width="140"
-                    >
-                        <template #default="{ row, $index }">
-                            <el-input-number
-                                v-model.number="specList[$index].stock"
-                                :min="0"
-                                :max="999999"
-                                controls-position="right"
-                                size="small"
-                            />
-                        </template>
-                    </el-table-column>
-                    <el-table-column
-                        prop="price"
-                        label="价格(元)"
-                        align="center"
-                        min-width="120"
-                    />
-                    <el-table-column
-                        label="套餐类型"
-                        align="center"
-                        min-width="120"
-                    >
-                        <template #default="{ row }">
-                            {{ packageTypeText(row.packageType) }}
-                        </template>
-                    </el-table-column>
-                    <el-table-column
-                        label="收费方式"
-                        align="center"
-                        min-width="140"
-                    >
-                        <template #default="{ row }">
-                            {{ chargeTypeText(row.chargeType) }}
-                            <span v-if="showQuantity(row.chargeType) && row.quantity">
-                                ({{ row.quantity }}{{ getUnitLabel(row.chargeType) }})
-                            </span>
-                        </template>
-                    </el-table-column>
-                    <el-table-column
-                        prop="remark"
-                        label="说明"
-                        align="center"
-                        min-width="200"
-                        show-overflow-tooltip
-                    />
-                </el-table>
-
-                <!-- 汇总信息 -->
-                <div class="summary-row">
-                    <div class="summary-item">
-                        <span class="label">规格数量:</span>
-                        <span>{{ specList.length }}</span>
+        <el-scrollbar class="ba-table-form-scrollbar" v-loading="loading">
+            <div class="detail-wrapper" v-if="packageInfo">
+
+                <!-- 统计信息卡片 -->
+                <div class="statistics-card">
+                    <div class="stat-item">
+                        <div class="stat-icon spec-icon">
+                            <el-icon><List /></el-icon>
+                        </div>
+                        <div class="stat-content">
+                            <div class="stat-label">规格总数</div>
+                            <div class="stat-value">{{ statistics.spec_count }}</div>
+                        </div>
                     </div>
-                    <div class="summary-item">
-                        <span class="label">总剩余库存:</span>
-                        <span class="value-number">{{ totalStock }}</span>
+                    <div class="stat-item">
+                        <div class="stat-icon stock-icon">
+                            <el-icon><Box /></el-icon>
+                        </div>
+                        <div class="stat-content">
+                            <div class="stat-label">总库存</div>
+                            <div class="stat-value">{{ statistics.total_stock }}</div>
+                        </div>
                     </div>
-                    <div class="summary-item">
-                        <span class="label">整体库存状态:</span>
-                        <el-tag :type="inventoryStatusTag" effect="plain">
-                            {{ inventoryStatusText }}
-                        </el-tag>
+                    <div class="stat-item">
+                        <div class="stat-icon sold-icon">
+                            <el-icon><TrendCharts /></el-icon>
+                        </div>
+                        <div class="stat-content">
+                            <div class="stat-label">已售数量</div>
+                            <div class="stat-value">{{ statistics.total_sold }}</div>
+                        </div>
                     </div>
                 </div>
+
+                <!-- 规格明细表格 -->
+                <div class="table-card">
+                    <div class="table-header">
+                        <h3 class="table-title">
+                            <el-icon class="title-icon"><Grid /></el-icon>
+                            套餐规格明细
+                            <span class="spec-count">(共 {{ specList.length }} 项)</span>
+                        </h3>
+                    </div>
+                    <el-table
+                        v-if="specList.length"
+                        :data="specList"
+                        class="spec-table"
+                        size="default"
+                        stripe
+                    >
+                        <el-table-column
+                            type="index"
+                            label="序号"
+                            align="center"
+                            width="70"
+                        />
+                        <el-table-column
+                            prop="spec_name"
+                            label="规格名称"
+                            align="center"
+                            min-width="140"
+                        >
+                            <template #default="{ row }">
+                                <span class="spec-name">{{ row.spec_name }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="price"
+                            label="价格"
+                            align="center"
+                            min-width="110"
+                        >
+                            <template #default="{ row }">
+                                <span class="price-text">¥{{ row.price }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="stock"
+                            label="库存"
+                            align="center"
+                            min-width="100"
+                        >
+                            <template #default="{ row }">
+                                <span class="stock-number">{{ row.stock }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            label="库存状态"
+                            align="center"
+                            min-width="120"
+                        >
+                            <template #default="{ row }">
+                                <el-tag :type="getStockStatusTag(row.stock_status)" effect="dark" size="small">
+                                    {{ row.stock_status }}
+                                </el-tag>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="sold_count"
+                            label="已售数量"
+                            align="center"
+                            min-width="110"
+                        >
+                            <template #default="{ row }">
+                                <span class="sold-number">{{ row.sold_count }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="type_text"
+                            label="套餐类型"
+                            align="center"
+                            min-width="120"
+                        >
+                            <template #default="{ row }">
+                                <el-tag type="primary" effect="plain" size="small">{{ row.type_text }}</el-tag>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="card_type_text"
+                            label="卡类型"
+                            align="center"
+                            min-width="110"
+                        >
+                            <template #default="{ row }">
+                                <el-tag type="warning" effect="plain" size="small">{{ row.card_type_text }}</el-tag>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            label="次数/积分"
+                            align="center"
+                            min-width="120"
+                        >
+                            <template #default="{ row }">
+                                <span v-if="String(row.card_type) === '1'">
+                                    <span class="unit-value">{{ row.unit_value || '-' }}</span>
+                                    <span class="unit-label">次</span>
+                                </span>
+                                <span v-else-if="String(row.card_type) === '2'">
+                                    <span class="points-value">{{ row.points_value || '-' }}</span>
+                                    <span class="unit-label">分</span>
+                                </span>
+                                <span v-else class="no-value">-</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="description"
+                            label="说明描述"
+                            align="center"
+                            min-width="150"
+                            show-overflow-tooltip
+                        >
+                            <template #default="{ row }">
+                                <span class="description-cell">{{ row.description || '-' }}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column
+                            prop="create_time"
+                            label="创建时间"
+                            align="center"
+                            min-width="180"
+                        />
+                    </el-table>
+                    <el-empty v-else description="暂无规格数据" :image-size="100" />
+                </div>
             </div>
         </el-scrollbar>
 
         <template #footer>
             <div class="dialog-footer">
                 <el-button size="large" class="cancel-btn" @click="handleClose">
-                    取消
-                </el-button>
-                <el-button
-                    size="large"
-                    type="primary"
-                    class="submit-btn"
-                    @click="handleConfirm"
-                >
-                    确认修改库存
+                    关闭
                 </el-button>
             </div>
         </template>
@@ -159,162 +191,300 @@
 </template>
 
 <script setup lang="ts">
-import { computed, reactive, ref } from 'vue'
-
-interface SpecificationItem {
-    specName: string
-    price: number | undefined
-    stock: number | undefined
-    packageType: string
-    chargeType: string
-    quantity?: number | undefined
-    remark?: string
-}
+import { ref } from 'vue'
+import { detail } from '/@/api/backend/package/package'
+import { ElMessage } from 'element-plus'
+import { Document, List, Box, TrendCharts, Grid } from '@element-plus/icons-vue'
 
-interface DetailRow {
+// 套餐基本信息接口
+interface PackageInfo {
     id: number
     name: string
-    inventory_status: string
-    remaining_stock: number
-    sold_quantity: number
+    description: string
+    sort: number
+    status: number
     create_time: string
-    specificationsList?: SpecificationItem[]
+    update_time: string
 }
 
-const visible = ref(false)
-const detailData = ref<DetailRow | null>(null)
-const specList = reactive<SpecificationItem[]>([])
+// 规格列表接口
+interface SpecItem {
+    id: number
+    package_id: number
+    spec_name: string
+    price: string
+    stock: number
+    type: number
+    card_type: number
+    unit_value: number
+    points_value: number
+    description: string
+    sold_count: number
+    sort: number
+    create_time: string
+    update_time: string
+    stock_status: string
+    type_text: string
+    card_type_text: string
+    charge_text: string
+}
 
-const emits = defineEmits<{
-    (e: 'confirm', payload: { id: number; specificationsList: SpecificationItem[]; totalStock: number }): void
-}>()
+// 统计信息接口
+interface Statistics {
+    total_stock: number
+    total_sold: number
+    spec_count: number
+}
 
-const open = (row: DetailRow) => {
-    detailData.value = { ...row }
+// 详情数据接口
+interface DetailResponse {
+    package_info: PackageInfo
+    spec_list: SpecItem[]
+    statistics: Statistics
+}
 
-    // 重置规格明细
-    specList.splice(0, specList.length)
-    const list = (row.specificationsList || []) as SpecificationItem[]
-    list.forEach((item) => {
-        specList.push({ ...item })
-    })
+const visible = ref(false)
+const loading = ref(false)
+const packageInfo = ref<PackageInfo | null>(null)
+const specList = ref<SpecItem[]>([])
+const statistics = ref<Statistics>({
+    total_stock: 0,
+    total_sold: 0,
+    spec_count: 0,
+})
 
+const open = async (id: number) => {
     visible.value = true
+    await fetchDetail(id)
+}
+
+// 获取详情数据
+const fetchDetail = async (id: number) => {
+    try {
+        loading.value = true
+        const res = await detail({ id })
+        const data: DetailResponse = res.data
+
+        if (data) {
+            packageInfo.value = data.package_info || null
+            specList.value = data.spec_list || []
+            statistics.value = data.statistics || {
+                total_stock: 0,
+                total_sold: 0,
+                spec_count: 0,
+            }
+        }
+    } catch (error: any) {
+        ElMessage.error(error?.msg || '获取详情失败')
+        handleClose()
+    } finally {
+        loading.value = false
+    }
 }
 
 const handleClose = () => {
     visible.value = false
+    // 重置数据
+    packageInfo.value = null
+    specList.value = []
+    statistics.value = {
+        total_stock: 0,
+        total_sold: 0,
+        spec_count: 0,
+    }
 }
 
-const totalStock = computed(() =>
-    specList.reduce((sum, item) => sum + (Number(item.stock) || 0), 0)
-)
-
-const inventoryStatusText = computed(() => {
-    if (totalStock.value === 0) return '售罄'
-    if (totalStock.value < 10) return '即将售罄'
-    return '充足'
-})
-
-const inventoryStatusTag = computed(() => {
-    if (totalStock.value === 0) return 'danger'
-    if (totalStock.value < 10) return 'warning'
+// 获取库存状态标签类型
+const getStockStatusTag = (status: string) => {
+    if (status === '售罄') return 'danger'
+    if (status === '即将售罄') return 'warning'
     return 'success'
-})
-
-const getRowStatusText = (stock?: number) => {
-    const value = Number(stock) || 0
-    if (value === 0) return '售罄'
-    if (value < 10) return '即将售罄'
-    return '充足'
 }
 
-const getRowStatusTag = (stock?: number) => {
-    const value = Number(stock) || 0
-    if (value === 0) return 'danger'
-    if (value < 10) return 'warning'
-    return 'success'
-}
+defineExpose({ open })
+</script>
 
-const packageTypeText = (type: string) => {
-    if (type === '2') return '团队套餐'
-    return '个人套餐'
+<style scoped lang="scss">
+.detail-wrapper {
+    padding: 20px;
 }
 
-const chargeTypeText = (type: string) => {
-    const map: Record<string, string> = {
-        '1': '按次数',
-        '2': '按积分',
-        '3': '日卡',
-        '4': '周卡',
-        '5': '月卡',
-        '6': '季卡',
-        '7': '年卡',
+// 统计信息卡片
+.statistics-card {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 16px;
+    margin-bottom: 20px;
+
+    .stat-item {
+        background: #fff;
+        border-radius: 12px;
+        padding: 20px;
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+        transition: all 0.3s ease;
+        border: 1px solid #f0f0f0;
+
+        &:hover {
+            transform: translateY(-2px);
+            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+        }
+
+        .stat-icon {
+            width: 56px;
+            height: 56px;
+            border-radius: 12px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 24px;
+            color: #fff;
+            flex-shrink: 0;
+
+            &.spec-icon {
+                background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
+            }
+
+            &.stock-icon {
+                background: linear-gradient(135deg, #36a3f7 0%, #409eff 100%);
+            }
+
+            &.sold-icon {
+                background: linear-gradient(135deg, #79bbff 0%, #409eff 100%);
+            }
+        }
+
+        .stat-content {
+            flex: 1;
+
+            .stat-label {
+                font-size: 13px;
+                color: #909399;
+                margin-bottom: 8px;
+                font-weight: 500;
+            }
+
+            .stat-value {
+                font-size: 28px;
+                font-weight: 700;
+                color: #303133;
+                line-height: 1;
+            }
+        }
     }
-    return map[type] || '-'
 }
 
-const showQuantity = (chargeType: string) => {
-    return chargeType === '1' || chargeType === '2'
-}
+// 表格卡片
+.table-card {
+    background: #fff;
+    border-radius: 12px;
+    padding: 20px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+    border: 1px solid #f0f0f0;
+
+    .table-header {
+        margin-bottom: 16px;
+        padding-bottom: 12px;
+        border-bottom: 2px solid #f5f7fa;
+
+        .table-title {
+            margin: 0;
+            font-size: 18px;
+            font-weight: 600;
+            color: #303133;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+
+            .title-icon {
+                font-size: 20px;
+                color: #409eff;
+            }
+
+            .spec-count {
+                font-size: 14px;
+                font-weight: 400;
+                color: #909399;
+                margin-left: 4px;
+            }
+        }
+    }
 
-const getUnitLabel = (chargeType: string) => {
-    if (chargeType === '1') return '次'
-    if (chargeType === '2') return '分'
-    return ''
-}
+    .spec-table {
+        :deep(.el-table__header) {
+            th {
+                background: #f8f9fa;
+                color: #606266;
+                font-weight: 600;
+                border-bottom: 2px solid #ebeef5;
+            }
+        }
 
-const handleConfirm = () => {
-    if (!detailData.value) return
-    emits('confirm', {
-        id: detailData.value.id,
-        specificationsList: specList.map((item) => ({ ...item })),
-        totalStock: totalStock.value,
-    })
-    handleClose()
-}
+        :deep(.el-table__body) {
+            tr {
+                transition: background-color 0.2s;
 
-defineExpose({ open })
-</script>
+                &:hover {
+                    background-color: #f5f7fa;
+                }
+            }
+        }
 
-<style scoped lang="scss">
-.detail-wrapper {
-    padding: 12px 16px 4px;
-}
+        .spec-name {
+            font-weight: 600;
+            color: #303133;
+        }
 
-.detail-base-info {
-    margin-bottom: 16px;
-}
+        .price-text {
+            color: #f56c6c;
+            font-weight: 600;
+            font-size: 15px;
+        }
 
-.spec-table-title {
-    margin-bottom: 8px;
-    font-weight: 600;
-    font-size: 14px;
-    color: #333;
-}
+        .stock-number {
+            font-weight: 600;
+            color: #409eff;
+        }
 
-.summary-row {
-    display: flex;
-    flex-wrap: wrap;
-    justify-content: flex-end;
-    gap: 16px;
-    margin-top: 12px;
-    padding-top: 8px;
-    border-top: 1px dashed #e5e5e5;
+        .sold-number {
+            font-weight: 500;
+            color: #67c23a;
+        }
 
-    .summary-item {
-        display: inline-flex;
-        align-items: center;
-        font-size: 13px;
+        .charge-text {
+            color: #606266;
+            font-size: 13px;
+        }
 
-        .label {
-            color: #666;
-            margin-right: 4px;
+        .unit-value {
+            font-weight: 600;
+            color: #409eff;
+            font-size: 15px;
         }
 
-        .value-number {
+        .points-value {
             font-weight: 600;
-            color: #333;
+            color: #e6a23c;
+            font-size: 15px;
+        }
+
+        .unit-label {
+            color: #909399;
+            font-size: 13px;
+            margin-left: 4px;
+        }
+
+        .no-value {
+            color: #c0c4cc;
+            font-size: 13px;
+        }
+
+        .description-cell {
+            color: #606266;
+            font-size: 13px;
+            line-height: 1.5;
         }
     }
 }
@@ -325,6 +495,13 @@ defineExpose({ open })
     align-items: center;
     gap: 10px;
 }
+
+// 响应式设计
+@media (max-width: 1200px) {
+    .statistics-card {
+        grid-template-columns: 1fr;
+    }
+}
 </style>
 
 

+ 32 - 11
web/src/views/backend/package/package/index.vue

@@ -6,7 +6,7 @@
         <!-- 表格顶部菜单 -->
         <TableHeader :buttons="['refresh', 'quickSearch', 'columnDisplay']"
             :quick-search-placeholder="t('Quick search placeholder', { fields: '套餐名称' })">
-            <el-button type="primary" @click="onAdd">添加</el-button>
+            <el-button type="primary" @click="onAdd" v-auth="'edit'">添加</el-button>
         </TableHeader>
 
         <!-- 表格 -->
@@ -15,16 +15,17 @@
             <template #status>
                 <el-table-column prop="status" align="center" label="状态" min-width="100">
                     <template #default="scope">
-                        <el-switch v-model="scope.row.status" 
-                            :active-value="1" :inactive-value="0" />
+                        <el-switch v-model="scope.row.status" @change="changeStatus(scope.row)"
+                            :active-value="1" :inactive-value="2" />
                     </template>
                 </el-table-column>
             </template>
             <template #operate>
-                <el-table-column prop="operate" align="center" label="操作" min-width="150">
+                <el-table-column prop="operate" align="center" label="操作" min-width="220">
                     <template #default="scope">
-                        <el-button type="primary" size="small" @click="onEdit(scope.row)">编辑</el-button>
-                        <el-button type="danger" size="small" @click="handleDel(scope.row)">删除</el-button>
+                        <el-button type="info" size="small" @click="onDetail(scope.row)"  v-auth="'detail'">详情</el-button>
+                        <el-button type="primary" size="small" @click="onEdit(scope.row)" v-auth="'edit'">编辑</el-button>
+                        <el-button type="danger" size="small" @click="handleDel(scope.row)" v-auth="'delete'">删除</el-button>
                     </template>
                 </el-table-column>
             </template>
@@ -32,19 +33,22 @@
 
         <!-- 表单 -->
         <PopupForm ref="popupFormRef" @success="handleFormSuccess" />
+
+        <!-- 详情弹框 -->
+        <Detail ref="detailRef" />
     </div>
 </template>
 
 <script setup lang="ts">
 import { provide, useTemplateRef } from 'vue'
-import { cloneDeep } from 'lodash-es'
 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 { del, edit } from '/@/api/backend/package/package'
+import { del, edit,batchStatus } from '/@/api/backend/package/package'
 import { ElMessageBox, ElMessage } from 'element-plus'
 
 type TableRow = Record<string, any>
@@ -56,6 +60,7 @@ defineOptions({
 const { t } = useI18n()
 
 const popupFormRef = useTemplateRef<InstanceType<typeof PopupForm>>('popupFormRef')
+const detailRef = useTemplateRef<InstanceType<typeof Detail>>('detailRef')
 
 const baTable = new baTableClass(
     new baTableApi('/admin/package.package/'),
@@ -77,14 +82,21 @@ const baTable = new baTableClass(
                 minWidth: 100,
             },
             {
-                label: '规格数量',
-                prop: 'spec_count',
+                label: '总库存数',
+                prop: 'remaining_stock',
                 align: 'center',
                 minWidth: 110,
             },
+            
             {
                 label: '已售数量',
-                prop: 'total_sold',
+                prop: 'sold_count',
+                align: 'center',
+                minWidth: 110,
+            },
+            {
+                label: '规格数量',
+                prop: 'spec_count',
                 align: 'center',
                 minWidth: 110,
             },
@@ -144,6 +156,11 @@ const handleDel = (row: TableRow) => {
     })
 }
 
+// 改变状态
+const changeStatus = (row: TableRow) => {
+    batchStatus({ ids: row.id, status: row.status })
+}
+
 const onAdd = () => {
     popupFormRef.value?.openDialog()
 }
@@ -152,6 +169,10 @@ const onEdit = (row: TableRow) => {
     popupFormRef.value?.editDialog(row)
 }
 
+const onDetail = (row: TableRow) => {
+    detailRef.value?.open(row.id)
+}
+
 // 处理表单提交成功
 const handleFormSuccess = async (submitData: any) => {
     try {

+ 21 - 30
web/src/views/backend/package/package/popupForm.vue

@@ -24,51 +24,47 @@
 						<div class="spec-section">
 							<div class="spec-header">
 								<span class="spec-title">规格列表:</span>
-								<el-button
-									type="primary"
-									size="small"
-									plain
-									class="spec-add-btn"
-									@click="openSpecDialog"
-								>
+								<el-button type="primary" size="small" plain class="spec-add-btn"
+									@click="openSpecDialog">
 									<span class="spec-add-icon">+</span>
 									<span>新增规格</span>
 								</el-button>
 							</div>
 							<div v-loading="specLoading" class="spec-list">
 								<el-table :data="specListData" border stripe style="width: 100%">
-									<el-table-column prop="spec_name" label="规格名称" min-width="120" />
-									<el-table-column prop="price" label="价格" min-width="100">
+									<el-table-column prop="spec_name" align="center" label="规格名称" min-width="120" />
+									<el-table-column prop="price" align="center" label="价格" min-width="100">
 										<template #default="scope">
 											<span>¥{{ scope.row.price }}</span>
 										</template>
 									</el-table-column>
-									<el-table-column prop="stock" label="库存" min-width="80" />
-									<el-table-column prop="type" label="套餐类型" min-width="100">
+									<el-table-column prop="stock" align="center" label="库存" min-width="80" />
+									<el-table-column prop="type" align="center" label="套餐类型" min-width="100">
 										<template #default="scope">
 											<el-tag :type="scope.row.type === '1' ? 'primary' : 'success'">
 												{{ scope.row.type === '1' ? '个人套餐' : '企业套餐' }}
 											</el-tag>
 										</template>
 									</el-table-column>
-									<el-table-column prop="card_type" label="卡类型" min-width="100">
+									<el-table-column prop="card_type" align="center" label="卡类型" min-width="100">
 										<template #default="scope">
 											{{ getCardTypeLabel(scope.row.card_type) }}
 										</template>
 									</el-table-column>
-									<el-table-column label="次数/积分" min-width="100">
+									<el-table-column label="次数/积分" align="center" min-width="100">
 										<template #default="scope">
-											<span v-if="scope.row.card_type === '1' && scope.row.unit_value">
+											<span v-if="scope.row.card_type == '1' && scope.row.unit_value">
 												{{ scope.row.unit_value }}次
 											</span>
-											<span v-else-if="scope.row.card_type === '2' && scope.row.points_value">
+											<span v-else-if="scope.row.card_type == '2' && scope.row.points_value">
 												{{ scope.row.points_value }}分
 											</span>
 											<span v-else>-</span>
 										</template>
 									</el-table-column>
-									<el-table-column prop="description" label="说明" min-width="150" show-overflow-tooltip />
-									<el-table-column label="操作" width="150" fixed="right">
+									<el-table-column prop="description" align="center" label="说明" min-width="150"
+										show-overflow-tooltip />
+									<el-table-column label="操作" align="center" width="150" fixed="right">
 										<template #default="scope">
 											<el-button type="primary" size="small" @click="editSpecItem(scope.row)">
 												编辑
@@ -86,12 +82,7 @@
 						</div>
 					</el-form-item>
 					<el-form-item label="说明" prop="description">
-						<el-input
-							v-model="formData.description"
-							type="textarea"
-							:rows="3"
-							placeholder="请输入套餐说明"
-						/>
+						<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入套餐说明" />
 					</el-form-item>
 				</el-form>
 			</div>
@@ -104,7 +95,7 @@
 			</div>
 		</template>
 	</el-dialog>
-	
+
 	<!-- 规格弹框 -->
 	<SpecDialog ref="specDialogRef" @success="loadSpecList" />
 </template>
@@ -193,12 +184,12 @@ const resetForm = () => {
 // 加载规格列表
 const loadSpecList = async () => {
 	if (!currentEditId.value) return
-	
+
 	// 防止重复请求
 	if (specListLoading.value) return
 	specListLoading.value = true
 	specLoading.value = true
-	
+
 	try {
 		const res = await specList({ package_id: currentEditId.value })
 		if (res.data && Array.isArray(res.data)) {
@@ -321,11 +312,11 @@ watch([dialogVisible, currentEditId], ([visible, id], [oldVisible, oldId]) => {
 	// 只在以下情况加载规格列表:
 	// 1. 对话框从关闭变为打开,且是编辑模式,且有ID
 	// 2. ID发生变化(例如从新增切换到编辑),且对话框是打开的,且是编辑模式
-	const shouldLoad = visible && 
-		dialogType.value === 'edit' && 
-		id && 
+	const shouldLoad = visible &&
+		dialogType.value === 'edit' &&
+		id &&
 		((!oldVisible && visible) || (id !== oldId && visible))
-	
+
 	if (shouldLoad) {
 		// 使用 nextTick 确保所有状态都已更新
 		nextTick(() => {

+ 5 - 4
web/src/views/backend/package/product/index.vue

@@ -4,7 +4,7 @@
             show-icon />
 
         <!-- 表格顶部菜单 -->
-        <TableHeader :buttons="['refresh', 'quickSearch', 'columnDisplay']"
+        <TableHeader :buttons="['refresh','columnDisplay']"
             :quick-search-placeholder="t('Quick search placeholder', { fields: '产品名称' })">
             <el-form style="margin-left: 5px;padding-top: 20px;" inline :model="seachForm" @submit.prevent="handleSearch">
                 <el-form-item >
@@ -24,7 +24,7 @@
                     </el-select>
                 </el-form-item>
                 <el-form-item>
-                    <el-button type="primary" @click="onAdd">添加</el-button>
+                    <el-button type="primary" @click="onAdd" v-auth="'edit'">添加</el-button>
                 </el-form-item>
             </el-form>
         </TableHeader>
@@ -43,8 +43,8 @@
             <template #operate>
                 <el-table-column prop="operate" align="center" label="操作" min-width="150">
                     <template #default="scope">
-                        <el-button type="primary" size="small" @click="onEdit(scope.row)">编辑</el-button>
-                        <el-button type="danger" size="small" @click="handleDel(scope.row)">删除</el-button>
+                        <el-button type="primary" size="small" @click="onEdit(scope.row)" v-auth="'edit'">编辑</el-button>
+                        <el-button type="danger" size="small" @click="handleDel(scope.row)" v-auth="'delete'">删除</el-button>
                     </template>
                 </el-table-column>
             </template>
@@ -52,6 +52,7 @@
 
         <!-- 表单 -->
         <PopupForm @success="baTable.getIndex()" :packageOptions="packageOptions" />
+
     </div>
 </template>
 

+ 0 - 14
web/src/views/backend/transaction/distribution/index.vue

@@ -1,14 +0,0 @@
-<template>
-    <div class="default-main ba-table-box">
-        <el-card shadow="never">
-            分润记录页面待开发
-        </el-card>
-    </div>
-</template>
-
-<script setup lang="ts">
-defineOptions({
-    name: 'transaction/distribution',
-})
-</script>
-

+ 0 - 14
web/src/views/backend/transaction/order/index.vue

@@ -1,14 +0,0 @@
-<template>
-    <div class="default-main ba-table-box">
-        <el-card shadow="never">
-            订单管理页面待开发
-        </el-card>
-    </div>
-</template>
-
-<script setup lang="ts">
-defineOptions({
-    name: 'transaction/order',
-})
-</script>
-

+ 0 - 14
web/src/views/backend/transaction/withdraw/index.vue

@@ -1,14 +0,0 @@
-<template>
-    <div class="default-main ba-table-box">
-        <el-card shadow="never">
-            提现审核页面待开发
-        </el-card>
-    </div>
-</template>
-
-<script setup lang="ts">
-defineOptions({
-    name: 'transaction/withdraw',
-})
-</script>
-