|
|
|
|
@ -160,35 +160,70 @@
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section v-show="currentStep === 4" class="step-panel">
|
|
|
|
|
<div class="product-plan-layout">
|
|
|
|
|
<div class="product-plan-form">
|
|
|
|
|
<p class="product-plan-desc">结合上述分析,请说明如果你来开发相关的产品,计划设计那些核心功能,预计产品的价格区间是多少。</p>
|
|
|
|
|
|
|
|
|
|
<div class="product-plan-label">计划设计的核心功能:</div>
|
|
|
|
|
<textarea
|
|
|
|
|
v-for="(_, index) in form.coreFunctions"
|
|
|
|
|
:key="index"
|
|
|
|
|
v-model="form.coreFunctions[index]"
|
|
|
|
|
class="core-function-input"
|
|
|
|
|
:placeholder="`核心功能${index + 1}`"
|
|
|
|
|
/>
|
|
|
|
|
<p class="task-desc task-desc--compact">
|
|
|
|
|
开发团队在用户分层后,已访谈了 3 位购买过相关产品的重点用户,并撰写了访谈记录。请根据访谈记录,从行为特征、需求痛点、消费心理三个维度构建重点用户画像。
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<div class="product-plan-label product-plan-label--price">预计产品价格区间(元):</div>
|
|
|
|
|
<div class="price-range-row">
|
|
|
|
|
<input v-model="form.priceMin" class="price-input" />
|
|
|
|
|
<span></span>
|
|
|
|
|
<input v-model="form.priceMax" class="price-input" />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="interview-tabs" aria-label="重点用户访谈记录">
|
|
|
|
|
<button
|
|
|
|
|
v-for="(record, index) in form.keyUserRecords"
|
|
|
|
|
:key="record.code"
|
|
|
|
|
type="button"
|
|
|
|
|
:class="['interview-tab', { active: activeInterviewIndex === index }]"
|
|
|
|
|
@click="handleInterviewClick(index)"
|
|
|
|
|
>
|
|
|
|
|
{{ record.title }}({{ record.code }})
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<label class="analysis-conclusion-block">
|
|
|
|
|
<span>分析结论:</span>
|
|
|
|
|
<textarea v-model="form.conclusion" class="analysis-conclusion-input" />
|
|
|
|
|
</label>
|
|
|
|
|
<el-dialog v-model="interviewDialogVisible" :title="`${currentInterviewTranscript.code} 访谈记录`" width="min(820px, 92vw)" append-to-body>
|
|
|
|
|
<div class="interview-dialog-tabs" aria-label="切换访谈记录">
|
|
|
|
|
<button
|
|
|
|
|
v-for="(record, index) in form.keyUserRecords"
|
|
|
|
|
:key="record.code"
|
|
|
|
|
type="button"
|
|
|
|
|
:class="{ active: activeInterviewIndex === index }"
|
|
|
|
|
@click="activeInterviewIndex = index"
|
|
|
|
|
>
|
|
|
|
|
{{ record.code }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="product-reference">
|
|
|
|
|
<img :src="productReferenceImage" alt="小米有品三雄极光天域声光伴眠吸顶灯参考图" />
|
|
|
|
|
<div class="interview-dialog-body interview-dialog-body--panel">
|
|
|
|
|
<p class="interview-line">基本属性:{{ currentInterviewTranscript.basicAttributes }}</p>
|
|
|
|
|
<p class="interview-title">访谈内容:</p>
|
|
|
|
|
<div v-for="item in currentInterviewTranscript.content" :key="item.question" class="interview-qa">
|
|
|
|
|
<p>Q:{{ item.question }}</p>
|
|
|
|
|
<p>A:{{ item.answer }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
<div class="portrait-table-wrap">
|
|
|
|
|
<section v-for="record in form.keyUserRecords" :key="record.code" class="portrait-record">
|
|
|
|
|
<div class="portrait-user portrait-user--form">
|
|
|
|
|
<label>
|
|
|
|
|
<span>用户:</span>
|
|
|
|
|
<input v-model="record.user" class="portrait-input" :placeholder="`例如:${record.code}`" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>基础属性:</span>
|
|
|
|
|
<input v-model="record.basicAttributes" class="portrait-input portrait-input--wide" placeholder="年龄、性别、职业、收入、城市等" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>标签:</span>
|
|
|
|
|
<input v-model="record.tags" class="portrait-input portrait-input--wide" placeholder="例如:高价值用户、品质偏好者" />
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="portrait-grid">
|
|
|
|
|
<div class="portrait-head">行为特征</div>
|
|
|
|
|
<div class="portrait-head">需求痛点</div>
|
|
|
|
|
<div class="portrait-head">消费心理</div>
|
|
|
|
|
<textarea v-model="record.behavior" class="portrait-cell" placeholder="请结合访谈记录归纳行为特征" />
|
|
|
|
|
<textarea v-model="record.painPoints" class="portrait-cell" placeholder="请结合访谈记录归纳需求痛点" />
|
|
|
|
|
<textarea v-model="record.psychology" class="portrait-cell" placeholder="请结合访谈记录归纳消费心理" />
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
@ -236,8 +271,7 @@ import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
|
|
|
|
|
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
|
|
|
|
|
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
|
|
|
|
|
import { getTrainingTaskByKey } from "@/api/trainingTask";
|
|
|
|
|
import { checkTargetUserProfileStepOne, checkTargetUserProfileStepThree, checkTargetUserProfileStepTwo, getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile } from "@/api/studentTrainingAnswer";
|
|
|
|
|
import productReferenceImage from "@/assets/images/target-user-profile-u1344.png";
|
|
|
|
|
import { checkTargetUserProfileStepFour, checkTargetUserProfileStepOne, checkTargetUserProfileStepThree, checkTargetUserProfileStepTwo, getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile } from "@/api/studentTrainingAnswer";
|
|
|
|
|
|
|
|
|
|
const TASK_KEY = "target-user-profile";
|
|
|
|
|
const RFM_CHARTS = [
|
|
|
|
|
@ -363,12 +397,12 @@ function createKeyUserRecords() {
|
|
|
|
|
{
|
|
|
|
|
title: "访谈记录一",
|
|
|
|
|
code: "U01",
|
|
|
|
|
user: "U01",
|
|
|
|
|
basicAttributes: "29岁,男性,职业为程序员,月收入2.8万元,居住于一线城市(上海)",
|
|
|
|
|
tags: "高压力职场人,高压力、高收入",
|
|
|
|
|
behavior: "1.工作侵占生活,作息不规律:\n2.积极寻求科技解决方案:\n3.碎片化与无缝衔接的需求:\n4.数据驱动的自我量化倾向。",
|
|
|
|
|
painPoints: "1.精神紧张与生理不适:2.现有解决方案有缺陷,药物担心依赖性,软件App体验被干扰,设备(手机)本身与睡眠环境冲突;\n3.对“智能化”有高阶需求;\n4.对专业性与有效性的渴求。",
|
|
|
|
|
psychology: "1.功效优先的理性决策者:\n2.为“效率”与“便捷”付费的意愿强烈:3.“科技感”与“品牌调性”是重要加分项:\n4.投资健康的长期主义者。",
|
|
|
|
|
user: "",
|
|
|
|
|
basicAttributes: "",
|
|
|
|
|
tags: "",
|
|
|
|
|
behavior: "",
|
|
|
|
|
painPoints: "",
|
|
|
|
|
psychology: "",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "访谈记录二",
|
|
|
|
|
@ -776,6 +810,9 @@ async function persist(status = "IN_PROGRESS") {
|
|
|
|
|
if (status !== "RESET" && (currentStep.value === 3 || status === "SUBMITTED")) {
|
|
|
|
|
await checkTargetUserProfileStepThree({ insights: form.value.rfmInsights });
|
|
|
|
|
}
|
|
|
|
|
if (status !== "RESET" && (currentStep.value === 4 || status === "SUBMITTED")) {
|
|
|
|
|
await checkTargetUserProfileStepFour({ records: form.value.keyUserRecords });
|
|
|
|
|
}
|
|
|
|
|
await saveStudentTrainingAnswer(TASK_KEY, buildAnswerPayload(status));
|
|
|
|
|
proxy?.$modal?.msgSuccess(status === "SUBMITTED" ? "提交成功" : "保存成功");
|
|
|
|
|
} finally {
|
|
|
|
|
@ -1503,6 +1540,36 @@ function exportOutcome() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.interview-dialog-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 0 20px 14px;
|
|
|
|
|
border-bottom: 1px solid #e2ebf0;
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
min-width: 58px;
|
|
|
|
|
height: 30px;
|
|
|
|
|
border: 1px solid #bdd6e2;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
color: #466477;
|
|
|
|
|
background: #f7fbfd;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
border-color: #138ac0;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
background: #138ac0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.interview-dialog-body--panel {
|
|
|
|
|
max-height: 58vh;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
color: #34495a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-insight-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 92px;
|
|
|
|
|
|