feat: 添加用户虚拟机商品管理
Build and Deploy Vue3 / build (push) Successful in 1m40s
Build and Deploy Vue3 / deploy (push) Successful in 1m8s

This commit is contained in:
2026-03-31 15:13:04 +08:00
parent 71d3605f4f
commit c07e09c151
28 changed files with 7143 additions and 621 deletions
+21 -24
View File
@@ -108,7 +108,7 @@
<el-avatar v-else-if="row.isGroup" :size="32" :style="{ background: getLevelColor(row.level) }">
<el-icon><Folder /></el-icon>
</el-avatar>
<el-avatar v-else-if="row.isProduct && row.data?.coverId" :size="32" :src="getFileUrl(row.data.coverId)" />
<el-avatar v-else-if="row.isProduct && row.data?.cover" :size="32" :src="row.data.cover" />
<el-avatar v-else-if="row.isProduct" :size="32" :style="{ background: '#409eff' }">
<el-icon><Document /></el-icon>
</el-avatar>
@@ -760,6 +760,19 @@
<el-form-item label="是否必选" prop="must">
<el-switch v-model="paramForm.must" :active-value="true" :inactive-value="false" active-text="必选" inactive-text="可选" />
</el-form-item>
<el-divider content-position="left">权限控制</el-divider>
<el-form-item label="允许单独购买">
<el-switch v-model="paramForm.user_add" active-text="允许" inactive-text="不允许" />
<div style="font-size: 12px; color: #909399; margin-top: 4px">购买后是否允许单独追加购买</div>
</el-form-item>
<el-form-item label="用户组优惠">
<el-switch v-model="paramForm.use_user_group_discount" active-text="允许" inactive-text="不允许" />
<div style="font-size: 12px; color: #909399; margin-top: 4px">是否允许使用用户组优惠</div>
</el-form-item>
<el-form-item label="用户优惠">
<el-switch v-model="paramForm.use_user_discount" active-text="允许" inactive-text="不允许" />
<div style="font-size: 12px; color: #909399; margin-top: 4px">是否允许使用用户优惠代金券与优惠码</div>
</el-form-item>
<template v-if="paramForm.arg_type === 'number'">
<el-divider content-position="left">数值参数配置</el-divider>
<el-form-item label="步进值" prop="arg_step">
@@ -1089,7 +1102,6 @@ import {
disablePlanFixedPrice,
enablePlanFixedPrice
} from '@/api/admin/product'
import { getFileDetail } from '@/api/admin/file'
import AvatarSelector from '@/components/admin/AvatarSelector.vue'
// Tab切换
@@ -1161,24 +1173,6 @@ const expandedGroups = ref(new Set()) // 记录展开的分组ID
const loadedProductGroups = ref(new Set()) // 记录已加载商品的分组ID,避免重复请求
const groupProductsMap = ref(new Map()) // 按分组ID存储商品列表 Map<groupId, product[]>
// 文件URL缓存 Map<fileId, url>
const fileUrlCache = ref(new Map())
// 获取文件URL(带缓存)
const getFileUrl = (fileId) => {
if (!fileId) return ''
const cached = fileUrlCache.value.get(fileId)
if (cached) return cached
// 异步加载
getFileDetail({ file_id: fileId }).then(res => {
if (res.data.code === 200 && res.data.data?.url) {
const newCache = new Map(fileUrlCache.value)
newCache.set(fileId, res.data.data.url)
fileUrlCache.value = newCache
}
}).catch(() => {})
return '' // 先返回空,加载完成后会响应式更新
}
// 商品表单
const productForm = reactive({
@@ -2317,7 +2311,10 @@ const paramForm = reactive({
must: false,
arg_step: 1,
arg_min: 0,
arg_max: 100
arg_max: 100,
user_add: false,
use_user_group_discount: false,
use_user_discount: false
})
const paramRules = {
arg_name: [{ required: true, message: '请输入参数名称', trigger: 'blur' }],
@@ -2384,14 +2381,14 @@ const getRangeTypeText = (type) => {
const handleAddParameter = () => {
paramFormType.value = 'add'
paramFormDialogVisible.value = true
Object.assign(paramForm, { arg_id: undefined, arg_name: '', arg_type: 'string', must: false, arg_step: 1, arg_min: 0, arg_max: 100 })
Object.assign(paramForm, { arg_id: undefined, arg_name: '', arg_type: 'string', must: false, arg_step: 1, arg_min: 0, arg_max: 100, user_add: false, use_user_group_discount: false, use_user_discount: false })
nextTick(() => { paramFormRef.value?.resetFields() })
}
const handleEditParameter = (row) => {
paramFormType.value = 'edit'
paramFormDialogVisible.value = true
Object.assign(paramForm, { arg_id: row.id, arg_name: row.name, arg_type: row.type, must: row.must || false, arg_step: row.step || 1, arg_min: row.min || 0, arg_max: row.max || 100 })
Object.assign(paramForm, { arg_id: row.id, arg_name: row.name, arg_type: row.type, must: row.must || false, arg_step: row.step || 1, arg_min: row.min || 0, arg_max: row.max || 100, user_add: row.userAdd ?? row.user_add ?? false, use_user_group_discount: row.useUserGroupDiscount ?? row.use_user_group_discount ?? false, use_user_discount: row.useUserDiscount ?? row.use_user_discount ?? false })
}
const handleDeleteParameter = (row) => {
@@ -2409,7 +2406,7 @@ const submitParamForm = () => {
paramFormRef.value?.validate(async (valid) => {
if (valid) {
try {
const submitData = { good_id: Number(currentProductId.value), arg_name: paramForm.arg_name, arg_type: paramForm.arg_type, must: paramForm.must === true }
const submitData = { good_id: Number(currentProductId.value), arg_name: paramForm.arg_name, arg_type: paramForm.arg_type, must: paramForm.must === true, user_add: paramForm.user_add === true, use_user_group_discount: paramForm.use_user_group_discount === true, use_user_discount: paramForm.use_user_discount === true }
if (paramForm.arg_type === 'number') {
submitData.arg_step = Number(paramForm.arg_step)
submitData.arg_min = Number(paramForm.arg_min)