|
|
|
|
|
<!--
|
|
|
|
|
|
* @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 app-container">
|
|
|
|
|
|
<div class="handren">
|
|
|
|
|
|
<div class="left">
|
|
|
|
|
|
<el-button>PSM模型</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="right">
|
|
|
|
|
|
<el-button @click="dialogVisible = true" type="primary" id="brtn">任务背景与目标</el-button>
|
|
|
|
|
|
<popModel :showModel="dialogVisible" title="任务背景:" @closePop="dialogVisible = false">
|
|
|
|
|
|
<template v-slot:content>
|
|
|
|
|
|
<p style="font-size: 1rem">
|
|
|
|
|
|
电商产品定价是一个复杂而关键的过程,它涉及多个因素和策略。主要包括折扣定价、客户心理定价、人群定价、组合定价、竞争定价、成本差异化定价、市场差异化定价、数量差异化定价等,通过灵活运用各种定价策略,商家可以制定出既符合市场需求又有利于企业发展的价格策略。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
在电商产品定价方面,PSM分析技术通过进行价格敏感度测试,基于问卷调查数据,可视化模型分析,给出合理定价区间。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
<br />
|
|
|
|
|
|
任务目标:
|
|
|
|
|
|
<br />
|
|
|
|
|
|
<br />
|
|
|
|
|
|
使用PSM模型给一款洗面奶进行定价。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
<br />
|
|
|
|
|
|
①数据源:价格区间问卷调查数据。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
<br />
|
|
|
|
|
|
②根据PSM模型分析得出合理定价区间。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</popModel>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<div class="taskfooter">
|
|
|
|
|
|
<el-button @click="submitTask">提交任务</el-button>
|
|
|
|
|
|
<el-button @click="resetTask">重新实训</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import * as echarts from "echarts";
|
|
|
|
|
|
import * as analysisAPI from "@/api/market-demand-mining.js";
|
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
|
import popModel from "@/views/components/popModal.vue";
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
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,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
const submitTask = () => {
|
|
|
|
|
|
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,
|
|
|
|
|
|
};
|
|
|
|
|
|
analysisAPI.productPricingSubmit({ stuAnswer: params, userId: userStore.userInfo.userId }).then((res) => {
|
|
|
|
|
|
proxy.$modal.msgSuccess("提交成功");
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
// 回显接口
|
|
|
|
|
|
const loadTaskInfo = () => {
|
|
|
|
|
|
analysisAPI.productPricingEcho({ userId: userStore.userInfo.userId }).then((res) => {
|
|
|
|
|
|
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();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
// 重新实训
|
|
|
|
|
|
const resetTask = () => {
|
|
|
|
|
|
analysisAPI.productPricingReTrain({ userId: userStore.userInfo.userId, module: "产品定价" }).then((res) => {
|
|
|
|
|
|
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 = "";
|
|
|
|
|
|
});
|
|
|
|
|
|
// loadTaskInfo();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
loadTaskInfo();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<style lang='scss' scoped>
|
|
|
|
|
|
.app-container {
|
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
|
:deep(.hidden-label) {
|
|
|
|
|
|
.el-radio__label {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.cnetent {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
.left {
|
|
|
|
|
|
width: 50%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.right {
|
|
|
|
|
|
width: 50%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|