Files
qnc-webview-v3/src/components/PriceInputPopup.vue

186 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<van-popup v-model:show="show" destroy-on-close round position="bottom">
<div class="min-h-[500px] bg-gray-50 text-gray-600">
<div class="h-10 bg-white flex items-center justify-center font-semibold text-lg">设置客户查询价
</div>
<div class="card m-4">
<div class="flex items-center justify-between">
<div class="text-lg">
客户查询价 ()</div>
</div>
<div class="border-b border-gray-200">
<van-field v-model="price" type="number" label="¥" label-width="28"
:placeholder="`${productConfig.price_range_min} - ${productConfig.price_range_max}`"
@blur="onBlurPrice" class="!text-3xl" />
</div>
<div class="flex items-center justify-between mt-2">
<div>推广收益为<span class="text-orange-500"> {{ promotionRevenue }} </span></div>
</div>
<div class="flex items-center justify-between mt-2">
<div>底价成本为<span class="text-orange-500"> {{ baseCost }} </span></div>
<div>提价成本为<span class="text-orange-500"> {{ raiseCost }} </span></div>
</div>
</div>
<div class="card m-4">
<div class="text-lg mb-2">收益与成本说明</div>
<div>推广收益 = 客户查询价 - 我的成本</div>
<div>我的成本 = 实际底价 + 提价成本</div>
<div class="mt-1">提价成本设置{{ productConfig.price_threshold }}元以上的部分收取{{
rateFormat(productConfig.price_fee_rate)
}}的提价成本</div>
<div class="">设定范围<span class="text-orange-500">{{
productConfig.price_range_min }}</span> - <span class="text-orange-500">{{
productConfig.price_range_max }}</span></div>
</div>
<div class="px-4 pb-4">
<van-button class="w-full" round type="primary" size="large" @click="onConfirm">确认</van-button>
</div>
</div>
</van-popup>
</template>
<script setup>
const props = defineProps({
defaultPrice: {
type: Number,
required: true
},
productConfig: {
type: Object,
required: true
}
})
const { defaultPrice, productConfig } = toRefs(props)
const emit = defineEmits(["change"])
const show = defineModel("show")
const price = ref(null)
watch(show, () => {
price.value = defaultPrice.value
})
const costPrice = computed(() => {
if (!productConfig.value) return 0.00
// 新代理系统:成本价 = 实际底价actual_base_price+ 提价成本
const actualBasePrice = Number(productConfig.value.actual_base_price) || 0;
const priceNum = Number(price.value) || 0;
const priceThreshold = Number(productConfig.value.price_threshold) || 0;
const priceFeeRate = Number(productConfig.value.price_fee_rate) || 0;
// 计算提价成本
let priceCost = 0;
if (priceNum > priceThreshold) {
priceCost = (priceNum - priceThreshold) * priceFeeRate;
}
// 总成本 = 实际底价 + 提价成本
const totalCost = actualBasePrice + priceCost;
return safeTruncate(totalCost);
})
const rateFormat = (rate) => {
return rate * 100 + '%';
}
const baseCost = computed(() => {
if (!productConfig.value) return "0.00";
const actualBasePrice = Number(productConfig.value.actual_base_price) || 0;
return safeTruncate(actualBasePrice);
})
const raiseCost = computed(() => {
if (!productConfig.value) return "0.00";
const priceNum = Number(price.value) || 0;
const priceThreshold = Number(productConfig.value.price_threshold) || 0;
const priceFeeRate = Number(productConfig.value.price_fee_rate) || 0;
let priceCost = 0;
if (priceNum > priceThreshold) {
priceCost = (priceNum - priceThreshold) * priceFeeRate;
}
return safeTruncate(priceCost);
})
const promotionRevenue = computed(() => {
return safeTruncate(price.value - costPrice.value)
});
// 价格校验与修正逻辑
const validatePrice = (currentPrice) => {
const min = productConfig.value.price_range_min;
const max = productConfig.value.price_range_max;
let newPrice = Number(currentPrice);
let message = '';
// 处理无效输入
if (isNaN(newPrice)) {
newPrice = defaultPrice.value;
return { newPrice, message: '输入无效,请输入价格' };
}
// 处理小数位数(兼容科学计数法)
try {
const priceString = newPrice.toString()
const [_, decimalPart = ""] = priceString.split('.');
console.log(priceString, decimalPart)
// 当小数位数超过2位时处理
if (decimalPart.length > 2) {
newPrice = parseFloat(safeTruncate(newPrice));
message = '价格已自动格式化为两位小数';
}
} catch (e) {
console.error('价格格式化异常:', e);
}
// 范围校验(基于可能格式化后的值)
if (newPrice < min) {
message = `价格不能低于 ${min}`;
newPrice = min;
} else if (newPrice > max) {
message = `价格不能高于 ${max}`;
newPrice = max;
}
console.log(newPrice, message)
return { newPrice, message };
}
function safeTruncate(num, decimals = 2) {
if (isNaN(num) || !isFinite(num)) return "0.00";
const factor = 10 ** decimals;
const scaled = Math.trunc(num * factor);
const truncated = scaled / factor;
return truncated.toFixed(decimals);
}
const isManualConfirm = ref(false)
const onConfirm = () => {
if (isManualConfirm.value) return
const { newPrice, message } = validatePrice(price.value)
if (message) {
price.value = newPrice
showToast({ message });
} else {
emit("change", price.value)
show.value = false
}
}
const onBlurPrice = () => {
const { newPrice, message } = validatePrice(price.value)
if (message) {
isManualConfirm.value = true
price.value = newPrice
showToast({ message });
}
setTimeout(() => {
isManualConfirm.value = false
}, 0)
}
</script>
<style lang="scss" scoped></style>