feat: 邮箱平台管理与商品购买限制 - 新增邮箱平台主控服务管理(页面/API/路由/菜单) - 商品与套餐表单新增max_per_user单用户购买限制 - 邮件主控控制台跳转改为/ui/index.html?token=
Build and Deploy Vue3 / build (push) Successful in 1m47s
Build and Deploy Vue3 / deploy (push) Successful in 37s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
shiran
2026-06-17 15:18:29 +08:00
parent 4180f73c53
commit 38c63cc451
6 changed files with 549 additions and 15 deletions
+43 -11
View File
@@ -113,6 +113,14 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="购买限制" width="90">
<template #default="{ row }">
<el-tag v-if="(row.maxPerUser || row.max_per_user) > 0" type="warning" size="small">
{{ row.maxPerUser || row.max_per_user }}
</el-tag>
<span v-else style="color:#c0c4cc;font-size:12px">不限</span>
</template>
</el-table-column>
<el-table-column label="操作" width="280" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
@@ -245,6 +253,10 @@
<el-switch v-model="productForm.sold_out" active-text="已售罄" inactive-text="正常" active-color="#f56c6c" />
<div class="form-tip">开启后用户端无法选购该商品KVM 商品可由后台定时任务根据 IP 资源自动标记</div>
</el-form-item>
<el-form-item label="购买限制" prop="max_per_user">
<el-input-number v-model="productForm.max_per_user" :min="0" placeholder="单用户最大购买数量" style="width: 100%" />
<div class="form-tip">限制单个用户对该商品的最大购买数量0 表示不限制</div>
</el-form-item>
<el-form-item label="商品价格" prop="price">
<div class="unit-input-row">
<el-input-number v-model="productForm.price" :min="0" :precision="2" :step="0.01" placeholder="请输入价格(元)" style="flex:1" />
@@ -572,6 +584,14 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="购买限制" width="90">
<template #default="{ row }">
<el-tag v-if="(row.maxPerUser || row.max_per_user) > 0" type="warning" size="small">
{{ row.maxPerUser || row.max_per_user }}
</el-tag>
<span v-else style="color:#c0c4cc;font-size:12px">不限</span>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="handleEditPlan(row)">编辑</el-button>
@@ -799,6 +819,10 @@
<el-switch v-model="planForm.can_update" active-text="允许" inactive-text="不允许" />
<div class="form-tip">控制用户是否可以升级到此套餐</div>
</el-form-item>
<el-form-item label="购买限制" prop="max_per_user">
<el-input-number v-model="planForm.max_per_user" :min="0" placeholder="单用户最大购买数量" style="width: 100%" />
<div class="form-tip">限制单个用户对该套餐的最大购买数量0 表示不限制</div>
</el-form-item>
</el-form>
</div>
<template #footer>
@@ -969,7 +993,8 @@ const productForm = reactive({
recommend_rebate: 0,
arg_type: 'all', // 商品参数类型 all/plan/customize
attribution_id: '', // 归属项ID
sold_out: false
sold_out: false,
max_per_user: 0
})
const productRules = {
@@ -1248,7 +1273,8 @@ const handleAdd = () => {
expire_time: 0,
recommend: false,
recommend_rebate: 0,
arg_type: 'all'
arg_type: 'all',
max_per_user: 0
})
productFormRef.value?.resetFields()
}
@@ -1274,7 +1300,8 @@ const handleEdit = (row) => {
recommend: row.recommend,
recommend_rebate: row.recommendRebate,
arg_type: row.argType || 'all',
sold_out: !!row.soldOut
sold_out: !!row.soldOut,
max_per_user: row.maxPerUser ?? row.max_per_user ?? 0
})
coverPreviewUrl.value = row.cover || ''
}
@@ -1370,7 +1397,8 @@ const submitForm = () => {
expire_time: productForm.expire_time || 0,
recommend_rebate: productForm.recommend_rebate || 0,
arg_type: productForm.arg_type || 'all', // 商品参数类型
attribution_id: productForm.attribution_id || '' // 归属项ID
attribution_id: productForm.attribution_id || '', // 归属项ID
max_per_user: Number(productForm.max_per_user) || 0
}
console.log('提交的数据:', submitData) // 调试日志
@@ -1777,7 +1805,8 @@ const planForm = reactive({
index: 0,
disable: false,
show_home: false,
can_update: false
can_update: false,
max_per_user: 0
})
const planFormRules = {
@@ -2279,7 +2308,8 @@ const handleAddPlan = async () => {
index: 0,
disable: false,
show_home: false,
can_update: false
can_update: false,
max_per_user: 0
})
planFormDialogVisible.value = true
@@ -2331,12 +2361,13 @@ const handleEditPlan = async (row) => {
extra_arg_ids: extraArgIdsArray.join(','),
extra_arg_ids_array: extraArgIdsArray,
inventory: data.inventory || 0,
fixed_price: ((data.fixedPrice || data.fixed_price || 0) / 100).toFixed(2) * 1, // 分转元
enable_fixed_price: !!(data.enableFixedPrice || data.enable_fixed_price), // 转为布尔值
fixed_price: ((data.fixedPrice || data.fixed_price || 0) / 100).toFixed(2) * 1,
enable_fixed_price: !!(data.enableFixedPrice || data.enable_fixed_price),
index: data.index || 0,
disable: data.disable || false,
show_home: !!(data.showHome || data.show_home),
can_update: !!(data.canUpdate || data.can_update)
can_update: !!(data.canUpdate || data.can_update),
max_per_user: data.maxPerUser ?? data.max_per_user ?? 0
})
// 从已有的args初始化选择状态(包括额外参数)
@@ -2434,10 +2465,11 @@ const submitPlanForm = () => {
args: planForm.args || '',
extra_arg_ids: extraArgIdsStr || planForm.extra_arg_ids || '',
inventory: Number(planForm.inventory) || 0,
fixed_price: Math.round(Number(planForm.fixed_price) * 100) || 0, // 元转分
fixed_price: Math.round(Number(planForm.fixed_price) * 100) || 0,
index: Number(planForm.index) || 0,
show_home: planForm.show_home === true,
can_update: planForm.can_update === true
can_update: planForm.can_update === true,
max_per_user: Number(planForm.max_per_user) || 0
}
// 只有创建时才传递 enable_fixed_price
@@ -113,6 +113,9 @@
<el-tag :type="row.canUpdate || row.can_update ? 'success' : 'info'" size="small" effect="plain">
{{ row.canUpdate || row.can_update ? '允许升级' : '禁止升级' }}
</el-tag>
<el-tag v-if="(row.maxPerUser || row.max_per_user) > 0" type="warning" size="small" effect="plain">
限购{{ row.maxPerUser || row.max_per_user }}
</el-tag>
</div>
<!-- 说明 -->
@@ -288,6 +291,10 @@
<el-switch v-model="planForm.can_update" active-text="允许" inactive-text="不允许" />
<div class="form-tip">控制用户是否可以升级到此套餐</div>
</el-form-item>
<el-form-item label="购买限制" prop="max_per_user">
<el-input-number v-model="planForm.max_per_user" :min="0" placeholder="单用户最大购买数量" style="width: 100%" />
<div class="form-tip">限制单个用户对该套餐的最大购买数量0 表示不限制</div>
</el-form-item>
</el-form>
</div>
<template #footer>
@@ -378,7 +385,8 @@ const planForm = reactive({
index: 0,
disable: false,
show_home: false,
can_update: false
can_update: false,
max_per_user: 0
})
const planFormRules = {
name: [{ required: true, message: '请输入套餐名称', trigger: 'blur' }]
@@ -758,7 +766,7 @@ const handleAddPlan = async () => {
displayUnits[spec.id] = getParamDefaultUnit(spec)
}
}
Object.assign(planForm, { plan_id: undefined, name: '', note: '', args: '', extra_arg_ids: '', extra_arg_ids_array: [], inventory: 0, fixed_price: 0, enable_fixed_price: false, index: 0, disable: false, show_home: false, can_update: false })
Object.assign(planForm, { plan_id: undefined, name: '', note: '', args: '', extra_arg_ids: '', extra_arg_ids_array: [], inventory: 0, fixed_price: 0, enable_fixed_price: false, index: 0, disable: false, show_home: false, can_update: false, max_per_user: 0 })
planFormDialogVisible.value = true
nextTick(() => { planFormRef.value?.resetFields() })
}
@@ -785,7 +793,8 @@ const handleEditPlan = async (row) => {
inventory: data.inventory || 0, fixed_price: ((data.fixedPrice || data.fixed_price || 0) / 100).toFixed(2) * 1,
enable_fixed_price: !!(data.enableFixedPrice || data.enable_fixed_price),
index: data.index || 0, disable: data.disable || false, show_home: !!(data.showHome || data.show_home),
can_update: !!(data.canUpdate || data.can_update)
can_update: !!(data.canUpdate || data.can_update),
max_per_user: data.maxPerUser ?? data.max_per_user ?? 0
})
initSelectedArgsFromJson(data.args, extraArgIdsArray)
planFormDialogVisible.value = true
@@ -842,7 +851,8 @@ const submitPlanForm = () => {
args: planForm.args || '', extra_arg_ids: extraArgIdsStr || planForm.extra_arg_ids || '',
inventory: Number(planForm.inventory) || 0, fixed_price: Math.round(Number(planForm.fixed_price) * 100) || 0,
index: Number(planForm.index) || 0, show_home: planForm.show_home === true,
can_update: planForm.can_update === true
can_update: planForm.can_update === true,
max_per_user: Number(planForm.max_per_user) || 0
}
if (planFormType.value === 'add') submitData.enable_fixed_price = planForm.enable_fixed_price === true
let res