refactor: switch to free-trial/private-deploy/buyout pricing, fix feature gaps, add SEO landing + deploy config

This commit is contained in:
TradeMate Dev
2026-07-12 08:09:28 +08:00
parent 9ca5d79d8a
commit 04924e3bc4
36 changed files with 1026 additions and 975 deletions
+10 -10
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
<meta name="description" content="TradeMate 外贸小助手 - AI 驱动的外贸智能工作台。智能翻译、客户管理、营销文案、报价单生成、WhatsApp 集成,专为外贸 SOHO 和小团队打造。" />
<meta name="description" content="TradeMate 外贸小助手 - AI 驱动的外贸智能工作台。智能翻译、客户管理、营销文案、报价单生成、AI 数字员工,支持免费试用、私有化部署与买断源码,专为外贸 SOHO 和小团队打造。" />
<meta name="keywords" content="外贸小助手,TradeMate,外贸AI工具,智能翻译,客户管理,营销文案,报价单,WhatsApp集成,外贸SOHO,外贸工具" />
<meta name="author" content="北京宇之然科技中心" />
<meta name="robots" content="index, follow" />
@@ -18,11 +18,11 @@
<meta property="og:description" content="专为外贸 SOHO 和小团队打造的 AI 智能工作台。集成智能翻译、客户管理、营销文案、报价单、WhatsApp 沟通于一体。" />
<meta property="og:url" content="https://trade.yuzhiran.com/app/" />
<meta property="og:site_name" content="TradeMate" />
<meta property="og:image" content="https://trade.yuzhiran.com/og-image.png" />
<meta property="og:image" content="https://trade.yuzhiran.com/app/static/images/yzr/yuzhiran.jpg" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="TradeMate 外贸小助手" />
<meta name="twitter:description" content="AI 驱动的外贸智能工作台" />
<meta name="twitter:image" content="https://trade.yuzhiran.com/og-image.png" />
<meta name="twitter:description" content="AI 驱动的外贸智能工作台,支持免费试用、私有化部署与买断源码" />
<meta name="twitter:image" content="https://trade.yuzhiran.com/app/static/images/yzr/yuzhiran.jpg" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
@@ -39,12 +39,12 @@
"url": "https://trade.yuzhiran.com/app/",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Web, iOS, Android",
"description": "AI 驱动的外贸智能工作台,专为外贸 SOHO 和小团队打造",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "CNY"
},
"description": "AI 驱动的外贸智能工作台,专为外贸 SOHO 和小团队打造,支持免费试用、私有化部署与买断源码",
"offers": [
{ "@type": "Offer", "name": "免费试用", "price": "0", "priceCurrency": "CNY" },
{ "@type": "Offer", "name": "私有化部署", "price": "39800", "priceCurrency": "CNY", "description": "年付授权,独立部署到客户自有服务器" },
{ "@type": "Offer", "name": "买断源码", "price": "98000", "priceCurrency": "CNY", "description": "一次性买断完整前后端源码,可二次开发" }
],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
+65 -70
View File
@@ -1,7 +1,7 @@
<template>
<view class="container">
<view class="header">
<text class="page-title">升级会员</text>
<text class="page-title">选择方案</text>
<text class="current-plan" v-if="currentPlan">当前: {{ planLabel(currentPlan) }}</text>
</view>
@@ -16,8 +16,7 @@
<text class="plan-name">{{ plan.name }}</text>
<text class="plan-price">
<text class="price-num">¥{{ plan.price }}</text>
<text class="price-unit" v-if="plan.price > 0">/</text>
<text class="price-unit" v-else>免费</text>
<text class="price-unit">{{ plan.unit }}</text>
</text>
<view class="plan-features">
<text class="feature" v-for="(f, i) in plan.features" :key="i"> {{ f }}</text>
@@ -26,92 +25,87 @@
</view>
</view>
<button
class="upgrade-btn"
@click="handleUpgrade"
:disabled="!selected || selected === currentPlan || loading"
>
{{ loading ? '处理中...' : (selected === currentPlan ? '当前方案' : '立即升级') }}
<button class="upgrade-btn" @click="handleSelect" :disabled="!selected || loading">
{{ loading ? '处理中...' : actionText }}
</button>
<!-- H5 Native 支付显示二维码 -->
<view class="qr-modal" v-if="showQr">
<!-- Enterprise contact popup -->
<view class="qr-modal" v-if="showContact">
<view class="qr-box">
<text class="qr-title">请使用微信扫码支付</text>
<image class="qr-img" :src="qrCodeUrl" mode="widthFix" />
<text class="qr-hint">打开微信扫一扫完成支付</text>
<text class="qr-close" @click="showQr = false">关闭</text>
<text class="qr-title">{{ contactTitle }}</text>
<input class="c-input" v-model="form.name" placeholder="您的称呼" />
<input class="c-input" v-model="form.company" placeholder="公司名称(选填)" />
<input class="c-input" v-model="form.phone" placeholder="手机号(必填)" />
<textarea class="c-area" v-model="form.message" placeholder="部署规模 / 定制需求(选填)" />
<button class="c-submit" :disabled="contactLoading" @click="submitContact">提交申请</button>
<text class="qr-close" @click="showContact = false">关闭</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { paymentApi } from '@/utils/api.js'
import { leadApi } from '@/utils/api.js'
const plans = ref([])
const PRICING = [
{ id: 'free', name: '免费试用', price: '0', unit: '', features: ['30 积分(一次性)', '每日 1000 字免费翻译', '网页端 / 插件通用'], action: 'trial' },
{ id: 'private', name: '私有化部署', price: '39,800', unit: '/年', features: ['独立部署、数据私有', '不限账号与调用量', '对接自有 AI 模型', '一年技术支持'], action: 'contact' },
{ id: 'buyout', name: '买断源码', price: '98,000', unit: '/一次性', features: ['完整前后端源码', '可二次开发', '永久授权', '文档与社区支持'], action: 'contact' },
]
const plans = ref(PRICING)
const currentPlan = ref('free')
const selected = ref('')
const loading = ref(false)
const showQr = ref(false)
const qrCodeUrl = ref('')
onShow(async () => {
try {
const [planRes, subRes] = await Promise.all([
paymentApi.plans(),
paymentApi.subscription(),
])
plans.value = planRes.plans || []
currentPlan.value = subRes.plan || 'free'
} catch (_) {}
const showContact = ref(false)
const contactLoading = ref(false)
const contactType = ref('private')
const form = ref({ name: '', company: '', phone: '', message: '' })
const contactTitle = computed(() => contactType.value === 'private' ? '申请私有化部署' : '咨询源码买断')
const actionText = computed(() => {
const p = plans.value.find(x => x.id === selected.value)
if (!p) return '请选择方案'
if (p.action === 'trial') return '免费使用'
return contactType.value === 'private' ? '申请私有化部署' : '咨询源码买断'
})
onShow(() => { currentPlan.value = 'free' })
const planLabel = (id) => {
const map = { free: '免费版', pro: 'Pro 版', enterprise: '企业版' }
const map = { free: '免费版', private: '私有化部署', buyout: '买断源码' }
return map[id] || id
}
const handleUpgrade = async () => {
if (!selected.value || selected.value === currentPlan.value) return
loading.value = true
const handleSelect = () => {
const p = plans.value.find(x => x.id === selected.value)
if (!p) return
if (p.action === 'trial') {
uni.showToast({ title: '已使用免费版', icon: 'success' })
return
}
contactType.value = p.id
form.value = { name: '', company: '', phone: '', message: '' }
showContact.value = true
}
const submitContact = async () => {
if (!form.value.name.trim() || !form.value.phone.trim()) {
uni.showToast({ title: '请填写称呼与手机号', icon: 'none' })
return
}
contactLoading.value = true
try {
// #ifdef MP-WEIXIN
const payType = 'jsapi'
// #endif
// #ifdef H5
const payType = 'native'
// #endif
const res = await paymentApi.createOrder(selected.value, payType)
if (res.amount === 0) {
uni.showToast({ title: '已切换为免费版', icon: 'success' })
currentPlan.value = selected.value
return
}
if (res.pay_type === 'jsapi' && res.pay_params) {
uni.requestPayment({
provider: 'wxpay',
...res.pay_params,
success: () => {
uni.showToast({ title: '支付成功', icon: 'success' })
currentPlan.value = selected.value
},
fail: (err) => {
uni.showToast({ title: err.errMsg || '支付失败', icon: 'none' })
},
})
} else if (res.pay_type === 'native' && res.code_url) {
qrCodeUrl.value = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(res.code_url)}`
showQr.value = true
}
await leadApi.create({ type: contactType.value, ...form.value })
uni.showToast({ title: '提交成功,商务会联系您', icon: 'success' })
showContact.value = false
} catch (err) {
uni.showToast({ title: err.message || '操作失败', icon: 'none' })
uni.showToast({ title: err.message || '提交失败', icon: 'none' })
} finally {
loading.value = false
contactLoading.value = false
}
}
</script>
@@ -136,9 +130,10 @@ const handleUpgrade = async () => {
.upgrade-btn[disabled] { background: #a0cfff; }
.qr-modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 999; display: flex; align-items: center; justify-content: center; }
.qr-box { background: #fff; border-radius: 20rpx; padding: 50rpx; text-align: center; width: 500rpx; }
.qr-title { font-size: 30rpx; font-weight: 600; color: #333; margin-bottom: 30rpx; display: block; }
.qr-img { width: 300rpx; height: 300rpx; display: block; margin: 0 auto 30rpx; }
.qr-hint { font-size: 24rpx; color: #999; display: block; margin-bottom: 20rpx; }
.qr-close { font-size: 26rpx; color: #1890ff; display: block; }
.qr-box { background: #fff; border-radius: 20rpx; padding: 40rpx; width: 560rpx; }
.qr-title { font-size: 30rpx; font-weight: 600; color: #333; margin-bottom: 24rpx; display: block; text-align: center; }
.c-input { border: 2rpx solid #e8e8e8; border-radius: 10rpx; padding: 16rpx; font-size: 26rpx; margin-bottom: 16rpx; width: 100%; box-sizing: border-box; }
.c-area { border: 2rpx solid #e8e8e8; border-radius: 10rpx; padding: 16rpx; font-size: 26rpx; margin-bottom: 16rpx; width: 100%; box-sizing: border-box; height: 120rpx; }
.c-submit { width: 100%; height: 80rpx; background: #1890ff; color: #fff; border: none; border-radius: 10rpx; font-size: 28rpx; margin-bottom: 12rpx; }
.qr-close { font-size: 26rpx; color: #1890ff; display: block; text-align: center; }
</style>
+4
View File
@@ -259,6 +259,10 @@ export const paymentApi = {
request('/payment/create-order', 'POST', { plan, pay_type: payType }),
}
export const leadApi = {
create: (data) => request('/leads', 'POST', data),
}
export const creditApi = {
balance: () => request('/credits/balance'),
history: (page = 1, size = 20) => request(`/credits/history?page=${page}&size=${size}`),