diff --git a/src/api/admin/mailService.js b/src/api/admin/mailService.js new file mode 100644 index 0000000..12ac135 --- /dev/null +++ b/src/api/admin/mailService.js @@ -0,0 +1,43 @@ +import { http2 } from '@/utils/request.js' + +const formHeaders = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } + +// ========== 邮件主控服务 ========== + +export const getMailServiceList = (params) => { + return http2.get('/api/v1/admin/server/mail_service/list', { params }) +} + +export const getMailServiceDetail = (params) => { + return http2.get('/api/v1/admin/server/mail_service/detail', { params }) +} + +export const createMailService = (data) => { + return http2.post('/api/v1/admin/server/mail_service/create', data, formHeaders) +} + +export const updateMailService = (data) => { + return http2.post('/api/v1/admin/server/mail_service/update', data, formHeaders) +} + +export const deleteMailService = (data) => { + return http2.delete('/api/v1/admin/server/mail_service/delete', { data, ...formHeaders }) +} + +// ========== 邮件额度商品 ========== + +export const getMailGoodsList = (params) => { + return http2.get('/api/v1/admin/server/mail_service/goods/list', { params }) +} + +export const createMailGoods = (data) => { + return http2.post('/api/v1/admin/server/mail_service/goods/create', data, formHeaders) +} + +export const updateMailGoods = (data) => { + return http2.post('/api/v1/admin/server/mail_service/goods/update', data, formHeaders) +} + +export const deleteMailGoods = (data) => { + return http2.delete('/api/v1/admin/server/mail_service/goods/delete', { data, ...formHeaders }) +} diff --git a/src/config/menus.js b/src/config/menus.js index 76ce3db..a404860 100644 --- a/src/config/menus.js +++ b/src/config/menus.js @@ -172,6 +172,17 @@ export const menus = [ } ] }, + { + path: '/mail', + title: '邮箱平台管理', + icon: 'Message', + children: [ + { + path: '/mail/service', + title: '主控服务管理' + } + ] + }, { title: '虚拟化平台管理', icon: 'Platform', diff --git a/src/router/index.js b/src/router/index.js index 3f85106..8271982 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -645,6 +645,24 @@ const routes = [ } ] }, + // 邮箱平台管理路由 + { + path: 'mail', + name: 'Mail', + meta: { + title: '邮箱平台管理', + icon: 'Message' + }, + redirect: '/mail/service', + children: [ + { + path: 'service', + name: 'MailService', + component: () => import('../views/mail/MailService.vue'), + meta: { title: '主控服务管理' } + } + ] + }, // 站点审计路由 { path: 'audit', diff --git a/src/views/mail/MailService.vue b/src/views/mail/MailService.vue new file mode 100644 index 0000000..c330a0b --- /dev/null +++ b/src/views/mail/MailService.vue @@ -0,0 +1,420 @@ + + + + + + + + + + + + + 邮件主控服务管理 + 管理邮件平台的主控服务实例,每个服务对应一个 mail-server 节点 + + + + 新增服务 + + + + + + + + + 搜索 + 重置 + + + + + + + + + + + + + {{ row.name }} + + + + + + {{ row.note || '-' }} + + + + + {{ row.host }} + + + + + + {{ row.smtpHost }}:{{ row.smtpPort || '-' }} + TLS + 无TLS + + - + + + + + + {{ maskToken(row.serviceToken) }} + {{ row.serviceToken }} + + {{ row._showToken ? '隐藏' : '显示' }} + + + + + + {{ formatTime(row.createdAt || row.CreatedAt) }} + + + + 控制台 + 编辑 + + + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + SMTP 配置(可选) + + + + + + + + + + + + 取消 + 确定 + + + + + + + + diff --git a/src/views/product/ProductGroup.vue b/src/views/product/ProductGroup.vue index 84cd6e7..6c0e8d0 100644 --- a/src/views/product/ProductGroup.vue +++ b/src/views/product/ProductGroup.vue @@ -567,6 +567,9 @@ + + + - - - 价格与销售 - - - - 商品价格(元) - - - - - - - - - 有效期(天,0 为永久) - - - - - - - - - - - - - - - - 启用后,用户购买/续费/升级此商品前须完成实名认证 - - - + + + + + + + 商品价格(元) + + + + + + + + + 有效期(天,0 为永久) + + + + + + + + + + + + + + + + 启用后,用户购买/续费/升级此商品前须完成实名认证 + + + - - - 库存管理 - - - - - - - - - + + + + + + + + + + + 开启后用户端将无法选购该商品 + + + + 限制单用户最大购买数量,0 表示不限制 + + + - - - 推荐与参数 - - - - - - - 推荐返还(%) - - - - - + + + + + + + + 推荐返还(%) + + + + + + 用户购买后是否发送通知给管理员 + + + + @@ -288,6 +291,10 @@ 控制用户是否可以升级到此套餐 + + + 限制单个用户对该套餐的最大购买数量,0 表示不限制 + @@ -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' }] @@ -546,11 +554,11 @@ const findMatchingNumberAttr = (spec, numValue) => { if (!spec.attrs || spec.attrs.length === 0) return null const sortedAttrs = [...spec.attrs].sort((a, b) => (a.index || 0) - (b.index || 0)) for (const attr of sortedAttrs) { - const phase = attr.phase || 0 + const rangeVal = attr.range ?? attr.phase ?? 0 const rangeType = attr.rangeType || 'before' - if (rangeType === 'before' && numValue <= phase) return attr - else if (rangeType === 'after' && numValue >= phase) return attr - else if (rangeType === 'equal' && numValue === phase) return attr + if (rangeType === 'before' && numValue <= rangeVal) return attr + else if (rangeType === 'after' && numValue >= rangeVal) return attr + else if (rangeType === 'equal' && numValue === rangeVal) return attr } return sortedAttrs[sortedAttrs.length - 1] } @@ -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
管理邮件平台的主控服务实例,每个服务对应一个 mail-server 节点