feat: 邮箱平台管理与商品购买限制 - 新增邮箱平台主控服务管理(页面/API/路由/菜单) - 商品与套餐表单新增max_per_user单用户购买限制 - 邮件主控控制台跳转改为/ui/index.html?token=
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user