|
|
|
|
|
import request from "@/utils/request";
|
|
|
|
|
|
import { getDemoAnswerKey, getStudentDemoSession, isStudentDemo } from "@/utils/studentDemo";
|
|
|
|
|
|
|
|
|
|
|
|
const newProductSurveyStepOneAnswers = {
|
|
|
|
|
|
"selling-point-1": "核心层",
|
|
|
|
|
|
"selling-point-2": "有形层",
|
|
|
|
|
|
"selling-point-3": "核心层",
|
|
|
|
|
|
"selling-point-4": "核心层",
|
|
|
|
|
|
"selling-point-5": "有形层",
|
|
|
|
|
|
"selling-point-6": "有形层",
|
|
|
|
|
|
"selling-point-7": "有形层",
|
|
|
|
|
|
"selling-point-8": "延伸层",
|
|
|
|
|
|
"selling-point-9": "延伸层",
|
|
|
|
|
|
"selling-point-10": "延伸层",
|
|
|
|
|
|
"selling-point-11": "有形层",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function demoResult(taskKey) {
|
|
|
|
|
|
try { return JSON.parse(sessionStorage.getItem(getDemoAnswerKey(taskKey)) || "null"); } catch (error) { return null; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getStudentTrainingAnswer(taskKey, params) {
|
|
|
|
|
|
if (isStudentDemo()) return Promise.resolve({ code: 200, data: demoResult(taskKey) });
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: `/api/student-training-answers/${taskKey}`,
|
|
|
|
|
|
method: "get",
|
|
|
|
|
|
params,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function saveStudentTrainingAnswer(taskKey, data) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const answer = { ...data, teachingClassId: getStudentDemoSession()?.userInfo?.demoTeachingClassId };
|
|
|
|
|
|
sessionStorage.setItem(getDemoAnswerKey(taskKey), JSON.stringify(answer));
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: answer });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: `/api/student-training-answers/${taskKey}`,
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function deleteStudentTrainingAnswer(taskKey, params) {
|
|
|
|
|
|
if (isStudentDemo()) { sessionStorage.removeItem(getDemoAnswerKey(taskKey)); return Promise.resolve({ code: 200 }); }
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: `/api/student-training-answers/${taskKey}`,
|
|
|
|
|
|
method: "delete",
|
|
|
|
|
|
params,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function uploadStudentTrainingFile(data) {
|
|
|
|
|
|
if (isStudentDemo()) return Promise.reject(new Error("演示模式不上传文件"));
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/common/upload",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data,
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
"Content-Type": "multipart/form-data",
|
|
|
|
|
|
repeatSubmit: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkNewProductSurveyStepOne(items) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const results = Array.isArray(items)
|
|
|
|
|
|
? items.map((item) => {
|
|
|
|
|
|
const correct = newProductSurveyStepOneAnswers[item?.id] === item?.category;
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: item?.id,
|
|
|
|
|
|
correct,
|
|
|
|
|
|
message: correct ? "分类正确" : "分类有误,请重新选择",
|
|
|
|
|
|
};
|
|
|
|
|
|
})
|
|
|
|
|
|
: [];
|
|
|
|
|
|
const correctCount = results.filter((item) => item.correct).length;
|
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
|
code: 200,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
items: results,
|
|
|
|
|
|
correctCount,
|
|
|
|
|
|
totalCount: results.length,
|
|
|
|
|
|
allCorrect: results.length === Object.keys(newProductSurveyStepOneAnswers).length && correctCount === results.length,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/new-product-survey/step-1/check",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: { items },
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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": "运营难度",
|
|
|
|
|
|
};
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const list = Array.isArray(items) ? items : [];
|
|
|
|
|
|
const incorrect = list.find((item) => expected[item?.id] !== item?.category);
|
|
|
|
|
|
if (list.length !== Object.keys(expected).length || incorrect) return Promise.reject(new Error("指标分类有误,请根据提示重新选择"));
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { correctCount: Object.keys(expected).length, totalCount: Object.keys(expected).length, allCorrect: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({ url: "/api/student-training-answers/market-opportunity-selection/step-1/check", method: "post", data: { items }, headers: { repeatSubmit: false } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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%"));
|
|
|
|
|
|
}
|
|
|
|
|
|
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 } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkMarketOpportunitySelectionStepThree(payload) {
|
|
|
|
|
|
const scores = Array.isArray(payload?.scores) ? payload.scores : [];
|
|
|
|
|
|
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 分的综合得分"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({ url: "/api/student-training-answers/market-opportunity-selection/step-3/validate", method: "post", data: { scores }, headers: { repeatSubmit: false } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkMarketOpportunitySelectionStepFour(payload) {
|
|
|
|
|
|
const visualizationUrl = String(payload?.visualizationUrl || "").trim();
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
if (!visualizationUrl) 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-4/validate", method: "post", data: { visualizationUrl }, headers: { repeatSubmit: false } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function validateNewProductSurveyStepTwoDemo(form) {
|
|
|
|
|
|
const value = form || {};
|
|
|
|
|
|
const images = Array.isArray(value.images) ? value.images : [];
|
|
|
|
|
|
if (!['京东', '天猫', '抖音电商'].includes(String(value.platform || '').trim())) {
|
|
|
|
|
|
throw new Error('请选择调查平台');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!String(value.scene || '').trim()) {
|
|
|
|
|
|
throw new Error('请选择调查场景');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value.scene === '其他场景' && !String(value.customScene || '').trim()) {
|
|
|
|
|
|
throw new Error('请填写自定义调查场景');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!String(value.keyword || '').trim()) {
|
|
|
|
|
|
throw new Error('请填写搜索关键词');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!String(value.productName || '').trim()) {
|
|
|
|
|
|
throw new Error('请填写产品名称');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (images.length < 1 || images.length > 5 || images.some((image) => !String(image?.url || '').trim())) {
|
|
|
|
|
|
throw new Error('请上传 1 至 5 张产品图片');
|
|
|
|
|
|
}
|
|
|
|
|
|
return { valid: true, message: '校验通过' };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkNewProductSurveyStepTwo(form) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: validateNewProductSurveyStepTwoDemo(form) });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: '/api/student-training-answers/new-product-survey/step-2/validate',
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data: { ...form, imageUrls: (form?.images || []).map((image) => image?.url) },
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function validateNewProductSurveyStepThreeDemo(payload) {
|
|
|
|
|
|
const expectedDimensions = ["核心层", "有形层", "延伸层"];
|
|
|
|
|
|
const rows = Array.isArray(payload?.rows) ? payload.rows : [];
|
|
|
|
|
|
if (rows.length !== expectedDimensions.length) {
|
|
|
|
|
|
throw new Error("请完成核心层、有形层、延伸层分析");
|
|
|
|
|
|
}
|
|
|
|
|
|
const dimensions = rows.map((row) => String(row?.dimension || "").trim());
|
|
|
|
|
|
if (new Set(dimensions).size !== expectedDimensions.length || expectedDimensions.some((dimension) => !dimensions.includes(dimension))) {
|
|
|
|
|
|
throw new Error("分析维度不完整或重复");
|
|
|
|
|
|
}
|
|
|
|
|
|
const invalidRow = rows.find((row) => {
|
|
|
|
|
|
const points = Array.isArray(row?.points) ? row.points : [];
|
|
|
|
|
|
return points.map((point) => String(point || "").trim()).filter(Boolean).length < 2;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (invalidRow) {
|
|
|
|
|
|
throw new Error(`${invalidRow.dimension || "该层"}至少填写 2 条分析要点`);
|
|
|
|
|
|
}
|
|
|
|
|
|
return { valid: true, message: "校验通过" };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkNewProductSurveyStepThree(payload) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: validateNewProductSurveyStepThreeDemo(payload) });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/new-product-survey/step-3/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: payload,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function validateNewProductSurveyStepFourDemo(form) {
|
|
|
|
|
|
const value = form || {};
|
|
|
|
|
|
const fields = [
|
|
|
|
|
|
["coreValue", "核心层"],
|
|
|
|
|
|
["tangibleValue", "有形层"],
|
|
|
|
|
|
["extendedValue", "延伸层"],
|
|
|
|
|
|
];
|
|
|
|
|
|
for (const [field, label] of fields) {
|
|
|
|
|
|
if (!String(value[field] || "").trim()) {
|
|
|
|
|
|
throw new Error(`请填写${label}AI应用价值分析`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return { valid: true, message: "校验通过" };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkNewProductSurveyStepFour(form) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: validateNewProductSurveyStepFourDemo(form) });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/new-product-survey/step-4/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: form,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const productDevelopmentFactorsStepOneDimensions = ["用户需求真实性", "产品体验闭环", "生态与兼容"];
|
|
|
|
|
|
|
|
|
|
|
|
function validateProductDevelopmentFactorsStepOneDemo(payload) {
|
|
|
|
|
|
const factors = Array.isArray(payload?.factors) ? payload.factors : [];
|
|
|
|
|
|
if (factors.length !== productDevelopmentFactorsStepOneDimensions.length) {
|
|
|
|
|
|
throw new Error("请完成用户需求真实性、产品体验闭环和生态与兼容分析");
|
|
|
|
|
|
}
|
|
|
|
|
|
const dimensions = factors.map((factor) => String(factor?.dimension || "").trim());
|
|
|
|
|
|
if (new Set(dimensions).size !== productDevelopmentFactorsStepOneDimensions.length
|
|
|
|
|
|
|| productDevelopmentFactorsStepOneDimensions.some((dimension) => !dimensions.includes(dimension))) {
|
|
|
|
|
|
throw new Error("提交的分析维度不完整或重复");
|
|
|
|
|
|
}
|
|
|
|
|
|
const incompleteFactor = factors.find((factor) => !String(factor?.successAnalysis || "").trim()
|
|
|
|
|
|
|| !String(factor?.failedAnalysis || "").trim());
|
|
|
|
|
|
if (incompleteFactor) {
|
|
|
|
|
|
throw new Error(`请完成${incompleteFactor.dimension || "该项"}的成功产品和失败产品分析`);
|
|
|
|
|
|
}
|
|
|
|
|
|
return { valid: true, message: "校验通过" };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkProductDevelopmentFactorsStepOne(payload) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepOneDemo(payload) });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/product-development-factors/step-1/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: payload,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const productDevelopmentFactorsStepTwoDimensions = ["技术应用", "合规与认证"];
|
|
|
|
|
|
function validateProductDevelopmentFactorsStepTwoDemo(payload) {
|
|
|
|
|
|
const factors = Array.isArray(payload?.factors) ? payload.factors : [];
|
|
|
|
|
|
const dimensions = factors.map((factor) => String(factor?.dimension || "").trim());
|
|
|
|
|
|
if (factors.length !== 2 || new Set(dimensions).size !== 2 || productDevelopmentFactorsStepTwoDimensions.some((dimension) => !dimensions.includes(dimension))) throw new Error("请完成技术应用和合规与认证分析");
|
|
|
|
|
|
const incomplete = factors.find((factor) => !String(factor?.successAnalysis || "").trim() || !String(factor?.failedAnalysis || "").trim());
|
|
|
|
|
|
if (incomplete) throw new Error(`请完成${incomplete.dimension || "该项"}的成功产品和失败产品分析`);
|
|
|
|
|
|
return { valid: true, message: "校验通过" };
|
|
|
|
|
|
}
|
|
|
|
|
|
export function checkProductDevelopmentFactorsStepTwo(payload) {
|
|
|
|
|
|
if (isStudentDemo()) { try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepTwoDemo(payload) }); } catch (error) { return Promise.reject(error); } }
|
|
|
|
|
|
return request({ url: "/api/student-training-answers/product-development-factors/step-2/validate", method: "post", data: payload, headers: { repeatSubmit: false } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const productDevelopmentFactorsStepThreeDimensions = ["供应链与成本控制", "商业模式", "市场反馈"];
|
|
|
|
|
|
|
|
|
|
|
|
function validateProductDevelopmentFactorsStepThreeDemo(payload) {
|
|
|
|
|
|
const factors = Array.isArray(payload?.factors) ? payload.factors : [];
|
|
|
|
|
|
const dimensions = factors.map((factor) => String(factor?.dimension || "").trim());
|
|
|
|
|
|
if (factors.length !== productDevelopmentFactorsStepThreeDimensions.length
|
|
|
|
|
|
|| new Set(dimensions).size !== productDevelopmentFactorsStepThreeDimensions.length
|
|
|
|
|
|
|| productDevelopmentFactorsStepThreeDimensions.some((dimension) => !dimensions.includes(dimension))) {
|
|
|
|
|
|
throw new Error("请完成供应链与成本控制、商业模式和市场反馈分析");
|
|
|
|
|
|
}
|
|
|
|
|
|
const incomplete = factors.find((factor) => !String(factor?.successAnalysis || "").trim()
|
|
|
|
|
|
|| !String(factor?.failedAnalysis || "").trim());
|
|
|
|
|
|
if (incomplete) throw new Error(`请完成${incomplete.dimension || "该项"}的成功产品和失败产品分析`);
|
|
|
|
|
|
return { valid: true, message: "校验通过" };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkProductDevelopmentFactorsStepThree(payload) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
try { return Promise.resolve({ code: 200, data: validateProductDevelopmentFactorsStepThreeDemo(payload) }); }
|
|
|
|
|
|
catch (error) { return Promise.reject(error); }
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({ url: "/api/student-training-answers/product-development-factors/step-3/validate", method: "post", data: payload, headers: { repeatSubmit: false } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkProductDevelopmentFactorsStepFour(payload) {
|
|
|
|
|
|
const value = payload || {};
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
if (!String(value.successFactors || "").trim()) return Promise.reject(new Error("请填写产品成功的关键因素"));
|
|
|
|
|
|
if (!String(value.failedFactors || "").trim()) return Promise.reject(new Error("请填写产品失败的关键因素"));
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({ url: "/api/student-training-answers/product-development-factors/step-4/validate", method: "post", data: payload, headers: { repeatSubmit: false } });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkConsumerBehaviorStepOne(payload) {
|
|
|
|
|
|
const hypotheses = Array.isArray(payload?.hypotheses) ? payload.hypotheses : [];
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const incomplete = hypotheses.find((item) => ["targetGroup", "keyFinding", "coreHypothesis"].some((field) => !String(item?.[field] || "").trim()));
|
|
|
|
|
|
if (!hypotheses.length || incomplete) {
|
|
|
|
|
|
return Promise.reject(new Error("请完整填写目标人群、实训8关键发现和问卷核心假设"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/consumer-behavior/step-1/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: { hypotheses },
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkConsumerBehaviorStepTwo(payload) {
|
|
|
|
|
|
const needs = Array.isArray(payload?.needs) ? payload.needs : [];
|
|
|
|
|
|
const allowedTypes = ["基本型", "期望型", "兴奋型", "无差异型", "反向型"];
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const incomplete = needs.find((item) => !String(item?.feature || "").trim()
|
|
|
|
|
|
|| !allowedTypes.includes(item?.kanoType) || !String(item?.featureDirection || "").trim());
|
|
|
|
|
|
if (!needs.length || needs.length > 20 || incomplete) {
|
|
|
|
|
|
return Promise.reject(new Error("请完整填写功能、KANO类型和对应功能方向(最多20行)"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/consumer-behavior/step-2/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: { needs },
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkConsumerBehaviorStepThree(payload) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const importedDocumentUrl = String(payload?.importedDocumentUrl || "").trim();
|
|
|
|
|
|
const questions = Array.isArray(payload?.questions) ? payload.questions : [];
|
|
|
|
|
|
const requiredSections = ["基本信息", "购买行为", "需求偏好", "开放建议"];
|
|
|
|
|
|
const validQuestions = questions.filter((item) => String(item?.title || "").trim());
|
|
|
|
|
|
const complete = requiredSections.every((section) => validQuestions.some((item) => item.section === section))
|
|
|
|
|
|
&& validQuestions.some((item) => item.section === "需求偏好" && item.type === "LIKERT");
|
|
|
|
|
|
if (!importedDocumentUrl && !complete) {
|
|
|
|
|
|
return Promise.reject(new Error("请完整设计包含四个部分且含Likert五级量表的问卷,或导入Word问卷"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/consumer-behavior/step-3/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: payload,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function previewConsumerBehaviorQuestionnaireWord(data) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
return Promise.reject(new Error("演示模式不支持 Word 文档预览"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/consumer-behavior/step-3/word-preview",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data,
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
"Content-Type": "multipart/form-data",
|
|
|
|
|
|
repeatSubmit: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkConsumerBehaviorStepFour(payload) {
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const rows = Array.isArray(payload?.rows) ? payload.rows : [];
|
|
|
|
|
|
if (rows.length < 5 || rows.some((item) => !String(item?.title || "").trim() || !String(item?.content || "").trim())) {
|
|
|
|
|
|
return Promise.reject(new Error("请完整填写 5 个问卷调研报告模块的标题和内容"));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/consumer-behavior/step-4/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: payload,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkProductValuePositioningStepOne(payload) {
|
|
|
|
|
|
const rows = Array.isArray(payload?.rows) ? payload.rows : [];
|
|
|
|
|
|
const types = ["核心痛点1", "核心痛点2", "核心痛点3", "弱需求", "伪需求"];
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const valid = rows.length === types.length && rows.every((row, index) => row?.id === index + 1
|
|
|
|
|
|
&& row?.type === types[index]
|
|
|
|
|
|
&& Boolean(String(row?.description || "").trim())
|
|
|
|
|
|
&& Boolean(String(row?.scene || "").trim()));
|
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
|
code: 200,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
valid,
|
|
|
|
|
|
message: valid ? "核心痛点分类填写完整。" : "请完成 3 个核心痛点、弱需求和伪需求的描述与场景。",
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/product-value-positioning/step-1/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: { rows },
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkProductValuePositioningStepTwo(payload) {
|
|
|
|
|
|
const customerProfile = payload?.customerProfile || {};
|
|
|
|
|
|
const valueMap = payload?.valueMap || {};
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const requiredValues = [
|
|
|
|
|
|
customerProfile.jobs, customerProfile.pains, customerProfile.gains,
|
|
|
|
|
|
valueMap.products, valueMap.painRelievers, valueMap.gainCreators,
|
|
|
|
|
|
payload?.canvasUrl,
|
|
|
|
|
|
];
|
|
|
|
|
|
const valid = requiredValues.every((item) => Boolean(String(item || "").trim()));
|
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
|
code: 200,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
valid,
|
|
|
|
|
|
message: valid ? "价值主张画布填写完整。" : "请完整填写用户档案、产品价值地图并上传产品价值主张画布。",
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/product-value-positioning/step-2/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: payload,
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function checkProductValuePositioningStepThree(payload) {
|
|
|
|
|
|
const statements = Array.isArray(payload?.statements) ? payload.statements : [];
|
|
|
|
|
|
const requiredFields = ["targetUser", "productName", "productCategory", "coreValue", "differentiationReason"];
|
|
|
|
|
|
if (isStudentDemo()) {
|
|
|
|
|
|
const valid = statements.length === 2 && statements.every((statement, index) => statement?.id === index + 1
|
|
|
|
|
|
&& requiredFields.every((field) => Boolean(String(statement?.[field] || "").trim())));
|
|
|
|
|
|
return Promise.resolve({
|
|
|
|
|
|
code: 200,
|
|
|
|
|
|
data: { valid, message: valid ? "两版定位陈述填写完整。" : "请完整填写两个产品价值定位陈述版本。" },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return request({
|
|
|
|
|
|
url: "/api/student-training-answers/product-value-positioning/step-3/validate",
|
|
|
|
|
|
method: "post",
|
|
|
|
|
|
data: { statements },
|
|
|
|
|
|
headers: { repeatSubmit: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|