﻿<!--
 * @Author: qinzhenpen qzp1807@126.com
 * @Date: 2025-04-28 15:05:56
 * @LastEditors: qinzhenpen qzp1807@126.com
 * @LastEditTime: 2025-05-08 17:17:01
 * @FilePath: \e-commerce-internet\src\views\product\price.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
  <div class="price student-training-shell">
    <main class="training-core">
      <section class="task-header"><div class="task-badge"><el-icon><Money /></el-icon> 核心实训 · 产品规划与设计</div><h1 class="task-title">{{ taskTitle }}</h1></section>
      <TrainingTaskBrief :background="taskBackground" :goals="taskGoals" :requirements="taskRequirement" :show-material="false" />
      <section class="task-card"><div class="workbench-head workbench-head--actions-only"><div class="workbench-actions"><TrainingMaterialButton :material-name="taskConfig?.materialName" :material-url="taskConfig?.materialUrl" /><button type="button" class="outline-action" @click="exportOutcome"><el-icon><Download /></el-icon>导出实训成果</button></div></div>
        <nav v-if="hasConfiguredSteps" class="step-indicator" aria-label="产品定价步骤"><template v-for="(step,index) in configuredSteps" :key="`${step}-${index}`"><button type="button" :class="['step-tab',{active:activeStep===index+1,complete:activeStep>index+1}]" :aria-current="activeStep===index+1?'step':undefined" :aria-label="`第${index+1}步：${step}`" @click="setCurrentStep(index+1)"><span class="step-marker">{{ String(index+1).padStart(2,'0') }}</span><span class="step-copy"><strong class="step-name" :title="step">{{ step }}</strong><span class="step-action">{{ activeStep===index+1?'当前步骤':'点击切换' }} <span aria-hidden="true">→</span></span></span></button><span v-if="index<configuredSteps.length-1" class="step-connector" aria-hidden="true"></span></template></nav>
        <section v-show="isSectionVisible(1)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>

    <div class="caseBacktitle">
      <img src="@/assets/images/步骤.png" alt="" />
      <span>一、知识学习</span>
    </div>
    <p>
      PSM分析技术的理论基础
      <br />
      在开发新产品时，厂商对产品价格的看法和消费者对产品价格的知觉经常会不一致，因此厂商往往不愿意事先设定产品的价格，而希望能通过调查从消费者那里得到产品价格的可接受范围。
      <br />
      价格敏感度测试(Price Sensitivity Measure ment，PSM）不需要预先给定好价格，而是让每位被访者自己表示他们的可接受价格范围。
      <br />
      目的：可用于新产品开发、产品线的扩张（产品线内产品项目的增加）、产品范围的扩张（增加产品线）等情况下，确定产品价格的可接受值或范围。
      <br />
      样本的选择：对于产品的开发，必须从所有可能使用新产品的消费总体中抽取有代表性的样本。对于产品线或产品范围的扩张，样本可以从原来的品牌使用者中抽取。
      <br />
    </p>
    <div class="caseBacktitle">
      <img src="@/assets/images/步骤.png" alt="" />
      <span>二、实验实训</span>
    </div>
    <p>假如淘宝某店铺计划推出一款针对大学生群体的洗面奶，您对下列价格区间有何想法？</p>
    <el-table
      :data="priceList1"
      border
      style="width: 60%"
      :header-cell-style="{
        color: '#fff',
        fontSize: '14px',
        backgroundColor: '#6A7DE9!important',
      }"
    >
      <el-table-column label="价格区间" align="center" prop="label"></el-table-column>
      <el-table-column label="太便宜，可能有陷阱" align="center" prop="value">
        <template #default="{ row }">
          <el-radio v-model="row.value" label="1" class="hidden-label"></el-radio>
        </template>
      </el-table-column>
      <el-table-column label="很划算，肯定会购买" align="center" prop="value">
        <template #default="{ row }">
          <el-radio v-model="row.value" label="2" class="hidden-label"></el-radio>
        </template>
      </el-table-column>
      <el-table-column label="有点贵，还可以接受" align="center" prop="value">
        <template #default="{ row }">
          <el-radio v-model="row.value" label="3" class="hidden-label"></el-radio>
        </template>
      </el-table-column>
      <el-table-column label="太贵，直接放弃" align="center" prop="value">
        <template #default="{ row }">
          <el-radio v-model="row.value" label="4" class="hidden-label"></el-radio>
        </template>
      </el-table-column>
    </el-table>
        </section><section v-show="isSectionVisible(2)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>
    <p>洗面奶价格梯度表，累计百分比用百分数表示，保留两位小数。</p>
    <el-table
      :data="priceList2"
      border
      style="width: 80%"
      :header-cell-style="{
        color: '#fff',
        fontSize: '14px',
        backgroundColor: '#6A7DE9!important',
      }"
    >
      <el-table-column label="价格区间" align="center" prop="label"></el-table-column>
      <el-table-column label="太便宜，可能有陷阱" align="center">
        <el-table-column label="" align="center" prop="pricevalue1"></el-table-column>
        <el-table-column label="累计总和" align="center" prop="toocheapsum1">
          <template #default="{ row }">
            <el-input v-model="row.toocheapsum1" placeholder="" />
          </template>
        </el-table-column>
        <el-table-column label="累计百分比" align="center" prop="percentage1">
          <template #default="{ row }">
            <el-input v-model="row.percentage1" placeholder="">
              <template #suffix>%</template>
            </el-input>
          </template>
        </el-table-column>
      </el-table-column>
      <el-table-column label="很划算，肯定会购买" align="center">
        <el-table-column label="" align="center" prop="pricevalue2"></el-table-column>
        <el-table-column label="累计总和" align="center" prop="toocheapsum2">
          <template #default="{ row }">
            <el-input v-model="row.toocheapsum2" placeholder="" />
          </template>
        </el-table-column>
        <el-table-column label="累计百分比" align="center" prop="percentage2">
          <template #default="{ row }">
            <el-input v-model="row.percentage2" placeholder="">
              <template #suffix>%</template>
            </el-input>
          </template>
        </el-table-column>
      </el-table-column>
      <el-table-column label="有点贵，还可以接受" align="center">
        <el-table-column label="" align="center" prop="pricevalue3"></el-table-column>
        <el-table-column label="累计总和" align="center" prop="toocheapsum3">
          <template #default="{ row }">
            <el-input v-model="row.toocheapsum3" placeholder="" />
          </template>
        </el-table-column>
        <el-table-column label="累计百分比" align="center" prop="percentage3">
          <template #default="{ row }">
            <el-input v-model="row.percentage3" placeholder="">
              <template #suffix>%</template>
            </el-input>
          </template>
        </el-table-column>
      </el-table-column>
      <el-table-column label="太贵，直接放弃" align="center">
        <el-table-column label="" align="center" prop="pricevalue4"></el-table-column>
        <el-table-column label="累计总和" align="center" prop="toocheapsum4">
          <template #default="{ row }">
            <el-input v-model="row.toocheapsum4" placeholder="" />
          </template>
        </el-table-column>
        <el-table-column label="累计百分比" align="center" prop="percentage4">
          <template #default="{ row }">
            <el-input v-model="row.percentage4" placeholder="">
              <template #suffix>%</template>
            </el-input>
          </template>
        </el-table-column>
      </el-table-column>
    </el-table>
        </section><section v-show="isSectionVisible(3)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>
    <p>太便宜和很划算向下累积，有点贵和太贵向上累积，画出每一个价格下开始觉得太便宜、便宜划算，开始觉得贵、太贵的累积人数百分比曲线图。</p>
    <div class="cnetent">
      <div class="left">
        <el-button @click="draw" style="margin-bottom: 20px">绘制</el-button>
        <div id="main" ref="main" style="width: 100%; height: 50vh"></div>
      </div>
      <div class="right">
        <div style="display: flex">
          <div class="online">
            <span>最优价格点:</span>
            <el-input style="width: 100px" type="text" v-model="formInline.optimalPrice" />
          </div>
          <div class="online ml20">
            <span>可接受价格点:</span>
            <el-input style="width: 100px" type="text" v-model="formInline.acceptablePrice" />
          </div>
        </div>
        <div class="online mt20">
          <span>可接受价格范围:</span>
          <el-input style="width: 100px" type="text" v-model="formInline.range1" />
          <span class="p10">----------</span>
          <el-input style="width: 100px" type="text" v-model="formInline.range2" />
        </div>
        <p>PSM模型在产品定价过程中的作用以及不足：</p>
        <el-input v-model="textarea2" :autosize="{ minRows: 11, maxRows: 11 }" type="textarea" placeholder="" />
      </div>
    </div>
        </section><section v-show="hasConfiguredSteps && activeStep > 3" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div><p>请复核价格梯度、PSM 曲线、最优价格点和可接受价格范围后，保存或提交实训成果。</p></section>
        <div v-if="hasConfiguredSteps" class="step-navigation"><button type="button" class="btn-nav" :disabled="activeStep===1" @click="goPreviousStep"><el-icon><ArrowLeft /></el-icon>上一步</button><button type="button" class="btn-nav btn-save" :disabled="saving" @click="saveCurrentProgress"><el-icon><CircleCheck /></el-icon>保存当前进度</button><button type="button" class="btn-nav btn-primary" :disabled="activeStep===configuredSteps.length" @click="goNextStep">下一步<el-icon><ArrowRight /></el-icon></button></div>
        <div class="taskfooter"><el-button @click="submitTask" :loading="saving">提交任务</el-button><el-button @click="resetTask" :loading="saving">清空填写</el-button></div>
      </section>
    </main>
    <TrainingAiSidebar study-tip="当前任务建议：完成价格敏感度数据整理，绘制 PSM 曲线并给出最优与可接受价格范围。" :progress-items="progressItems" />
  </div>
