first commit
This commit is contained in:
225
src/pages/promote.vue
Normal file
225
src/pages/promote.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<script setup>
|
||||
import PriceInputPopup from '@/components/PriceInputPopup.vue'
|
||||
import VipBanner from '@/components/VipBanner.vue'
|
||||
|
||||
definePage({ layout: 'default' })
|
||||
|
||||
const showPricePicker = ref(false)
|
||||
const pickerProductConfig = ref(null)
|
||||
const productConfig = ref(null)
|
||||
const linkIdentifier = ref('')
|
||||
const showQRcode = ref(false)
|
||||
const promotionForm = ref(null)
|
||||
const loadingConfig = ref(false)
|
||||
const generating = ref(false)
|
||||
|
||||
const formData = ref({
|
||||
productType: '',
|
||||
clientPrice: null,
|
||||
})
|
||||
|
||||
const availableReportTypes = computed(() => {
|
||||
if (!productConfig.value?.length)
|
||||
return []
|
||||
return productConfig.value
|
||||
.map(item => ({
|
||||
id: item.product_id,
|
||||
label: item.product_name || `产品${item.product_id}`,
|
||||
value: item.product_en || '',
|
||||
}))
|
||||
.filter(item => !!item.value)
|
||||
})
|
||||
|
||||
const costPrice = computed(() => {
|
||||
if (!pickerProductConfig.value)
|
||||
return '0.00'
|
||||
let platformPricing = 0
|
||||
platformPricing += pickerProductConfig.value.cost_price
|
||||
if (formData.value.clientPrice > pickerProductConfig.value.p_pricing_standard) {
|
||||
platformPricing += (formData.value.clientPrice - pickerProductConfig.value.p_pricing_standard) * pickerProductConfig.value.p_overpricing_ratio
|
||||
}
|
||||
|
||||
if (pickerProductConfig.value.a_pricing_standard > platformPricing && pickerProductConfig.value.a_pricing_end > platformPricing && pickerProductConfig.value.a_overpricing_ratio > 0) {
|
||||
if (formData.value.clientPrice > pickerProductConfig.value.a_pricing_standard) {
|
||||
if (formData.value.clientPrice > pickerProductConfig.value.a_pricing_end) {
|
||||
platformPricing += (pickerProductConfig.value.a_pricing_end - pickerProductConfig.value.a_pricing_standard) * pickerProductConfig.value.a_overpricing_ratio
|
||||
}
|
||||
else {
|
||||
platformPricing += (formData.value.clientPrice - pickerProductConfig.value.a_pricing_standard) * pickerProductConfig.value.a_overpricing_ratio
|
||||
}
|
||||
}
|
||||
}
|
||||
return safeTruncate(platformPricing)
|
||||
})
|
||||
|
||||
const promotionRevenue = computed(() => {
|
||||
return safeTruncate(formData.value.clientPrice - costPrice.value)
|
||||
})
|
||||
|
||||
function safeTruncate(num, decimals = 2) {
|
||||
if (Number.isNaN(num) || !Number.isFinite(num))
|
||||
return '0.00'
|
||||
const factor = 10 ** decimals
|
||||
const scaled = Math.trunc(num * factor)
|
||||
return (scaled / factor).toFixed(decimals)
|
||||
}
|
||||
|
||||
function selectProductType(reportTypeValue) {
|
||||
const reportType = availableReportTypes.value.find(item => item.id === reportTypeValue || item.value === reportTypeValue)
|
||||
if (!reportType)
|
||||
return
|
||||
|
||||
formData.value.productType = reportType.value
|
||||
|
||||
const matchedConfig = productConfig.value?.find(item => item.product_id === reportType.id)
|
||||
if (!matchedConfig) {
|
||||
pickerProductConfig.value = null
|
||||
formData.value.clientPrice = null
|
||||
uni.showToast({ title: '该报告暂不可推广', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
pickerProductConfig.value = matchedConfig
|
||||
formData.value.clientPrice = matchedConfig.cost_price
|
||||
}
|
||||
|
||||
function onConfirmType(e) {
|
||||
const nextValue = e?.value?.[0] ?? e?.selectedOptions?.[0]?.value
|
||||
if (nextValue)
|
||||
selectProductType(nextValue)
|
||||
}
|
||||
|
||||
function onPriceChange(price) {
|
||||
formData.value.clientPrice = price
|
||||
}
|
||||
|
||||
function getPricePayload() {
|
||||
return safeTruncate(Number(formData.value.clientPrice))
|
||||
}
|
||||
|
||||
function openPricePicker() {
|
||||
if (!pickerProductConfig.value) {
|
||||
uni.showToast({ title: '请先选择报告类型', icon: 'none' })
|
||||
return
|
||||
}
|
||||
showPricePicker.value = true
|
||||
}
|
||||
|
||||
async function getPromoteConfig() {
|
||||
loadingConfig.value = true
|
||||
try {
|
||||
const { data, error } = await useApiFetch('/agent/product_config').get().json()
|
||||
if (data.value && !error.value && data.value.code === 200) {
|
||||
const list = data.value.data.AgentProductConfig || []
|
||||
productConfig.value = list
|
||||
const availableType = availableReportTypes.value[0]
|
||||
if (availableType) {
|
||||
selectProductType(availableType.value)
|
||||
}
|
||||
else {
|
||||
pickerProductConfig.value = null
|
||||
formData.value.productType = ''
|
||||
formData.value.clientPrice = null
|
||||
uni.showToast({ title: '暂无可推广产品', icon: 'none' })
|
||||
}
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: data.value?.msg || '获取配置失败', icon: 'none' })
|
||||
}
|
||||
catch {
|
||||
uni.showToast({ title: '获取配置失败', icon: 'none' })
|
||||
}
|
||||
finally {
|
||||
loadingConfig.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function generatePromotionCode() {
|
||||
if (generating.value)
|
||||
return
|
||||
try {
|
||||
await promotionForm.value.validate()
|
||||
}
|
||||
catch {
|
||||
return
|
||||
}
|
||||
generating.value = true
|
||||
try {
|
||||
const { data, error } = await useApiFetch('/agent/generating_link')
|
||||
.post({ product: formData.value.productType, price: getPricePayload() })
|
||||
.json()
|
||||
if (data.value && !error.value && data.value.code === 200) {
|
||||
linkIdentifier.value = data.value.data.link_identifier
|
||||
showQRcode.value = true
|
||||
}
|
||||
else {
|
||||
uni.showToast({ title: data.value?.msg || '生成推广码失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
catch {
|
||||
uni.showToast({ title: '生成推广码失败', icon: 'none' })
|
||||
}
|
||||
finally {
|
||||
generating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getPromoteConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="promote min-h-screen p-4">
|
||||
<view class="card mb-4 from-orange-200 to-orange-200/80 !bg-gradient-to-b">
|
||||
<view class="text-lg text-orange-500 font-bold">
|
||||
直推用户查询
|
||||
</view>
|
||||
<view class="mt-1 text-orange-400 font-bold">
|
||||
自定义价格,赚取差价
|
||||
</view>
|
||||
<view class="mt-6 rounded-xl bg-orange-100 px-4 py-2 text-gray-600">
|
||||
在下方 “自定义价格” 处选择报告类型,设置客户查询价(元)即可立即推广。
|
||||
</view>
|
||||
</view>
|
||||
<VipBanner />
|
||||
|
||||
<view class="card mb-4">
|
||||
<view class="mb-2 text-xl font-semibold">
|
||||
生成推广码
|
||||
</view>
|
||||
<wd-form ref="promotionForm" :model="formData">
|
||||
<wd-cell-group border>
|
||||
<wd-picker v-model="formData.productType" label="报告类型" label-width="100px" title="选择报告类型"
|
||||
:columns="[availableReportTypes]" prop="productType" placeholder="请选择报告类型"
|
||||
:rules="[{ required: true, message: '请选择报告类型' }]" @confirm="onConfirmType" />
|
||||
<wd-input v-model="formData.clientPrice" label="客户查询价" label-width="100px" placeholder="请输入价格"
|
||||
prop="clientPrice" clickable readonly suffix-icon="arrow-right"
|
||||
:rules="[{ required: true, message: '请输入客户查询价' }]" @click="openPricePicker" />
|
||||
</wd-cell-group>
|
||||
<view class="my-2 flex items-center justify-between text-sm text-gray-500">
|
||||
<view>
|
||||
推广收益为 <text class="text-orange-500">
|
||||
{{ promotionRevenue }}
|
||||
</text> 元
|
||||
</view>
|
||||
<view>
|
||||
我的成本为 <text class="text-orange-500">
|
||||
{{ costPrice }}
|
||||
</text> 元
|
||||
</view>
|
||||
</view>
|
||||
</wd-form>
|
||||
<wd-button type="primary" block :loading="generating || loadingConfig"
|
||||
:disabled="loadingConfig || !pickerProductConfig" @click="generatePromotionCode">
|
||||
点击立即推广
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<PriceInputPopup v-model:show="showPricePicker" :default-price="formData.clientPrice"
|
||||
:product-config="pickerProductConfig" @change="onPriceChange" />
|
||||
<QRcode v-model:show="showQRcode" :link-identifier="linkIdentifier" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user