feat: 高考志愿与定价分销链路更新(管理端/API/小程序)
- 新增高考志愿核心服务、模型、控制器与迁移脚本 - 同步管理端定价/分销/设置与小程序入口、历史、测试选择和支付逻辑 - 补充相关开发文档 Made-with: Cursor
This commit is contained in:
@@ -579,6 +579,7 @@ const testTypeItems = [
|
||||
{ key: 'sbti', label: 'SBTI 测试' },
|
||||
{ key: 'disc', label: 'DISC 测试' },
|
||||
{ key: 'pdp', label: 'PDP 测试' },
|
||||
{ key: 'gaokao', label: '高考志愿报告' },
|
||||
]
|
||||
type TestSetting = { enabled: boolean; commissionType: 'ratio' | 'amount'; commissionRate: number; commissionAmount: number; noPayment: boolean }
|
||||
const makeDefaultTs = (): TestSetting => ({ enabled: true, commissionType: 'ratio', commissionRate: 90, commissionAmount: 0, noPayment: false })
|
||||
@@ -588,6 +589,7 @@ const testSettings = reactive<Record<string, TestSetting>>({
|
||||
sbti: makeDefaultTs(),
|
||||
disc: makeDefaultTs(),
|
||||
pdp: makeDefaultTs(),
|
||||
gaokao: makeDefaultTs(),
|
||||
})
|
||||
|
||||
// 加载数据概览
|
||||
@@ -1175,7 +1177,10 @@ onMounted(() => {
|
||||
.card-desc { font-size: 12px; color: #6b7280; margin: 0 0 20px; }
|
||||
|
||||
.ts-grid {
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
|
||||
display: grid;
|
||||
/* 一行最多 4 个测试佣金卡片 */
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
.ts-card {
|
||||
background: #f9fafb; border-radius: 8px; padding: 16px; display: flex; flex-direction: column; gap: 12px;
|
||||
.ts-head {
|
||||
@@ -1270,5 +1275,10 @@ onMounted(() => {
|
||||
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.charts-grid { grid-template-columns: 1fr; }
|
||||
.settings-card .form-grid.three-cols { grid-template-columns: 1fr; }
|
||||
.settings-card .ts-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.settings-card .ts-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
<label>SBTI测试价格 (元/次)</label>
|
||||
<el-input-number v-model="personal.sbti" :min="0" :precision="2" :controls="false" class="w-full" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label>高考志愿报告 (元/次)</label>
|
||||
<el-input-number v-model="personal.gaokao" :min="0" :precision="2" :controls="false" class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isUsingSuperAdminPersonalConfig" class="notice-box">
|
||||
<el-icon class="notice-icon"><InfoFilled /></el-icon>
|
||||
@@ -83,6 +87,10 @@
|
||||
<label>SBTI测试价格 (元/次)</label>
|
||||
<el-input-number v-model="enterprise.sbti" :min="0" :precision="2" :controls="false" class="w-full" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label>高考志愿报告 (元/次)</label>
|
||||
<el-input-number v-model="enterprise.gaokao" :min="0" :precision="2" :controls="false" class="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isUsingSuperAdminEnterpriseConfig" class="notice-box">
|
||||
<el-icon class="notice-icon"><InfoFilled /></el-icon>
|
||||
@@ -115,8 +123,8 @@ const tabs = [
|
||||
]
|
||||
const activeTab = ref('personal')
|
||||
|
||||
const personal = reactive({ face: 0, mbti: 0, disc: 0, pdp: 0, sbti: 0 })
|
||||
const enterprise = reactive({ face: 0, mbti: 0, disc: 0, pdp: 0, sbti: 0 })
|
||||
const personal = reactive({ face: 0, mbti: 0, disc: 0, pdp: 0, sbti: 0, gaokao: 0 })
|
||||
const enterprise = reactive({ face: 0, mbti: 0, disc: 0, pdp: 0, sbti: 0, gaokao: 0 })
|
||||
|
||||
const loading = ref(false)
|
||||
const isUsingSuperAdminPersonalConfig = ref(false)
|
||||
@@ -170,7 +178,8 @@ const saveEnterprise = async () => {
|
||||
mbti: enterprise.mbti,
|
||||
disc: enterprise.disc,
|
||||
pdp: enterprise.pdp,
|
||||
sbti: enterprise.sbti
|
||||
sbti: enterprise.sbti,
|
||||
gaokao: enterprise.gaokao
|
||||
}
|
||||
})
|
||||
if (response.code === 200) {
|
||||
|
||||
@@ -319,11 +319,12 @@ const permItems = [
|
||||
{ key: 'sbti', label: 'SBTI' },
|
||||
{ key: 'pdp', label: 'PDP' },
|
||||
{ key: 'disc', label: 'DISC' },
|
||||
{ key: 'gaokao', label: '高考志愿' },
|
||||
{ key: 'distribution', label: '分销推广' }
|
||||
] as const
|
||||
|
||||
const defaultAdminPermissions = () =>
|
||||
({ face: true, mbti: true, sbti: true, pdp: true, disc: true, distribution: true }) as Record<string, boolean>
|
||||
({ face: true, mbti: true, sbti: true, pdp: true, disc: true, gaokao: true, distribution: true }) as Record<string, boolean>
|
||||
|
||||
const permLoading = ref(false)
|
||||
const permSaving = ref(false)
|
||||
|
||||
@@ -397,6 +397,7 @@ const testTypeItems = [
|
||||
{ key: 'sbti', label: 'SBTI 测试' },
|
||||
{ key: 'disc', label: 'DISC 测试' },
|
||||
{ key: 'pdp', label: 'PDP 测试' },
|
||||
{ key: 'gaokao', label: '高考志愿报告' },
|
||||
]
|
||||
type TestSetting = { enabled: boolean; commissionType: 'ratio' | 'amount'; commissionRate: number; commissionAmount: number; noPayment: boolean }
|
||||
const makeDefaultTs = (): TestSetting => ({ enabled: true, commissionType: 'ratio', commissionRate: 90, commissionAmount: 0, noPayment: false })
|
||||
@@ -406,6 +407,7 @@ const testSettings = reactive<Record<string, TestSetting>>({
|
||||
sbti: makeDefaultTs(),
|
||||
disc: makeDefaultTs(),
|
||||
pdp: makeDefaultTs(),
|
||||
gaokao: makeDefaultTs(),
|
||||
})
|
||||
|
||||
// 拒绝弹窗
|
||||
@@ -703,7 +705,10 @@ onMounted(() => { loadOverview(); loadTopAgents() })
|
||||
&.full-width-card { grid-column: 1 / -1; }
|
||||
.setting-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.ts-grid {
|
||||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
|
||||
display: grid;
|
||||
/* 一行最多 4 个测试佣金卡片 */
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
.ts-card {
|
||||
background: #f9fafb; border-radius: 8px; padding: 16px; display: flex; flex-direction: column; gap: 12px;
|
||||
.ts-head {
|
||||
@@ -752,6 +757,11 @@ onMounted(() => { loadOverview(); loadTopAgents() })
|
||||
@media (max-width: 1200px) {
|
||||
.overview-section .stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.overview-section .two-cols { grid-template-columns: 1fr; }
|
||||
.settings-card .ts-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.settings-card .ts-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.page-container.is-embedded {
|
||||
|
||||
@@ -1088,10 +1088,12 @@ const permItems = [
|
||||
{ key: 'sbti', label: 'SBTI' },
|
||||
{ key: 'pdp', label: 'PDP' },
|
||||
{ key: 'disc', label: 'DISC' },
|
||||
{ key: 'gaokao', label: '高考志愿' },
|
||||
{ key: 'distribution', label: '分销' },
|
||||
]
|
||||
|
||||
const defaultPermissions = () => ({ face: true, mbti: true, sbti: true, pdp: true, disc: true, distribution: true })
|
||||
const defaultPermissions = () =>
|
||||
({ face: true, mbti: true, sbti: true, pdp: true, disc: true, gaokao: true, distribution: true })
|
||||
|
||||
/** 列表/展示:超管授权上限(兼容未返回 permissionsCeiling 的旧接口) */
|
||||
const permCeilingVal = (row: Record<string, any>, key: string) => {
|
||||
|
||||
@@ -85,6 +85,16 @@
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label>高考志愿报告 (元/次)</label>
|
||||
<el-input-number
|
||||
v-model="personal.gaokao"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="save-actions">
|
||||
<el-button type="primary" class="save-btn" @click="savePersonal">
|
||||
@@ -149,6 +159,16 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label>高考志愿报告 (元/次)</label>
|
||||
<el-input-number
|
||||
v-model="enterprise.gaokao"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item form-item--span-full">
|
||||
<label>最低充值金额 (元)</label>
|
||||
<el-input-number
|
||||
v-model="enterprise.minRecharge"
|
||||
@@ -468,7 +488,8 @@ const personal = reactive({
|
||||
mbti: 9.9,
|
||||
disc: 9.9,
|
||||
pdp: 9.9,
|
||||
sbti: 9.9
|
||||
sbti: 9.9,
|
||||
gaokao: 0
|
||||
})
|
||||
|
||||
const enterprise = reactive({
|
||||
@@ -477,6 +498,7 @@ const enterprise = reactive({
|
||||
pdp: 8.0,
|
||||
disc: 8.0,
|
||||
sbti: 8.0,
|
||||
gaokao: 0,
|
||||
minRecharge: 1000.0
|
||||
})
|
||||
|
||||
@@ -569,12 +591,12 @@ const loadPricing = async () => {
|
||||
if (response.code === 200 && response.data) {
|
||||
// 更新个人版配置
|
||||
if (response.data.personal) {
|
||||
Object.assign(personal, response.data.personal)
|
||||
Object.assign(personal, { gaokao: 0 }, response.data.personal)
|
||||
}
|
||||
|
||||
|
||||
// 更新企业版配置
|
||||
if (response.data.enterprise) {
|
||||
Object.assign(enterprise, response.data.enterprise)
|
||||
Object.assign(enterprise, { gaokao: 0, minRecharge: enterprise.minRecharge }, response.data.enterprise)
|
||||
}
|
||||
|
||||
// 更新深度服务配置(个人/企业类目)
|
||||
@@ -624,7 +646,14 @@ const savePersonal = async () => {
|
||||
try {
|
||||
const response: any = await request.put('/superadmin/pricing', {
|
||||
type: 'personal',
|
||||
config: personal
|
||||
config: {
|
||||
face: personal.face,
|
||||
mbti: personal.mbti,
|
||||
disc: personal.disc,
|
||||
pdp: personal.pdp,
|
||||
sbti: personal.sbti,
|
||||
gaokao: personal.gaokao
|
||||
}
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
@@ -648,6 +677,7 @@ const saveEnterprise = async () => {
|
||||
pdp: enterprise.pdp,
|
||||
disc: enterprise.disc,
|
||||
sbti: enterprise.sbti,
|
||||
gaokao: enterprise.gaokao,
|
||||
minRecharge: enterprise.minRecharge
|
||||
}
|
||||
})
|
||||
@@ -865,6 +895,10 @@ onMounted(() => {
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-item--span-full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
.form-item {
|
||||
|
||||
Reference in New Issue
Block a user