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 @@
Step 01 请对实训数据表中的各项指标,按照市场规模、市场潜力、竞争强度、运营难度 4 个类别进行分类。 Step 02 根据案例信息,为各二级指标设定权重。系统将自动汇总一级指标权重。 第 1 步分类结果已自动带入。请填写四类一级指标权重和各维度内二级指标权重,系统将自动计算对应总权重。 请先完成并保存第 1 步的指标分类,再填写本步骤。 Step 03 请根据数据表及第 2 步权重,使用 Min-Max 归一法计算各二级指标得分,再采用加权评分法填写产品综合得分。 通过 Min-Max 归一法计算各二级指标得分,采用加权评分法计算产品品类综合得分。 请先完成并保存第 2 步的指标权重设定,再填写本步骤。 1. Excel/WPS 表格,自行操作计算,可参考《实训操作说明》完成。 2. 下载 Excel 模板,填写关键数据,完成指标计算。 3. 导出实训数据表,上传到通用 AI 大模型,参考提示词完成计算。 请根据数据表及各指标权重设定表,通过 Min-Max 归一法计算各二级指标得分,采用加权评分法计算综合得分。 1. Excel/WPS 表格,自行操作计算,可参考《实训操作说明》完成。 2. 下载自动化模板,填写案例数据及第 2 步权重,完成指标计算。 3. 导出实训数据表,上传到通用 AI 大模型,参考提示词完成计算。 先按 Min-Max 归一法和加权评分法计算品类综合得分;如案例资料提供三段式时间权重,则分别按第 1–20 天、第 21–40 天、第 41–60 天的权重计算最终得分。 支持 PNG、JPG、WEBP 格式,文件不超过 5MB。 1. 使用 Excel/WPS 表格计算三类产品的平均得分、最高分和最低分,并生成图表。 2. 下载 Excel 模板,填写关键数据,完成指标计算和可视化。 3. 导出实训数据表,上传到通用 AI 大模型,参考提示词生成可视化结果。 1. 使用 Excel/WPS 表格计算三类产品的平均得分、最高分和最低分,并生成图表。 2. 下载自动化模板,填写关键数据,完成指标计算和可视化。 3. 导出实训数据表,上传到通用 AI 大模型,参考提示词完成计算与图表生成。 请根据数据表及各指标权重设定表,使用 Min-Max 归一法与加权评分法计算产品综合得分,并汇总平均分、最高分和最低分。 Step 05 结合本实训提供的智能健身镜、智能卫浴镜和智能美妆镜三类产品评论数据,分析用户的痛点及未被满足的需求。 请先完成并保存第 4 步的数据可视化结果,再填写本步骤。 1. 自行根据评价数据计算好评率,阅读评论后分析用户痛点及未被满足的需求。 2. 导出评论数据表,上传到通用 AI 大模型,通过文本分析完成计算与提炼。 提示词:请计算 Excel 表中产品评论数据的好评率,提炼用户痛点和未被满足的需求。 Step 06 结合前面的数据分析,请说明应该进行开发什么产品,具体原因是什么。 请先完成并保存第 5 步的用户需求洞察,再填写本步骤。{{ trainingSteps[0] }}
序号 指标 分类 {{ index + 1 }} {{ item.name }} 序号 指标 分类 {{ index + 1 }} {{ item.name }} {{ stepOneCheckResults[item.id].message }} {{ trainingSteps[1] }}
一级指标 二级指标 权重(%) {{ item.group }}({{ groupWeight(item.group) }}%) {{ item.name }} % 一级指标 权重占比(%) 二级指标 维度内权重(%) 对应总权重 {{ group.name }} %建议 {{ group.suggestion }} {{ item.name }} %建议 {{ item.suggestion }} {{ formatWeight(correspondingWeight(item)) }}% {{ trainingSteps[2] }}
序号 产品品类 综合得分 {{ index + 1 }} {{ product.name }} 分 推荐工具及方法如下,自行选择:
推荐工具及方法如下,自行选择:
推荐工具及方法如下,自行选择:
推荐工具及方法如下,自行选择:
{{ trainingSteps[4] }}
品类 好评率(%) 用户痛点 未被满足的需求 {{ product.name }} % 可参考以下方法:
{{ trainingSteps[5] }}
第 {{ currentStep }} 步内容待配置
已保存的步骤可从上方步骤栏返回查看。