|
|
|
|
@ -93,64 +93,41 @@
|
|
|
|
|
|
|
|
|
|
<section v-show="currentStep === 2" class="step-panel">
|
|
|
|
|
<p class="task-desc task-desc--compact">
|
|
|
|
|
重点用户画像,开发团队已经访谈了3位重点用户,并撰写了访谈记录,请根据访谈记录,从用户的行为特征、需求痛点、消费心理等三个维度,进行重点用户画像。
|
|
|
|
|
基于步骤一的 RFM 分析结果,制作可视化图表,包括客户分层占比饼图、各客户分层平均消费对比柱状图、RFM 分数分布热力图。
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
<div v-if="interviewDialogVisible" class="interview-dialog-layer" role="dialog" aria-modal="true">
|
|
|
|
|
<div class="interview-dialog">
|
|
|
|
|
<button type="button" class="interview-dialog-close" @click="interviewDialogVisible = false">关闭</button>
|
|
|
|
|
<div class="interview-dialog-body">
|
|
|
|
|
<p class="interview-line">访谈编号:{{ currentInterviewTranscript.code }}</p>
|
|
|
|
|
<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 class="rfm-chart-grid">
|
|
|
|
|
<article v-for="chart in rfmCharts" :key="chart.id" class="rfm-chart-card">
|
|
|
|
|
<div class="rfm-chart-card__head">
|
|
|
|
|
<span class="rfm-chart-card__index">0{{ chart.index }}</span>
|
|
|
|
|
<h4>{{ chart.title }}</h4>
|
|
|
|
|
</div>
|
|
|
|
|
<input
|
|
|
|
|
:ref="(element) => setChartInputRef(chart.id, element)"
|
|
|
|
|
type="file"
|
|
|
|
|
class="file-input"
|
|
|
|
|
accept=".png,.jpg,.jpeg,.webp"
|
|
|
|
|
@change="(event) => handleChartFileChange(chart, event)"
|
|
|
|
|
/>
|
|
|
|
|
<button type="button" class="rfm-chart-upload" :disabled="uploadingChartId === chart.id" @click="triggerChartFileSelect(chart.id)">
|
|
|
|
|
<el-icon><Upload /></el-icon>
|
|
|
|
|
{{ uploadingChartId === chart.id ? "上传中…" : chart.buttonText }}
|
|
|
|
|
</button>
|
|
|
|
|
<div v-if="form.rfmCharts[chart.id]?.url" class="rfm-chart-file">
|
|
|
|
|
<img :src="form.rfmCharts[chart.id].url" :alt="`${chart.title}缩略图`" @click="openChartPreview(chart)" />
|
|
|
|
|
<div>
|
|
|
|
|
<strong>{{ form.rfmCharts[chart.id].name }}</strong>
|
|
|
|
|
<button type="button" @click="openChartPreview(chart)">预览图片</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<p v-else class="rfm-chart-empty">上传 PNG、JPG 或 WEBP 格式的图表</p>
|
|
|
|
|
</article>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="upload-tip">三类图表均需上传。图片上传后可点击缩略图预览,保存后再次进入页面仍可查看。</p>
|
|
|
|
|
|
|
|
|
|
<div class="portrait-table-wrap">
|
|
|
|
|
<section v-for="(record, index) in form.keyUserRecords" :key="record.code" class="portrait-record">
|
|
|
|
|
<div class="portrait-user">
|
|
|
|
|
<p v-if="index === 0" class="portrait-example">示例</p>
|
|
|
|
|
<label>
|
|
|
|
|
<span>用户:</span>
|
|
|
|
|
<input v-model="record.user" class="portrait-input" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>基础属性:</span>
|
|
|
|
|
<input v-model="record.basicAttributes" class="portrait-input portrait-input--wide" />
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<span>标签:</span>
|
|
|
|
|
<input v-model="record.tags" class="portrait-input portrait-input--wide" />
|
|
|
|
|
</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" />
|
|
|
|
|
<textarea v-model="record.painPoints" class="portrait-cell" />
|
|
|
|
|
<textarea v-model="record.psychology" class="portrait-cell" />
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
<el-dialog v-model="chartPreviewVisible" :title="activeChartPreview.title" width="min(960px, 92vw)" append-to-body>
|
|
|
|
|
<img class="rfm-chart-preview" :src="activeChartPreview.url" :alt="activeChartPreview.title" />
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section v-show="currentStep === 3" class="step-panel">
|
|
|
|
|
@ -264,10 +241,15 @@ 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, getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile } from "@/api/studentTrainingAnswer";
|
|
|
|
|
import { checkTargetUserProfileStepOne, checkTargetUserProfileStepTwo, getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile } from "@/api/studentTrainingAnswer";
|
|
|
|
|
import productReferenceImage from "@/assets/images/target-user-profile-u1344.png";
|
|
|
|
|
|
|
|
|
|
const TASK_KEY = "target-user-profile";
|
|
|
|
|
const RFM_CHARTS = [
|
|
|
|
|
{ id: "segment-share", index: 1, title: "客户分层占比饼图", buttonText: "上传客户分层占比饼图" },
|
|
|
|
|
{ id: "average-spend", index: 2, title: "客户分层平均消费对比柱状图", buttonText: "上传客户分层平均消费对比柱状图" },
|
|
|
|
|
{ id: "score-heatmap", index: 3, title: "RFM 分数分布热力图", buttonText: "上传 RFM 分布热力图" },
|
|
|
|
|
];
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
|
|
|
|
|
const taskConfig = ref(null);
|
|
|
|
|
@ -275,6 +257,10 @@ const saving = ref(false);
|
|
|
|
|
const uploading = ref(false);
|
|
|
|
|
const fileInputRef = ref(null);
|
|
|
|
|
const previewDialogVisible = ref(false);
|
|
|
|
|
const chartFileInputRefs = ref({});
|
|
|
|
|
const uploadingChartId = ref("");
|
|
|
|
|
const chartPreviewVisible = ref(false);
|
|
|
|
|
const activeChartPreview = ref({ title: "图表预览", url: "" });
|
|
|
|
|
const currentStep = ref(1);
|
|
|
|
|
const activeInterviewIndex = ref(0);
|
|
|
|
|
const interviewDialogVisible = ref(false);
|
|
|
|
|
@ -418,6 +404,7 @@ function createForm() {
|
|
|
|
|
profileFileName: "",
|
|
|
|
|
profileFileUrl: "",
|
|
|
|
|
profilePreview: createEmptyPreview(),
|
|
|
|
|
rfmCharts: createEmptyRfmCharts(),
|
|
|
|
|
profileAnalysis: "",
|
|
|
|
|
keyUserProfile: "",
|
|
|
|
|
keyUserRecords: createKeyUserRecords(),
|
|
|
|
|
@ -430,6 +417,13 @@ function createForm() {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createEmptyRfmCharts() {
|
|
|
|
|
return RFM_CHARTS.reduce((charts, chart) => {
|
|
|
|
|
charts[chart.id] = { url: "", name: "" };
|
|
|
|
|
return charts;
|
|
|
|
|
}, {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createEmptyPreview() {
|
|
|
|
|
return {
|
|
|
|
|
sheetName: "",
|
|
|
|
|
@ -455,6 +449,7 @@ const steps = computed(() =>
|
|
|
|
|
);
|
|
|
|
|
const activeStepName = computed(() => steps.value.find((step) => step.no === currentStep.value)?.name || "");
|
|
|
|
|
const hasExcelPreview = computed(() => form.value.profilePreview.columns.length > 0);
|
|
|
|
|
const rfmCharts = computed(() => RFM_CHARTS);
|
|
|
|
|
|
|
|
|
|
const progressItems = computed(() => [
|
|
|
|
|
...steps.value.map((step) => ({
|
|
|
|
|
@ -545,6 +540,7 @@ function applySavedAnswer(value) {
|
|
|
|
|
profileFileName: parsed.profileFileName || parsed.file?.name || "",
|
|
|
|
|
profileFileUrl: parsed.profileFileUrl || parsed.file?.url || "",
|
|
|
|
|
profilePreview: normalizeExcelPreview(parsed.profilePreview),
|
|
|
|
|
rfmCharts: normalizeRfmCharts(parsed.rfmCharts),
|
|
|
|
|
profileAnalysis: parsed.profileAnalysis || "",
|
|
|
|
|
keyUserProfile: parsed.keyUserProfile || "",
|
|
|
|
|
keyUserRecords: normalizeKeyUserRecords(parsed.keyUserRecords, parsed.keyUserProfile),
|
|
|
|
|
@ -561,6 +557,21 @@ function applySavedAnswer(value) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeRfmCharts(charts) {
|
|
|
|
|
const defaults = createEmptyRfmCharts();
|
|
|
|
|
if (!charts || typeof charts !== "object") {
|
|
|
|
|
return defaults;
|
|
|
|
|
}
|
|
|
|
|
return RFM_CHARTS.reduce((result, chart) => {
|
|
|
|
|
const value = charts[chart.id] || {};
|
|
|
|
|
result[chart.id] = {
|
|
|
|
|
url: String(value.url || ""),
|
|
|
|
|
name: String(value.name || ""),
|
|
|
|
|
};
|
|
|
|
|
return result;
|
|
|
|
|
}, defaults);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeExcelPreview(preview) {
|
|
|
|
|
if (!preview || typeof preview !== "object" || !Array.isArray(preview.columns) || !Array.isArray(preview.rows)) {
|
|
|
|
|
return createEmptyPreview();
|
|
|
|
|
@ -578,6 +589,23 @@ function triggerFileSelect() {
|
|
|
|
|
fileInputRef.value?.click();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setChartInputRef(chartId, element) {
|
|
|
|
|
if (element) {
|
|
|
|
|
chartFileInputRefs.value[chartId] = element;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function triggerChartFileSelect(chartId) {
|
|
|
|
|
chartFileInputRefs.value[chartId]?.click();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openChartPreview(chart) {
|
|
|
|
|
const file = form.value.rfmCharts[chart.id];
|
|
|
|
|
if (!file?.url) return;
|
|
|
|
|
activeChartPreview.value = { title: chart.title, url: file.url };
|
|
|
|
|
chartPreviewVisible.value = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleInterviewClick(index) {
|
|
|
|
|
activeInterviewIndex.value = index;
|
|
|
|
|
const record = form.value.keyUserRecords[index];
|
|
|
|
|
@ -619,6 +647,39 @@ async function handleFileChange(event) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleChartFileChange(chart, event) {
|
|
|
|
|
const file = event.target.files?.[0];
|
|
|
|
|
if (!file) return;
|
|
|
|
|
if (!/\.(png|jpe?g|webp)$/i.test(file.name)) {
|
|
|
|
|
proxy?.$modal?.msgError("请上传 PNG、JPG 或 WEBP 格式的图表图片");
|
|
|
|
|
event.target.value = "";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (file.size > 10 * 1024 * 1024) {
|
|
|
|
|
proxy?.$modal?.msgError("图表图片不能超过 10MB");
|
|
|
|
|
event.target.value = "";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const data = new FormData();
|
|
|
|
|
data.append("file", file);
|
|
|
|
|
uploadingChartId.value = chart.id;
|
|
|
|
|
try {
|
|
|
|
|
const res = await uploadStudentTrainingFile(data);
|
|
|
|
|
const url = res?.url || res?.fileName || res?.data?.url || res?.data?.fileName || "";
|
|
|
|
|
if (!url) {
|
|
|
|
|
throw new Error("上传结果缺少文件地址");
|
|
|
|
|
}
|
|
|
|
|
form.value.rfmCharts[chart.id] = { url, name: file.name };
|
|
|
|
|
openChartPreview(chart);
|
|
|
|
|
proxy?.$modal?.msgSuccess(`${chart.title}上传成功`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
proxy?.$modal?.msgError(error?.message || "图表上传失败,请重试");
|
|
|
|
|
} finally {
|
|
|
|
|
uploadingChartId.value = "";
|
|
|
|
|
event.target.value = "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function parseExcelPreview(file) {
|
|
|
|
|
const workbook = XLSX.read(await file.arrayBuffer(), { type: "array" });
|
|
|
|
|
const sheetName = workbook.SheetNames?.[0];
|
|
|
|
|
@ -657,6 +718,7 @@ function buildAnswerPayload(status) {
|
|
|
|
|
profileFileName: form.value.profileFileName,
|
|
|
|
|
profileFileUrl: form.value.profileFileUrl,
|
|
|
|
|
profilePreview: form.value.profilePreview,
|
|
|
|
|
rfmCharts: form.value.rfmCharts,
|
|
|
|
|
file: {
|
|
|
|
|
name: form.value.profileFileName,
|
|
|
|
|
url: form.value.profileFileUrl,
|
|
|
|
|
@ -685,6 +747,15 @@ async function persist(status = "IN_PROGRESS") {
|
|
|
|
|
previewColumnCount: form.value.profilePreview.columns.length,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (status !== "RESET" && (currentStep.value === 2 || status === "SUBMITTED")) {
|
|
|
|
|
await checkTargetUserProfileStepTwo({
|
|
|
|
|
charts: RFM_CHARTS.map((chart) => ({
|
|
|
|
|
type: chart.id,
|
|
|
|
|
url: form.value.rfmCharts[chart.id]?.url || "",
|
|
|
|
|
name: form.value.rfmCharts[chart.id]?.name || "",
|
|
|
|
|
})),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
await saveStudentTrainingAnswer(TASK_KEY, buildAnswerPayload(status));
|
|
|
|
|
proxy?.$modal?.msgSuccess(status === "SUBMITTED" ? "提交成功" : "保存成功");
|
|
|
|
|
} finally {
|
|
|
|
|
@ -697,7 +768,7 @@ function saveCurrentProgress() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function goNext() {
|
|
|
|
|
if (currentStep.value === 1) {
|
|
|
|
|
if (currentStep.value === 1 || currentStep.value === 2) {
|
|
|
|
|
await persist("IN_PROGRESS");
|
|
|
|
|
}
|
|
|
|
|
currentStep.value += 1;
|
|
|
|
|
@ -1594,6 +1665,140 @@ function exportOutcome() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
gap: 18px;
|
|
|
|
|
margin: 26px 0 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-card {
|
|
|
|
|
display: flex;
|
|
|
|
|
min-height: 232px;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
padding: 18px;
|
|
|
|
|
border: 1px solid rgba(83, 201, 244, 0.28);
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
background:
|
|
|
|
|
linear-gradient(145deg, rgba(23, 111, 153, 0.2), rgba(4, 20, 34, 0.72)),
|
|
|
|
|
rgba(2, 15, 26, 0.82);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-card__head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
margin-bottom: auto;
|
|
|
|
|
|
|
|
|
|
h4 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: #e9f8ff;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-card__index {
|
|
|
|
|
display: grid;
|
|
|
|
|
width: 31px;
|
|
|
|
|
height: 31px;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
place-items: center;
|
|
|
|
|
border: 1px solid rgba(114, 232, 255, 0.64);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
color: #5ee8ff;
|
|
|
|
|
background: rgba(6, 84, 124, 0.38);
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-upload {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
min-height: 42px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-top: 18px;
|
|
|
|
|
padding: 0 14px;
|
|
|
|
|
border: 1px solid rgba(92, 224, 255, 0.7);
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
background: linear-gradient(95deg, #0b84bd, #096491);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
transition: transform 0.16s ease, box-shadow 0.16s ease;
|
|
|
|
|
|
|
|
|
|
&:hover:not(:disabled) {
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: 0 8px 18px rgba(0, 141, 205, 0.22);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:disabled {
|
|
|
|
|
cursor: wait;
|
|
|
|
|
opacity: 0.72;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-empty {
|
|
|
|
|
margin: 15px 0 0;
|
|
|
|
|
color: #7f9aa9;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-file {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 11px;
|
|
|
|
|
margin-top: 16px;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border: 1px solid rgba(102, 219, 255, 0.28);
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: rgba(0, 13, 23, 0.58);
|
|
|
|
|
|
|
|
|
|
img {
|
|
|
|
|
width: 58px;
|
|
|
|
|
height: 46px;
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: zoom-in;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
div {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strong {
|
|
|
|
|
display: block;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
color: #bcefff;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 1.35;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
border: 0;
|
|
|
|
|
color: #55dfff;
|
|
|
|
|
background: transparent;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-preview {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-height: 72vh;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.field-block {
|
|
|
|
|
display: block;
|
|
|
|
|
|
|
|
|
|
@ -1718,6 +1923,10 @@ function exportOutcome() {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rfm-chart-grid {
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-reference {
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
|
|
|
|
@ -1781,6 +1990,10 @@ function exportOutcome() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 760px) {
|
|
|
|
|
.rfm-chart-grid {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.interview-tabs {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|