diff --git a/src/api/studentTrainingAnswer.js b/src/api/studentTrainingAnswer.js index 5bdf7fe..bcb5bde 100644 --- a/src/api/studentTrainingAnswer.js +++ b/src/api/studentTrainingAnswer.js @@ -628,7 +628,7 @@ export function checkRequirementsDocumentStepOne(payload) { export function checkMarketOpportunitySelectionStepOne(items) { const expected = { "search-count": "市场规模", "demand-supply-ratio": "竞争强度", "transaction-amount": "市场规模", "transaction-growth": "市场潜力", - "category-transaction-growth": "市场潜力", "online-products": "竞争强度", "online-merchants": "竞争强度", "organic-traffic-ratio": "运营难度", + "category-transaction-growth": "市场潜力", "online-products": "竞争强度", "online-merchants": "竞争强度", "organic-traffic-ratio": "运营难度", "return-rate": "运营难度", }; if (isStudentDemo()) { const list = Array.isArray(items) ? items : []; @@ -640,17 +640,23 @@ export function checkMarketOpportunitySelectionStepOne(items) { } export function checkMarketOpportunitySelectionStepTwo(payload) { - const weights = Array.isArray(payload?.weights) ? payload.weights : []; - const expectedIds = ["search-count", "transaction-amount", "transaction-growth", "category-transaction-growth", "demand-supply-ratio", "online-products", "online-merchants", "organic-traffic-ratio"]; - if (isStudentDemo()) { - const ids = new Set(weights.map((item) => item?.id)); - const total = weights.reduce((sum, item) => sum + Number(item?.weight || 0), 0); - if (weights.length !== expectedIds.length || expectedIds.some((id) => !ids.has(id)) || weights.some((item) => !Number.isInteger(Number(item?.weight)) || Number(item.weight) < 1 || Number(item.weight) > 100) || total !== 100) { - return Promise.reject(new Error("二级指标权重需为 1 至 100 的整数,且合计必须为 100%")); + const primaryWeights = Array.isArray(payload?.primaryWeights) ? payload.primaryWeights : []; + const dimensionWeights = Array.isArray(payload?.dimensionWeights) ? payload.dimensionWeights : []; + const groups = ["市场规模", "市场潜力", "竞争强度", "运营难度"]; + const expectedIds = ["search-count", "demand-supply-ratio", "transaction-amount", "transaction-growth", "category-transaction-growth", "online-products", "online-merchants", "organic-traffic-ratio", "return-rate"]; + if (isStudentDemo()) { + const primaryMap = Object.fromEntries(primaryWeights.map((item) => [item?.group, Number(item?.weight)])); + const dimensionMap = Object.fromEntries(dimensionWeights.map((item) => [item?.id, Number(item?.weight)])); + const groupsByIndicator = { "search-count": "市场规模", "transaction-amount": "市场规模", "transaction-growth": "市场潜力", "category-transaction-growth": "市场潜力", "demand-supply-ratio": "竞争强度", "online-products": "竞争强度", "online-merchants": "竞争强度", "organic-traffic-ratio": "运营难度", "return-rate": "运营难度" }; + const isWeight = (value) => Number.isInteger(value) && value >= 1 && value <= 100; + const validPrimary = primaryWeights.length === groups.length && groups.every((group) => isWeight(primaryMap[group])) && groups.reduce((sum, group) => sum + primaryMap[group], 0) === 100; + const validDimensions = dimensionWeights.length === expectedIds.length && expectedIds.every((id) => isWeight(dimensionMap[id])) && groups.every((group) => expectedIds.filter((id) => groupsByIndicator[id] === group).reduce((sum, id) => sum + dimensionMap[id], 0) === 100); + if (!validPrimary || !validDimensions) { + return Promise.reject(new Error("一级指标权重合计和各类维度内权重合计均须为 100%")); } return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); } - return request({ url: "/api/student-training-answers/market-opportunity-selection/step-2/validate", method: "post", data: { weights }, headers: { repeatSubmit: false } }); + return request({ url: "/api/student-training-answers/market-opportunity-selection/step-2/validate", method: "post", data: { primaryWeights, dimensionWeights }, headers: { repeatSubmit: false } }); } export function checkMarketOpportunitySelectionStepThree(payload) { @@ -658,8 +664,9 @@ export function checkMarketOpportunitySelectionStepThree(payload) { const expectedIds = ["smart-fitness-mirror", "smart-bathroom-mirror", "smart-beauty-mirror"]; if (isStudentDemo()) { const ids = new Set(scores.map((item) => item?.id)); - if (scores.length !== expectedIds.length || expectedIds.some((id) => !ids.has(id)) || scores.some((item) => !Number.isFinite(Number(item?.score)) || Number(item.score) < 0 || Number(item.score) > 100)) { - return Promise.reject(new Error("请为三类产品填写 0 至 100 分的综合得分")); + const isScore = (value) => Number.isFinite(Number(value)) && Number(value) >= 0 && Number(value) <= 100 && Math.round(Number(value) * 100) === Number(value) * 100; + if (scores.length !== expectedIds.length || expectedIds.some((id) => !ids.has(id)) || scores.some((item) => !isScore(item?.score))) { + return Promise.reject(new Error("请为三类产品填写 0 至 100 分、最多保留两位小数的综合得分")); } return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); } @@ -675,6 +682,30 @@ export function checkMarketOpportunitySelectionStepFour(payload) { return request({ url: "/api/student-training-answers/market-opportunity-selection/step-4/validate", method: "post", data: { visualizationUrl }, headers: { repeatSubmit: false } }); } +export function checkMarketOpportunitySelectionStepFive(payload) { + const insights = Array.isArray(payload?.insights) ? payload.insights : []; + const expectedIds = ["smart-fitness-mirror", "smart-bathroom-mirror", "smart-beauty-mirror"]; + const isRate = (value) => Number.isFinite(Number(value)) && Number(value) >= 0 && Number(value) <= 100 && Math.round(Number(value) * 100) === Number(value) * 100; + const isFilled = (value) => String(value || "").trim().length > 0; + if (isStudentDemo()) { + const ids = new Set(insights.map((item) => item?.productId)); + if (insights.length !== expectedIds.length || expectedIds.some((id) => !ids.has(id)) || insights.some((item) => !isRate(item?.favorableRate) || !isFilled(item?.painPoint) || !isFilled(item?.unmetNeed))) { + return Promise.reject(new Error("请完整填写三类产品的好评率、用户痛点和未被满足的需求;好评率为 0 至 100,最多保留两位小数")); + } + return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); + } + return request({ url: "/api/student-training-answers/market-opportunity-selection/step-5/validate", method: "post", data: { insights }, headers: { repeatSubmit: false } }); +} + +export function checkMarketOpportunitySelectionStepSix(payload) { + const developmentConclusion = String(payload?.developmentConclusion || "").trim(); + if (isStudentDemo()) { + if (!developmentConclusion) return Promise.reject(new Error("请填写建议开发的产品及具体原因")); + return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } }); + } + return request({ url: "/api/student-training-answers/market-opportunity-selection/step-6/validate", method: "post", data: { developmentConclusion }, headers: { repeatSubmit: false } }); +} + export function checkProductDevelopmentProcessStepOne(items) { if (isStudentDemo()) { const results = Array.isArray(items) diff --git a/src/views/product/market-opportunity-selection.vue b/src/views/product/market-opportunity-selection.vue index 30f4c01..46e2da7 100644 --- a/src/views/product/market-opportunity-selection.vue +++ b/src/views/product/market-opportunity-selection.vue @@ -10,23 +10,23 @@ +