</template>
<script setup>


import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
import * as echarts from "echarts";
import * as analysisAPI from "@/api/market-demand-mining.js";
import { ArrowLeft, ArrowRight, CircleCheck, Download, Money } from "@element-plus/icons-vue";
import { getTrainingTaskByKey } from "@/api/trainingTask";
import { getStudentTrainingAnswer, saveStudentTrainingAnswer } from "@/api/studentTrainingAnswer";
import { parseTrainingArray } from "@/views/training/taskKeyMap";
const { proxy } = getCurrentInstance();
import useUserStore from "@/store/modules/user";
import popModel from "@/views/components/popModal.vue";
const userStore = useUserStore();
const TASK_KEY = "product-pricing";
const taskConfig = ref(null);
const activeStep = ref(1);
const saving = ref(false);
const taskTitle = computed(() => taskConfig.value?.taskName || "产品定价");
const taskBackground = computed(() => taskConfig.value?.background || "PSM 价格敏感度分析通过价格区间问卷数据，帮助产品团队确定合理的定价区间。" );
const taskRequirement = computed(() => taskConfig.value?.requirements || "使用 PSM 模型完成价格梯度整理、曲线分析与定价结论输出。" );
const taskGoals = computed(() => parseTrainingArray(taskConfig.value?.objectives, ["理解 PSM 价格敏感度模型", "能够完成价格梯度与累计百分比计算", "能够给出合理定价区间"]));
const configuredSteps = computed(() => parseTrainingArray(taskConfig.value?.steps));
const hasConfiguredSteps = computed(() => configuredSteps.value.length > 0);
const activeStepName = computed(() => configuredSteps.value[activeStep.value - 1] || "产品定价");
const progressItems = computed(() => configuredSteps.value.map((text, index) => ({ text, status: index + 1 < activeStep.value ? "done" : index + 1 === activeStep.value ? "doing" : "" })));
const formInline = ref({
  optimalPrice: "",
  acceptablePrice: "",
  range1: "",
  range2: "",
});
const textarea2 = ref("");
const dialogVisible = ref(false);
const reTraining = () => {
  proxy.$message.success("重新实训");
};
onMounted(() => {});
const main = ref(null);
const priceList1 = ref([
  {
    label: "15元以内",
    value: "",
  },
  {
    label: "15-20元",
    value: "",
  },
  {
    label: "20-30元",
    value: "",
  },
  {
    label: "30-50元",
    value: "",
  },
  {
    label: "50-70元",
    value: "",
  },
  {
    label: "70-100元",
    value: "",
  },
  {
    label: "100-200元",
    value: "",
  },
  {
    label: "200元以上",
    value: "",
  },
]);
const priceList2 = ref([
  {
    label: "15元以内",
    pricevalue1: "126",
    pricevalue2: "56",
    pricevalue3: "0",
    pricevalue4: "0",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "15-20元",
    pricevalue1: "85",
    pricevalue2: "102",
    pricevalue3: "0",
    pricevalue4: "0",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "20-30元",
    pricevalue1: "36",
    pricevalue2: "198",
    pricevalue3: "5",
    pricevalue4: "0",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "30-50元",
    pricevalue1: "2",
    pricevalue2: "85",
    pricevalue3: "30",
    pricevalue4: "0",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "50-70元",
    pricevalue1: "0",
    pricevalue2: "25",
    pricevalue3: "98",
    pricevalue4: "8",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "70-100元",
    pricevalue1: "0",
    pricevalue2: "2",
    pricevalue3: "126",
    pricevalue4: "35",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "100-200元",
    pricevalue1: "0",
    pricevalue2: "0",
    pricevalue3: "159",
    pricevalue4: "89",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
  {
    label: "200元以上",
    pricevalue1: "0",
    pricevalue2: "0",
    pricevalue3: "109",
    pricevalue4: "192",
    toocheapsum1: "",
    toocheapsum2: "",
    toocheapsum3: "",
    toocheapsum4: "",
    percentage1: "",
    percentage2: "",
    percentage3: "",
    percentage4: "",
  },
]);
// 校验空值并只提示一次
function validateAndAlertOnce() {
  let hasEmptyValue = false; // 标记是否发现空值
  // 遍历数组检查空值
  priceList2.value.forEach((item) => {
    Object.keys(item).forEach((key) => {
      if (item[key] === "") {
        hasEmptyValue = true; // 发现空值，标记为 true
        // 可选择替换空值（如果需要）
        item[key] = ""; // 替换为空值时的默认值
      }
    });
  });

  // 只在发现空值时提示一次
  if (hasEmptyValue) {
    // alert("数据中存在空值，已自动替换为默认值 0。");
    proxy.$modal.msgWarning("请填写完整数据!");
    return false; // 返回 false，表示存在空值
  }
  return true; // 没有空值，返回 true
}
const draw = () => {
  const isValid = validateAndAlertOnce();
  if (!isValid) return;
  const minPicture = priceList2.value.map((item) => item.percentage1);
  const calculatePrice = priceList2.value.map((item) => item.percentage2);
  const minexpensive = priceList2.value.map((item) => item.percentage3);
  const expensive = priceList2.value.map((item) => item.percentage4);
  const priceRanges = ['15元以内',"15-20元", "20-30元", "30-50元", "50-70元", "70-100元", "100-200元", "200元以上"];
  var myChart = echarts.init(main.value);
  myChart.setOption({
    title: {
      text: "PSM价格数据分析",
      left: "center",
    },
    tooltip: {
      trigger: "axis",
    },
    legend: {
      data: ["太便宜", "很划算", "有点贵", "太贵了"],
      top: 30,
    },
    xAxis: {
      type: "category",
      name: "价格区间",
      data: priceRanges,
    },
    yAxis: {
      type: "value",
      name: "销售数量 (件)",
      min: 0,
      max: 100,
    },
    series: [
      {
        name: "太便宜",
        type: "line",
        data: minPicture,
        color: "#1f77b4",
        symbol: "circle",
        symbolSize: 8,
      },
      {
        name: "太贵了",
        type: "line",
        data: expensive,
        color: "#d62728",
        symbol: "circle",
        symbolSize: 8,
      },
      {
        name: "有点贵",
        type: "line",
        data: minexpensive,
        color: "#2ca02c",
        symbol: "circle",
        symbolSize: 8,
      },
      {
        name: "很划算",
        type: "line",
        data: calculatePrice,
        color: "#ff7f0e",
        symbol: "circle",
        symbolSize: 8,
      },
    ],
    grid: {
      left: "2%",
      right: "15%",
      bottom: "10%",
      containLabel: true,
    },
    graphic: [
      {
        type: "text",
        left: 391, // 或 "60%" 等
        top: 334,
        z: 100, // 设置更高的图层层级
        style: {
          text: "P4",
          font: "bold 14px sans-serif",
          fill: "#2ca02c",
          // 显示在线条墙上面
          distance: 10,
        },
      },
      {
        type: "text",
        left: 300, // 或 "60%" 等
        top: 380,
        z: 100, // 设置更高的图层层级
        symbol: "circle",
        style: {
          text: "P1",
          font: "bold 14px sans-serif",
          fill: "#1f77b4",
          // 显示在线条墙上面
          distance: 10,
        },
      },
      {
        type: "text",
        left: 350, // 或 "60%" 等
        top: 388,
        z: 100, // 设置更高的图层层级
        symbol: "circle",
        style: {
          text: "P2",
          font: "bold 14px sans-serif",
          fill: "#d62728",
          // 显示在线条墙上面
          distance: 10,
        },
      },
      {
        type: "text",
        left: 430, // 或 "60%" 等
        top: 380,
        z: 100, // 设置更高的图层层级
        symbol: "circle",
        style: {
          text: "P3",
          font: "bold 14px sans-serif",
          fill: "#ff7f0e",
          // 显示在线条墙上面
          distance: 10,
        },
      },
    ],
  });
};
function normalizeActiveStep(value) { if (!configuredSteps.value.length) return 1; return Math.min(Math.max(Number(value) || 1, 1), configuredSteps.value.length); }
function isSectionVisible(section) { return !hasConfiguredSteps.value || activeStep.value === section; }
function buildProgress() { return { activeStep: activeStep.value, formInline: formInline.value, textarea2: textarea2.value, priceList1: priceList1.value, priceList2: priceList2.value, updatedAt: new Date().toISOString() }; }
function applyProgress(value) { if (!value || typeof value !== "object") return; activeStep.value = normalizeActiveStep(value.activeStep); formInline.value = { ...formInline.value, ...(value.formInline || {}) }; textarea2.value = value.textarea2 || ""; if (Array.isArray(value.priceList1)) priceList1.value = value.priceList1; if (Array.isArray(value.priceList2)) priceList2.value = value.priceList2; }
async function persistProgress(saveAction = "SAVE", silent = false) { await saveStudentTrainingAnswer(TASK_KEY, { saveAction, currentStep: activeStep.value, step1Answer: JSON.stringify(buildProgress()) }); if (!silent) proxy?.$modal?.msgSuccess(saveAction === "SUBMIT" ? "提交成功" : "保存成功"); }
function setCurrentStep(step) { activeStep.value = normalizeActiveStep(step); persistProgress("SAVE", true).catch(() => {}); }
function goPreviousStep() { setCurrentStep(activeStep.value - 1); }
function goNextStep() { setCurrentStep(activeStep.value + 1); }
async function saveCurrentProgress() { if (saving.value) return; saving.value = true; try { await persistProgress(); } finally { saving.value = false; } }
async function loadTaskConfig() { try { const res = await getTrainingTaskByKey(TASK_KEY); taskConfig.value = res?.data || null; activeStep.value = normalizeActiveStep(activeStep.value); } catch (error) { taskConfig.value = null; activeStep.value = 1; } }
async function loadSavedProgress() { try { const res = await getStudentTrainingAnswer(TASK_KEY); const raw = res?.data?.step1Answer; if (raw) applyProgress(JSON.parse(raw)); } catch (error) { /* Historical progress is optional. */ } }
function exportOutcome() { const blob = new Blob([JSON.stringify(buildProgress(), null, 2)], { type: "application/json;charset=utf-8" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = `${taskTitle.value}-实训成果.json`; link.click(); URL.revokeObjectURL(url); }
const submitTask = async () => {
  const minPicture = priceList2.value.map((item) => item.percentage1);
  const minPriceValue = priceList2.value.map((item) => item.toocheapsum1);
  const calculatePrice = priceList2.value.map((item) => item.percentage2);
  const calculatevalue = priceList2.value.map((item) => item.toocheapsum2);
  const minexpensive = priceList2.value.map((item) => item.percentage3);
  const minexpensivevalue = priceList2.value.map((item) => item.toocheapsum3);
  const expensive = priceList2.value.map((item) => item.percentage4);
  const tooSum = priceList2.value.map((item) => item.toocheapsum4);
  //   判断数组是否为空
  if (minPicture.includes("") || minPriceValue.includes("") || calculatePrice.includes("") || calculatevalue.includes("") || minexpensive.includes("") || minexpensivevalue.includes("") || expensive.includes("") || priceList1.value.some((item) => !item.value)) {
    return proxy.$modal.msgWarning("请填写完整数据");
  }
  //   判断formInline的值是否为空
  if (formInline.value.optimalPrice == "" || formInline.value.acceptablePrice == "" || formInline.value.range1 == "" || formInline.value.range2 == "") {
    return proxy.$modal.msgWarning("请填写完整数据");
  }
  const params = {
    acceptablePrice: formInline.value.acceptablePrice,
    acceptablePriceRange: formInline.value.range1 + "," + formInline.value.range2,
    optimalPrice: formInline.value.optimalPrice,
    cheapPercentage: minPicture.join(","),
    cheapSum: minPriceValue.join(","),
    context: textarea2.value,
    costEffectivePercentage: calculatePrice.join(","),
    costEffectiveSum: calculatevalue.join(","),
    expensivePercentage: minexpensive.join(","),
    expensiveSum: minexpensivevalue.join(","),
    tooExpensivePercentage: expensive.join(","),
    tooSum: tooSum.join(","),
    priceRange: JSON.stringify(priceList1.value),
    displayStatus: 1,
  };
  if (saving.value) return;
  saving.value = true;
  try { await analysisAPI.productPricingSubmit({ stuAnswer: params, userId: userStore.userInfo.userId }); await persistProgress("SUBMIT"); } finally { saving.value = false; }
};
// 回显接口
const loadTaskInfo = async () => {
  try {
    const res = await analysisAPI.productPricingEcho({ userId: userStore.userInfo.userId });
    if (!res.data) return;
    formInline.value.optimalPrice = res.data.optimalPrice;
    formInline.value.acceptablePrice = res.data.acceptablePrice;
    formInline.value.range1 = res.data.acceptablePriceRange.split(",")[0];
    formInline.value.range2 = res.data.acceptablePriceRange.split(",")[1];
    textarea2.value = res.data.context;
    priceList1.value = JSON.parse(res.data.priceRange !== "" ? res.data.priceRange : priceList1.value);
    priceList2.value.forEach((item, index) => {
      item.toocheapsum1 = res.data.cheapSum.split(",")[index];
      item.toocheapsum2 = res.data.costEffectiveSum.split(",")[index];
      item.toocheapsum3 = res.data.expensiveSum.split(",")[index];
      item.percentage1 = res.data.cheapPercentage.split(",")[index];
      item.percentage2 = res.data.costEffectivePercentage.split(",")[index];
      item.percentage3 = res.data.expensivePercentage.split(",")[index];
      item.percentage4 = res.data.tooExpensivePercentage.split(",")[index];
      item.toocheapsum4 = res.data.tooSum.split(",")[index];
    });
    draw();
  } catch (error) { /* Existing submission is optional. */ }
};
// 重新实训
const resetTask = async () => {
  if (saving.value) return;
  saving.value = true;
  try {
    await analysisAPI.productPricingReTrain({ userId: userStore.userInfo.userId, module: "产品定价" });
    proxy.$modal.msgSuccess("重新实训成功");
    // 清楚数据
    formInline.value.optimalPrice = "";
    formInline.value.acceptablePrice = "";
    formInline.value.range1 = "";
    formInline.value.range2 = "";
    textarea2.value = "";
    // 清除数据
    priceList1.value.forEach((item) => {
      item.value = "";
    });
    priceList2.value.forEach((item) => {
      item.toocheapsum1 = "";
      item.toocheapsum2 = "";
      item.toocheapsum3 = "";
      item.toocheapsum4 = "";
      item.percentage1 = "";
      item.percentage2 = "";
      item.percentage3 = "";
      item.percentage4 = "";
    });
    activeStep.value = 1;
    await persistProgress("RESET", true);
    proxy.$modal.msgSuccess("清空填写成功");
  } finally { saving.value = false; }
};
onMounted(async () => {
  await loadTaskConfig();
  await loadTaskInfo();
  await loadSavedProgress();
});
</script>
<style lang="scss" scoped>
.price {
  --price-panel: rgba(4, 31, 46, 0.78);
  --price-panel-strong: rgba(3, 20, 31, 0.95);
  --price-line: rgba(54, 174, 221, 0.42);

  min-height: calc(100vh - 84px);
  padding: 0 0 32px;
  color: #d7ecff;
  background:
    radial-gradient(circle at 10% 0%, rgba(15, 129, 189, 0.16), transparent 34%),
    linear-gradient(180deg, rgba(3, 17, 27, 0.94), rgba(5, 19, 30, 0.98));

  :deep(.training-step-area) {
    margin-top: 0 !important;
    border-color: var(--price-line) !important;
    background: var(--price-panel) !important;
    box-shadow: inset 0 1px 0 rgba(129, 231, 255, 0.12), 0 18px 38px rgba(0, 0, 0, 0.2) !important;
  }

  :deep(.el-table) {
    --el-table-border-color: rgba(63, 164, 207, 0.36);
    --el-table-header-bg-color: #0b577f;
    --el-table-header-text-color: #edfbff;
    --el-table-row-hover-bg-color: rgba(15, 107, 149, 0.32);
    --el-table-bg-color: rgba(2, 20, 31, 0.82);
    --el-table-tr-bg-color: rgba(2, 20, 31, 0.82);
    --el-table-text-color: #d7ecff;
    overflow: hidden;
    margin-top: 12px;
    border: 1px solid var(--price-line);
    border-radius: 12px;
    background: var(--price-panel-strong);
  }

  :deep(.el-table::before),
  :deep(.el-table__inner-wrapper::before) {
    background-color: transparent;
  }

  :deep(.el-table th.el-table__cell) {
    color: #edfbff !important;
    background: linear-gradient(180deg, #0f76a9, #0a567e) !important;
    font-weight: 800;
  }

  :deep(.el-table td.el-table__cell),
  :deep(.el-table tr) {
    color: #d7ecff !important;
    background: rgba(2, 20, 31, 0.82) !important;
  }

  :deep(.el-table .cell) {
    line-height: 1.65;
  }

  :deep(.el-input__wrapper),
  :deep(.el-textarea__inner) {
    border: 1px solid rgba(76, 169, 211, 0.48);
    border-radius: 7px;
    color: #ebf8ff;
    box-shadow: none !important;
    background: var(--price-panel-strong);
  }

  :deep(.el-input__wrapper) {
    min-height: 36px;
  }

  :deep(.el-input__inner),
  :deep(.el-textarea__inner) {
    color: #ebf8ff;
  }

  :deep(.el-input__inner::placeholder),
  :deep(.el-textarea__inner::placeholder) {
    color: #7194a8;
  }

  :deep(.el-input__wrapper.is-focus),
  :deep(.el-textarea__inner:focus) {
    border-color: #4edbff;
    box-shadow: 0 0 0 3px rgba(50, 205, 255, 0.12) !important;
  }

  :deep(.el-radio__inner) {
    border-color: #48bfe8;
    background: rgba(3, 20, 31, 0.9);
  }

  :deep(.el-radio__input.is-checked .el-radio__inner) {
    border-color: #4edbff;
    background: #159ac9;
  }

  :deep(.el-button) {
    border-color: rgba(55, 174, 221, 0.72) !important;
    color: #e9faff !important;
    background: linear-gradient(180deg, #168ab9, #0c628d) !important;
  }

  :deep(.el-button:hover) {
    border-color: #65e5ff !important;
    color: #ffffff !important;
    background: linear-gradient(180deg, #1b9ece, #0f709f) !important;
  }

  :deep(.hidden-label) {
    .el-radio__label {
      display: none;
    }
  }

  .online {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid rgba(44, 141, 182, 0.3);
    border-radius: 10px;
    background: rgba(1, 17, 27, 0.3);
  }

  .cnetent {
    display: flex;
    gap: 24px;
    align-items: stretch;
    padding: 18px;
    border: 1px solid rgba(44, 141, 182, 0.3);
    border-radius: 12px;
    background: rgba(1, 17, 27, 0.24);
    .left {
      width: 50%;
      height: 100%;
    }
    .right {
      width: 50%;
      height: 100%;
    }
  }

  .taskfooter {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 28px;
    margin-top: 32px;
    padding-top: 28px;
    border-top: 1px solid rgba(55, 174, 221, 0.24);
  }

  .taskfooter :deep(.el-button) {
    min-width: 172px;
    height: 56px;
    border-radius: 8px;
    font-size: 18px;
    font-weight: 800;
  }

  @media (max-width: 960px) {
    .cnetent {
      flex-direction: column;

      .left,
      .right {
        width: 100%;
      }
    }
  }
}

.price {
  display: grid; grid-template-columns: minmax(720px, 1fr) 340px; gap: 24px; min-height: calc(100vh - 76px); padding: 24px 24px 32px; color: #eef5ff; background: #06111d;
  .training-core { min-width: 0; padding: 28px; border: 1px solid rgba(0,180,255,.25); border-radius: 36px; background: rgba(8,18,28,.6); box-shadow: inset 0 1px 0 rgba(169,229,255,.08),0 22px 48px rgba(0,0,0,.22); }.task-header { margin-bottom: 24px; }.task-badge { display: inline-flex; align-items: center; gap: 8px; min-height: 28px; padding: 4px 14px; border: 1px solid rgba(99,217,255,.55); border-radius: 999px; color: #dff5ff; background: rgba(17,126,178,.36); font-size: 12px; font-weight: 700; }.task-title { margin: 12px 0 0; color: #fff; font-size: 30px; font-weight: 900; line-height: 1.3; }.task-card,.step-card { border: 1px solid rgba(82,174,226,.42); border-radius: 26px; background: rgba(0,25,42,.64); }.task-card { margin-top: 28px; padding: 18px 22px 22px; }.workbench-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 18px; margin-bottom: 22px; padding-bottom: 18px; border-bottom: 1px solid rgba(0,180,255,.2); }.section-eyebrow { margin: 0; color: #71dfff; font-size: 12px; font-weight: 800; line-height: 1.4; }.workbench-head h3 { margin: 4px 0 8px; color: #fff; font-size: 26px; font-weight: 800; line-height: 1.3; }.workbench-description { margin: 0; color: #d7ecff; font-size: 15px; line-height: 1.8; }.workbench-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; flex-shrink: 0; }.outline-action,.btn-nav { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; padding: 0 18px; border: 1px solid rgba(99,217,255,.55); border-radius: 999px; color: #e9fbff; background: rgba(17,126,178,.36); font: inherit; font-size: 14px; font-weight: 800; cursor: pointer; }.step-indicator { display: flex; gap: 6px; min-height: 58px; margin: 0 0 32px; padding: 6px; overflow-x: auto; border: 1px solid rgba(0,180,255,.3); border-radius: 999px; background: rgba(0,20,35,.65); }.step-tab { flex: 1 0 max-content; min-height: 46px; padding: 0 18px; border: 1px solid transparent; border-radius: 999px; color: #9ab3d0; background: transparent; font: inherit; font-size: 14px; font-weight: 800; cursor: pointer; }.step-tab.active { border-color: rgba(92,224,255,.7); color: #fff; background: linear-gradient(95deg,#0a6b9e,#0a5c86); }.step-card { margin-top: 32px; padding: 18px 22px 22px; }.step-indicator + .step-card,.workbench-head + .step-card { margin-top: 0; }.step-title-row { margin-bottom: 14px; }.step-title-row h3 { margin: 4px 0 0; color: #fff; font-size: 22px; font-weight: 900; line-height: 1.3; }.caseBacktitle { display: flex; align-items: center; gap: 8px; margin: 20px 0 10px; color: #dff5ff; font-size: 16px; font-weight: 800; }.caseBacktitle img { display: none; }.step-card > p { color: #d7ecff; font-size: 15px; line-height: 1.8; }:deep(.el-table) { width: 100% !important; max-width: 100%; border-radius: 18px; }:deep(.el-table th.el-table__cell) { background: rgba(8,55,82,.98) !important; }:deep(.el-input__wrapper),:deep(.el-textarea__inner) { border-radius: 14px; background: #0f1a24; }.step-navigation { display: flex; justify-content: center; gap: 14px; margin-top: 22px; }.btn-nav.btn-save,.btn-nav.btn-primary { border-color: rgba(92,224,255,.7); color: #fff; background: linear-gradient(95deg,#0b84bd,#096491); }.btn-nav:disabled { cursor: not-allowed; opacity: .45; }.taskfooter { gap: 52px; margin-top: 36px; padding-top: 0; border-top: 0; }.taskfooter :deep(.el-button) { min-width: 172px; height: 64px; border: 1px solid #2c83aa !important; border-radius: 6px; color: #dff7ff !important; background: rgba(13,64,88,.62) !important; font-size: 24px; font-weight: 900; }
}
.price {
  .workbench-actions { margin-left: auto; }
  .step-indicator { display:flex; align-items:center; gap:12px; width:calc(100% + 24px); min-height:0; margin:0 -12px 32px; padding:18px 22px; overflow-x:auto; border:1px solid rgba(0,180,255,.3); border-radius:18px; background:linear-gradient(110deg,rgba(4,39,58,.9),rgba(2,20,33,.94)); box-shadow:inset 0 1px 0 rgba(169,229,255,.08); }
  .step-tab { display:flex; flex:1 0 145px; align-items:center; min-height:66px; padding:8px 10px; border:1px solid rgba(68,141,177,.35); border-radius:8px; color:#9ab3d0; background:rgba(3,27,43,.52); text-align:left; transition:transform .2s,color .2s,background .2s,border-color .2s,box-shadow .2s; }
  .step-tab:hover,.step-tab:focus-visible { transform:translateY(-2px); border-color:rgba(89,223,255,.82); color:#fff; background:rgba(8,92,131,.52); box-shadow:0 8px 18px rgba(0,0,0,.2); outline:none; }
  .step-tab.active { border-color:rgba(89,226,255,.85); color:#fff; background:linear-gradient(95deg,#0a6b9e,#0a5c86); box-shadow:0 8px 20px rgba(0,120,170,.22); }
  .step-marker { display:inline-flex; flex:0 0 38px; align-items:center; justify-content:center; width:38px; height:38px; border:1px solid rgba(113,223,255,.55); border-radius:50%; color:#bdefff; background:rgba(3,27,43,.72); font-size:13px; font-weight:900; }
  .step-copy { display:grid; min-width:0; gap:4px; margin-left:10px; }.step-copy strong { display:-webkit-box; overflow:hidden; color:inherit; font-size:14px; line-height:1.25; -webkit-box-orient:vertical; -webkit-line-clamp:2; }.step-copy span { color:#7ec7e7; font-size:12px; font-weight:700; }.step-tab.active .step-marker { border-color:#a8f3ff; color:#fff; background:linear-gradient(135deg,#0bb2ed,#0a6b9e); box-shadow:0 0 0 4px rgba(77,215,255,.13),0 0 18px rgba(77,215,255,.4); }.step-tab.complete .step-marker { border-color:#59e0ca; color:#dffbff; background:rgba(13,132,119,.5); }.step-connector { flex:0 0 28px; height:2px; background:rgba(77,167,220,.54); color:#78cfe9; font-size:16px; line-height:0; text-align:right; }.step-connector.complete { background:#55ddcc; color:#75f0df; }
}
@media (max-width:1100px) { .price { grid-template-columns:1fr; } } @media (max-width:900px) { .price { padding:14px; .training-core { padding:18px; border-radius:24px; }.workbench-head,.workbench-actions,.step-navigation { flex-direction:column; align-items:stretch; }.workbench-actions,.outline-action,.btn-nav,.taskfooter :deep(.el-button) { width:100%; }.step-indicator { align-items:stretch; flex-direction:column; width:100%; margin-right:0; margin-left:0; overflow:visible; border-radius:20px; }.step-tab { flex:0 0 auto; width:100%; }.step-connector { flex:0 0 22px; width:2px; height:22px; margin-left:29px; text-align:center; writing-mode:vertical-rl; }.taskfooter { gap:14px; }.cnetent { flex-direction:column; .left,.right { width:100%; } } } }
.step-tab.active { border-color:rgba(89,226,255,.85); color:#fff; background:linear-gradient(115deg,rgba(4,109,155,.78),rgba(5,62,102,.64)); box-shadow:0 0 18px rgba(24,183,235,.22),inset 0 1px 0 rgba(176,246,255,.2); }.step-tab.active .step-marker { border-color:#72e8ff; color:#fff; background:linear-gradient(135deg,#08b7e8,#1375d0); box-shadow:0 0 0 5px rgba(25,179,237,.14),0 0 18px rgba(46,208,255,.58); }.step-tab.complete { color:#dffbff; }.step-tab.complete .step-marker { border-color:rgba(84,230,221,.85); color:#062b37; background:#6ef0e1; box-shadow:0 0 12px rgba(79,231,218,.35); }.step-connector.complete { background:linear-gradient(90deg,#6ef0e1,#1cb8e9); color:#63e6f1; box-shadow:0 0 9px rgba(53,218,235,.46); }
.price .step-indicator { position:relative; display:flex; align-items:center; gap:0; width:calc(100% + 24px); margin:0 -12px 32px; padding:18px 22px; overflow-x:auto; border-radius:18px; background:linear-gradient(90deg,rgba(6,42,61,.58),rgba(1,18,31,.42)),rgba(0,0,0,.3); }
.price .step-tab { display:flex; flex:1 0 145px; align-items:center; min-width:0; min-height:66px; padding:8px 10px; border:1px solid rgba(68,141,177,.35); border-radius:8px; color:#9ab3d0; background:rgba(3,27,43,.52); text-align:left; }
.price .step-tab.active { border-color:rgba(89,226,255,0.85); color:#fff; background:linear-gradient(115deg,rgba(4,109,155,0.78),rgba(5,62,102,0.64)); box-shadow:0 0 18px rgba(24,183,235,.22); }
.price .step-copy { display:grid; min-width:0; gap:4px; margin-left:10px; }.price .step-name { display:-webkit-box; overflow:hidden; font-size:14px; line-height:1.25; -webkit-box-orient:vertical; -webkit-line-clamp:2; }.price .step-action { color:#66b6d4; font-size:10px; font-weight:800; }.price .step-connector { position:relative; flex:0 1 40px; min-width:18px; height:2px; margin:0 8px; overflow:visible; background:rgba(98,169,201,.34); }.price .step-connector::after { position:absolute; top:50%; right:-1px; width:6px; height:6px; border-top:1px solid #73b1ce; border-right:1px solid #73b1ce; content:""; transform:translateY(-50%) rotate(45deg); }.price .step-tab.complete + .step-connector { background:linear-gradient(90deg,#6ef0e1,#1cb8e9); }
@media (max-width:900px) { .price .step-indicator { flex-direction:column; width:100%; margin-right:0; margin-left:0; overflow:visible; }.price .step-tab { flex:0 0 auto; width:100%; }.price .step-connector { flex:0 0 20px; min-width:0; width:2px; height:20px; margin-left:29px; } }
.workbench-head--actions-only {
  justify-content: flex-end;
  margin-bottom: 18px;
  padding-bottom: 0;
  border-bottom: 0;
}
</style>
