539 lines
20 KiB
Vue
539 lines
20 KiB
Vue
<template>
|
||
<div class="p-4 max-w-3xl mx-auto min-h-screen">
|
||
<!-- 标题部分 -->
|
||
<div class="card mb-4 p-4 bg-gradient-to-r from-blue-500 to-blue-600 rounded-lg shadow-lg text-white">
|
||
<h1 class="text-2xl font-extrabold mb-2">专业报告定价配置</h1>
|
||
<p class="opacity-90">
|
||
请选择报告类型并设置定价策略,助您实现精准定价
|
||
</p>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<van-field readonly clickable name="reportType" v-model="selectedReportText" label="报告类型"
|
||
placeholder="点击选择报告" @click="showPicker = true" class="card">
|
||
<template #label>
|
||
<span class="text-blue-600 font-medium">📝 选择报告</span>
|
||
</template>
|
||
<template #right-icon>
|
||
<van-icon name="arrow-down" class="text-gray-400" />
|
||
</template>
|
||
</van-field>
|
||
<van-popup v-model:show="showPicker" position="bottom">
|
||
<van-picker :columns="reportOptions" :default-index="0" @confirm="onConfirm"
|
||
@cancel="showPicker = false" />
|
||
</van-popup>
|
||
</div>
|
||
|
||
<div v-if="selectedReportText" class="space-y-6">
|
||
<!-- 配置卡片 -->
|
||
<div class="card">
|
||
<!-- 当前报告标题 -->
|
||
<div class="flex items-center mb-6">
|
||
<van-icon name="description" class="text-blue-500 text-xl mr-2" />
|
||
<h2 class="text-xl font-semibold text-gray-800">
|
||
{{ selectedReportText }}配置
|
||
</h2>
|
||
</div>
|
||
|
||
<!-- 显示当前产品的基础成本信息 -->
|
||
<div v-if="productConfigData && productConfigData.cost_price"
|
||
class="px-4 py-2 mb-4 bg-gray-50 border border-gray-200 rounded-lg shadow-sm">
|
||
<div class="text-lg font-semibold text-gray-700">
|
||
报告基础配置信息
|
||
</div>
|
||
<div class="mt-1 text-sm text-gray-600">
|
||
<div>
|
||
基础成本价:<span class="font-medium">{{
|
||
productConfigData.cost_price
|
||
}}</span>
|
||
元
|
||
</div>
|
||
<!-- <div>区间起始价:<span class="font-medium">{{ productConfigData.price_range_min }}</span> 元</div> -->
|
||
<div>
|
||
最高设定金额上限:<span class="font-medium">{{
|
||
productConfigData.price_range_max
|
||
}}</span>
|
||
元
|
||
</div>
|
||
<div>
|
||
最高设定比例上限:<span class="font-medium">{{
|
||
priceRatioMax
|
||
}}</span>
|
||
%
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分隔线 -->
|
||
<van-divider :style="{ borderColor: '#e5e7eb', padding: '0 16px' }" class="my-6">
|
||
<van-icon name="exchange" class="text-gray-400 mx-2" />
|
||
<span class="text-gray-400 text-sm">成本策略配置</span>
|
||
</van-divider>
|
||
|
||
<!-- 加价金额 -->
|
||
<van-field v-model.number="configData.price_increase_amount" label="加价金额" type="number" placeholder="0"
|
||
@blur="validateDecimal('price_increase_amount')" class="custom-field"
|
||
:class="{ 'van-field--error': increaseError }">
|
||
<template #label>
|
||
<span class="text-gray-600 font-medium">🚀 加价金额</span>
|
||
</template>
|
||
<template #extra>
|
||
<span class="text-blue-500 font-medium ml-2">元</span>
|
||
</template>
|
||
</van-field>
|
||
<div class="text-xs text-gray-400 mt-1">
|
||
提示:最大加价金额为{{ priceIncreaseAmountMax }}元<br />
|
||
说明:加价金额是在基础成本价上增加的额外费用,决定下级报告的最低定价,您将获得所有输入的金额利润。
|
||
</div>
|
||
|
||
<!-- 分隔线 -->
|
||
<van-divider :style="{ borderColor: '#e5e7eb', padding: '0 16px' }" class="my-6">
|
||
<van-icon name="exchange" class="text-gray-400 mx-2" />
|
||
<span class="text-gray-400 text-sm">定价策略配置</span>
|
||
</van-divider>
|
||
|
||
<!-- 定价区间最低 -->
|
||
<van-field v-model.number="configData.price_range_from" label="定价区间最低" type="number" placeholder="0"
|
||
@blur="
|
||
() => {
|
||
validateDecimal('price_range_from');
|
||
validateRange();
|
||
}
|
||
" class="custom-field" :class="{ 'van-field--error': rangeError }">
|
||
<template #label>
|
||
<span class="text-gray-600 font-medium">💰 最低金额</span>
|
||
</template>
|
||
<template #extra>
|
||
<span class="text-blue-500 font-medium ml-2">元</span>
|
||
</template>
|
||
</van-field>
|
||
<div class="text-xs text-gray-400 mt-1">
|
||
提示:最低金额不能低于(基础最低
|
||
{{ productConfigData?.price_range_min || 0 }}元 +
|
||
加价金额)<br />
|
||
说明:设定的最低金额为定价区间的起始值,若下级设定的报告金额在区间内,则区间内部分将按比例获得收益。
|
||
</div>
|
||
|
||
<!-- 定价区间最高 -->
|
||
<van-field v-model.number="configData.price_range_to" label="定价区间最高" type="number" placeholder="0"
|
||
@blur="
|
||
() => {
|
||
validateDecimal('price_range_to');
|
||
validateRange();
|
||
}
|
||
" class="custom-field" :class="{ 'van-field--error': rangeError }">
|
||
<template #label>
|
||
<span class="text-gray-600 font-medium">💰 最高金额</span>
|
||
</template>
|
||
<template #extra>
|
||
<span class="text-blue-500 font-medium ml-2">元</span>
|
||
</template>
|
||
</van-field>
|
||
<div class="text-xs text-gray-400 mt-1">
|
||
提示:最高金额不能超过上限({{
|
||
productConfigData?.price_range_max || 0
|
||
}}元)和大于最低金额({{ priceIncreaseMax }}元)<br />
|
||
说明:设定的最高金额为定价区间的结束值,若下级设定的报告金额在区间内,则区间内部分将按比例获得收益。
|
||
</div>
|
||
|
||
<!-- 收取比例 -->
|
||
<van-field v-model.number="configData.price_ratio" label="收取比例" type="digit" placeholder="0" @blur="
|
||
() => {
|
||
validateRatio();
|
||
}
|
||
" class="custom-field" :class="{ 'van-field--error': ratioError }">
|
||
<template #label>
|
||
<span class="text-gray-600 font-medium">📈 收取比例</span>
|
||
</template>
|
||
<template #extra>
|
||
<span class="text-blue-500 font-medium ml-2">%</span>
|
||
</template>
|
||
</van-field>
|
||
<div class="text-xs text-gray-400 mt-1">
|
||
提示:最大收取比例为{{ priceRatioMax }}%<br />
|
||
说明:收取比例表示对定价区间内(即报告金额超过最低金额,小于最高金额的部分)的金额,按此比例进行利润分成。
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 保存按钮 -->
|
||
<van-button type="primary" block
|
||
class="shadow-lg bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white rounded-xl h-12"
|
||
@click="handleSubmit">
|
||
<van-icon name="success" class="mr-2" />
|
||
保存当前报告配置
|
||
</van-button>
|
||
</div>
|
||
<!-- 未选择提示 -->
|
||
<div v-else class="text-center py-12">
|
||
<van-icon name="warning" class="text-gray-400 text-4xl mb-4" />
|
||
<p class="text-gray-500">请先选择需要配置的报告类型</p>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, computed, onMounted } from "vue";
|
||
import { showToast } from "vant";
|
||
import { settings } from "nprogress";
|
||
|
||
// 报告类型选项
|
||
const reportOptions = [
|
||
{ text: "小微企业", value: "companyinfo", id: 2 },
|
||
{ text: "贷前风险", value: "preloanbackgroundcheck", id: 5 },
|
||
{ text: "个人大数据", value: "personaldata", id: 27 },
|
||
{ text: "入职风险", value: "backgroundcheck", id: 1 },
|
||
{ text: "家政风险", value: "homeservice", id: 3 },
|
||
{ text: "婚恋风险", value: "marriage", id: 4 },
|
||
// { text: "租赁风险", value: "rentalrisk", id: 6 },
|
||
// { text: "个人风险", value: "riskassessment", id: 7 },
|
||
];
|
||
|
||
// 状态管理
|
||
const showPicker = ref(false);
|
||
const selectedReport = ref(reportOptions[0]);
|
||
const selectedReportText = ref(reportOptions[0].text);
|
||
const selectedReportId = ref(reportOptions[0].id);
|
||
|
||
const configData = ref({});
|
||
const productConfigData = ref({});
|
||
const priceIncreaseMax = ref(null);
|
||
const priceIncreaseAmountMax = ref(null);
|
||
const priceRatioMax = ref(null);
|
||
const rangeError = ref(false);
|
||
const ratioError = ref(false);
|
||
const increaseError = ref(false);
|
||
|
||
// 金额输入格式验证:确保最多两位小数
|
||
const validateDecimal = (field) => {
|
||
const value = configData.value[field];
|
||
if (value === null || value === undefined) return;
|
||
|
||
const numValue = Number(value);
|
||
if (isNaN(numValue)) {
|
||
configData.value[field] = null;
|
||
return;
|
||
}
|
||
const fixedValue = parseFloat(numValue.toFixed(2));
|
||
configData.value[field] = fixedValue;
|
||
|
||
if (field === "price_increase_amount") {
|
||
if (fixedValue > priceIncreaseAmountMax.value) {
|
||
configData.value[field] = priceIncreaseAmountMax.value;
|
||
showToast(`加价金额最大为${priceIncreaseAmountMax.value}元`);
|
||
increaseError.value = true;
|
||
setTimeout(() => {
|
||
increaseError.value = false;
|
||
}, 2000);
|
||
} else {
|
||
increaseError.value = false;
|
||
}
|
||
// 当加价金额改变后,重新验证价格区间
|
||
validateRange();
|
||
}
|
||
};
|
||
|
||
// 价格区间验证(在 @blur 中调用)
|
||
const validateRange = () => {
|
||
console.log(
|
||
"configData.value.price_range_from",
|
||
configData.value.price_range_from
|
||
);
|
||
console.log(
|
||
"configData.value.price_range_to",
|
||
configData.value.price_range_to
|
||
);
|
||
|
||
if (
|
||
configData.value.price_range_from === null ||
|
||
configData.value.price_range_to === null
|
||
) {
|
||
rangeError.value = false;
|
||
return;
|
||
}
|
||
|
||
if (
|
||
isNaN(configData.value.price_range_from) ||
|
||
isNaN(configData.value.price_range_to)
|
||
)
|
||
return;
|
||
const additional = configData.value.price_increase_amount || 0;
|
||
const minAllowed = parseFloat(
|
||
(
|
||
Number(productConfigData.value.cost_price) + Number(additional)
|
||
).toFixed(2)
|
||
); // 使用成本价作为最小值
|
||
const maxAllowed = productConfigData.value.price_range_max; // 使用产品配置中的最大价格作为最大值
|
||
if (configData.value.price_range_from < minAllowed) {
|
||
configData.value.price_range_from = minAllowed;
|
||
showToast(`最低金额不能低于成本价 ${minAllowed}元`);
|
||
rangeError.value = true;
|
||
closeRangeError();
|
||
|
||
configData.value.price_range_to = parseFloat(
|
||
(
|
||
Number(configData.value.price_range_from) +
|
||
Number(priceIncreaseMax.value)
|
||
).toFixed(2)
|
||
);
|
||
return;
|
||
}
|
||
|
||
if (configData.value.price_range_to < configData.value.price_range_from) {
|
||
showToast("最高金额不能低于最低金额");
|
||
if (
|
||
configData.value.price_range_from + priceIncreaseMax.value >
|
||
maxAllowed
|
||
) {
|
||
configData.value.price_range_to = maxAllowed;
|
||
} else {
|
||
configData.value.price_range_to =
|
||
configData.value.price_range_from + priceIncreaseMax.value;
|
||
}
|
||
rangeError.value = true;
|
||
closeRangeError();
|
||
return;
|
||
}
|
||
const diff = parseFloat(
|
||
(
|
||
configData.value.price_range_to - configData.value.price_range_from
|
||
).toFixed(2)
|
||
);
|
||
if (diff > priceIncreaseMax.value) {
|
||
showToast(`价格区间最大差值为${priceIncreaseMax.value}元`);
|
||
configData.value.price_range_to =
|
||
configData.value.price_range_from + priceIncreaseMax.value;
|
||
closeRangeError();
|
||
return;
|
||
}
|
||
|
||
if (configData.value.price_range_to > maxAllowed) {
|
||
configData.value.price_range_to = maxAllowed;
|
||
showToast(`最高金额不能超过 ${maxAllowed}元`);
|
||
closeRangeError();
|
||
}
|
||
|
||
if (!rangeError.value) {
|
||
rangeError.value = false;
|
||
}
|
||
};
|
||
|
||
// 收取比例验证(修改为保留两位小数,不再四舍五入取整)
|
||
const validateRatio = () => {
|
||
let value = configData.value.price_ratio;
|
||
if (value === null || value === undefined) return;
|
||
|
||
const numValue = Number(value);
|
||
if (isNaN(numValue)) {
|
||
configData.value.price_ratio = null;
|
||
ratioError.value = true;
|
||
return;
|
||
}
|
||
|
||
if (numValue > priceRatioMax.value) {
|
||
configData.value.price_ratio = priceRatioMax.value;
|
||
showToast(`收取比例最大为${priceRatioMax.value}%`);
|
||
ratioError.value = true;
|
||
setTimeout(() => {
|
||
ratioError.value = false;
|
||
}, 1000);
|
||
} else if (numValue < 0) {
|
||
configData.value.price_ratio = 0;
|
||
ratioError.value = true;
|
||
} else {
|
||
configData.value.price_ratio = parseFloat(numValue.toFixed(2));
|
||
ratioError.value = false;
|
||
}
|
||
};
|
||
|
||
// 获取配置
|
||
const getConfig = async () => {
|
||
try {
|
||
const { data, error } = await useApiFetch(
|
||
"/agent/membership/user_config?product_id=" + selectedReportId.value
|
||
)
|
||
.get()
|
||
.json();
|
||
|
||
if (data.value?.code === 200) {
|
||
const respConfigData = data.value.data.agent_membership_user_config;
|
||
configData.value = {
|
||
id: respConfigData.product_id,
|
||
price_range_from: respConfigData.price_range_from || null,
|
||
price_range_to: respConfigData.price_range_to || null,
|
||
price_ratio: respConfigData.price_ratio * 100 || null, // 转换为百分比
|
||
price_increase_amount:
|
||
respConfigData.price_increase_amount || null,
|
||
};
|
||
console.log("configData", configData.value);
|
||
// const respProductConfigData = data.value.data.product_config
|
||
productConfigData.value = data.value.data.product_config;
|
||
// 设置动态限制值
|
||
priceIncreaseMax.value = data.value.data.price_increase_max;
|
||
priceIncreaseAmountMax.value =
|
||
data.value.data.price_increase_amount;
|
||
priceRatioMax.value = data.value.data.price_ratio * 100;
|
||
}
|
||
} catch (error) {
|
||
showToast("配置加载失败");
|
||
}
|
||
};
|
||
|
||
// 提交处理
|
||
const handleSubmit = async () => {
|
||
try {
|
||
if (!finalValidation()) {
|
||
return;
|
||
}
|
||
// 前端数据转换
|
||
const submitData = {
|
||
product_id: configData.value.id,
|
||
price_range_from: configData.value.price_range_from || 0,
|
||
price_range_to: configData.value.price_range_to || 0,
|
||
price_ratio: (configData.value.price_ratio || 0) / 100, // 转换为小数
|
||
price_increase_amount: configData.value.price_increase_amount || 0,
|
||
};
|
||
console.log("submitData", submitData);
|
||
const { data, error } = await useApiFetch(
|
||
"/agent/membership/save_user_config"
|
||
)
|
||
.post(submitData)
|
||
.json();
|
||
|
||
if (data.value?.code === 200) {
|
||
setTimeout(() => {
|
||
showToast("保存成功");
|
||
}, 500);
|
||
getConfig();
|
||
}
|
||
} catch (error) {
|
||
showToast("保存失败,请稍后重试");
|
||
}
|
||
};
|
||
// 最终验证函数
|
||
const finalValidation = () => {
|
||
// 校验最低金额:不能为空且大于0
|
||
if (
|
||
!configData.value.price_range_from ||
|
||
configData.value.price_range_from <= 0
|
||
) {
|
||
showToast("最低金额不能为空");
|
||
return false;
|
||
}
|
||
// 校验最高金额:不能为空且大于0
|
||
if (
|
||
!configData.value.price_range_to ||
|
||
configData.value.price_range_to <= 0
|
||
) {
|
||
showToast("最高金额不能为空");
|
||
return false;
|
||
}
|
||
// 校验收取比例:不能为空且大于0
|
||
if (!configData.value.price_ratio || configData.value.price_ratio <= 0) {
|
||
showToast("收取比例不能为空");
|
||
return false;
|
||
}
|
||
// 验证最低金额必须小于最高金额
|
||
if (configData.value.price_range_from >= configData.value.price_range_to) {
|
||
showToast("最低金额必须小于最高金额");
|
||
return false;
|
||
}
|
||
// 验证价格区间差值不能超过最大允许差值
|
||
const finalDiff = parseFloat(
|
||
(
|
||
configData.value.price_range_to - configData.value.price_range_from
|
||
).toFixed(2)
|
||
);
|
||
if (finalDiff > priceIncreaseMax.value) {
|
||
showToast(`价格区间最大差值为${priceIncreaseMax.value}元`);
|
||
return false;
|
||
}
|
||
// 验证最高金额不能超过产品配置中设定的上限
|
||
if (
|
||
configData.value.price_range_to >
|
||
productConfigData.value.price_range_max
|
||
) {
|
||
showToast(
|
||
`最高金额不能超过${productConfigData.value.price_range_max}元`
|
||
);
|
||
return false;
|
||
}
|
||
// 验证最低金额不能低于成本价+加价金额(加价金额允许为空)
|
||
const additional = configData.value.price_increase_amount || 0;
|
||
if (
|
||
configData.value.price_range_from <
|
||
productConfigData.value.cost_price + additional
|
||
) {
|
||
showToast(
|
||
`最低金额不能低于成本价${productConfigData.value.cost_price + additional
|
||
}元`
|
||
);
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
// 选择器确认
|
||
const onConfirm = ({ selectedOptions }) => {
|
||
selectedReport.value = selectedOptions[0];
|
||
selectedReportText.value = selectedOptions[0].text;
|
||
selectedReportId.value = selectedOptions[0].id;
|
||
showPicker.value = false;
|
||
// 重置错误状态
|
||
rangeError.value = false;
|
||
ratioError.value = false;
|
||
increaseError.value = false;
|
||
|
||
getConfig();
|
||
};
|
||
const closeRangeError = () => {
|
||
setTimeout(() => {
|
||
rangeError.value = false;
|
||
}, 2000);
|
||
};
|
||
onMounted(() => {
|
||
getConfig();
|
||
});
|
||
</script>
|
||
|
||
<style>
|
||
.custom-field {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.custom-field .van-field__body {
|
||
@apply bg-gray-50 rounded-lg px-3 py-2;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.custom-field:focus-within .van-field__body {
|
||
@apply ring-2 ring-blue-200;
|
||
}
|
||
|
||
.van-picker__toolbar {
|
||
@apply bg-gray-50 rounded-t-lg;
|
||
}
|
||
|
||
.van-picker__confirm {
|
||
@apply text-blue-500 font-medium;
|
||
}
|
||
|
||
.van-divider {
|
||
@apply before:bg-gray-100 after:bg-gray-100;
|
||
}
|
||
|
||
/* 错误状态样式 */
|
||
.van-field--error .van-field__control {
|
||
color: #ee0a24;
|
||
}
|
||
|
||
.van-field--error .van-field__label {
|
||
color: inherit;
|
||
}
|
||
|
||
.van-field--error .van-field__body {
|
||
@apply ring-2 ring-red-200 bg-red-50;
|
||
}
|
||
</style>
|