2026-04-20 16:42:28 +08:00
|
|
|
|
<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) {
|
2026-04-23 14:57:35 +08:00
|
|
|
|
// 单列 wd-picker 的 e.value 为标量;多列时为数组。不能用 e.value[0] 取标量,否则字符串会变成首字符。
|
|
|
|
|
|
const raw = e?.value
|
|
|
|
|
|
const nextValue = Array.isArray(raw) ? raw[0] : raw
|
|
|
|
|
|
const fromItem = e?.selectedItems
|
|
|
|
|
|
const resolved = nextValue ?? (Array.isArray(fromItem) ? fromItem[0]?.value : fromItem?.value)
|
|
|
|
|
|
if (resolved != null && resolved !== '')
|
|
|
|
|
|
selectProductType(resolved)
|
2026-04-20 16:42:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 {
|
2026-04-23 14:57:35 +08:00
|
|
|
|
const { data, error } = await useApiFetch('/agent/product_config', { silent: true }).get().json()
|
2026-04-20 16:42:28 +08:00
|
|
|
|
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>
|