feat: AI routing DB-driven, payment gateway full integration, WeChat mini-program CI/CD
- AI routing rules now stored in system_configs DB table instead of hardcoded config - Multi-model support via name|model composite key for same-provider routing - UnifiedPayService with HMAC-SHA256 gateway integration (alipay/wechat) - Admin payment panel: list, stats, search, filter, refund - WeChat mini-program CI/CD via miniprogram-ci (v1.0.9) - Translation quota extended to LLM provider tier - SearchService with DB-driven provider config (bing/google_cse/searxng) - Footer cleanup across admin/workspace/uni-app - Private key excluded from git tracking
This commit is contained in:
@@ -102,7 +102,7 @@ export function markAllRead() { return http.post('/notifications/read-all') }
|
||||
|
||||
export function getPlans() { return http.get('/payment/plans') }
|
||||
export function getSubscription() { return http.get('/payment/subscription') }
|
||||
export function createOrder(planId) { return http.post('/payment/create-order', { plan_id: planId }) }
|
||||
export function createOrder(plan, payType = 'alipay') { return http.post('/payment/create-order', { plan, pay_type: payType }) }
|
||||
|
||||
export function submitCertification(data) { return http.post('/certification/submit', data) }
|
||||
export function getCertificationStatus() { return http.get('/certification/status') }
|
||||
|
||||
@@ -67,37 +67,13 @@
|
||||
|
||||
<footer class="footer">
|
||||
<div class="footer-content">
|
||||
<div class="footer-section">
|
||||
<div class="footer-brand">TradeMate</div>
|
||||
<p class="footer-tagline">AI 外贸小助手 · 让外贸更简单</p>
|
||||
<div class="qrcode-row">
|
||||
<div class="qrcode-item">
|
||||
<img src="/images/yzr/yuzhiran.jpg" alt="微信公众号" class="qrcode-img" />
|
||||
<span>微信公众号</span>
|
||||
</div>
|
||||
<div class="qrcode-item">
|
||||
<img src="/images/yzr/yuzhiran-tech.jpg" alt="微信服务号" class="qrcode-img" />
|
||||
<span>微信服务号</span>
|
||||
</div>
|
||||
<div class="qrcode-item">
|
||||
<img src="/images/yzr/yuzhiran-yhl.jpg" alt="小程序" class="qrcode-img" />
|
||||
<span>小程序</span>
|
||||
</div>
|
||||
<div class="qrcode-item">
|
||||
<img src="/images/yzr/kefu.png" alt="客服" class="qrcode-img" />
|
||||
<span>微信客服</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>© {{ new Date().getFullYear() }} TradeMate 外贸小助手. 保留所有权利.</p>
|
||||
<div class="footer-links">
|
||||
<a href="http://beian.miit.gov.cn/" target="_blank">{{ beianInfo.icp }}</a>
|
||||
<a v-if="beianInfo.showGongan" :href="beianInfo.gonganLink" target="_blank" rel="noreferrer" class="gongan-link">
|
||||
<img src="/images/beian/gongan-beian.png" alt="公安备案" class="gongan-icon" />
|
||||
{{ beianInfo.gongan }}
|
||||
</a>
|
||||
</div>
|
||||
<p>© {{ new Date().getFullYear() }} TradeMate</p>
|
||||
<div class="footer-links">
|
||||
<a href="http://beian.miit.gov.cn/" target="_blank">{{ beianInfo.icp }}</a>
|
||||
<a v-if="beianInfo.showGongan" :href="beianInfo.gonganLink" target="_blank" rel="noreferrer" class="gongan-link">
|
||||
<img src="/images/beian/gongan-beian.png" alt="公安备案" class="gongan-icon" />
|
||||
{{ beianInfo.gongan }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -160,15 +136,8 @@ function handleLogout() {
|
||||
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
||||
.notif-badge :deep(.el-badge__content) { top: 8px; right: 4px; }
|
||||
.content { flex: 1; padding: 24px; overflow-y: auto; background: #f5f5f5; }
|
||||
.footer { text-align: center; color: #999; font-size: 12px; border-top: 1px solid #e8e8e8; background: #fff; flex-shrink: 0; }
|
||||
.footer-content { padding: 16px 24px 12px; }
|
||||
.footer-section { margin-bottom: 12px; }
|
||||
.footer-brand { font-size: 14px; font-weight: 700; color: #1890ff; margin-bottom: 2px; }
|
||||
.footer-tagline { color: #999; font-size: 11px; margin-bottom: 10px; }
|
||||
.qrcode-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
|
||||
.qrcode-item { display: flex; flex-direction: column; align-items: center; gap: 3px; color: #999; font-size: 10px; }
|
||||
.qrcode-img { width: 44px; height: 44px; border-radius: 6px; }
|
||||
.footer-bottom { border-top: 1px solid #eee; padding-top: 10px; display: flex; justify-content: center; align-items: center; gap: 14px; flex-wrap: wrap; }
|
||||
.footer { text-align: center; color: #999; font-size: 12px; border-top: 1px solid #e8e8e8; background: #fff; flex-shrink: 0; padding: 8px 24px; }
|
||||
.footer-content { display: flex; justify-content: center; align-items: center; gap: 14px; flex-wrap: wrap; }
|
||||
.footer-links { display: flex; gap: 14px; align-items: center; }
|
||||
.footer-links a { color: #999; text-decoration: none; }
|
||||
.footer-links a:hover { color: #1890ff; }
|
||||
|
||||
@@ -23,17 +23,51 @@
|
||||
<div style="text-align:center;margin-top:16px">
|
||||
<el-button v-if="p.id === currentPlan" type="default" disabled>当前套餐</el-button>
|
||||
<el-button v-else-if="p.id === 'free'" @click="handleFree">当前套餐</el-button>
|
||||
<el-button v-else type="primary" :loading="loadingId === p.id" @click="upgrade(p.id)">{{ p.price === 0 ? '当前套餐' : '升级' }}</el-button>
|
||||
<el-button v-else type="primary" :loading="loadingId === p.id" @click="showPayDialog(p.id)">{{ p.price === 0 ? '当前套餐' : '升级' }}</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty v-if="!plans.length" description="暂无套餐信息" />
|
||||
|
||||
<el-dialog v-model="payDialog.visible" title="选择支付方式" width="400px" :close-on-click-modal="false">
|
||||
<div style="text-align:center;padding:20px 0" v-if="!payDialog.orderCreated">
|
||||
<el-radio-group v-model="payDialog.payType" style="margin-bottom:24px">
|
||||
<el-radio-button value="alipay">
|
||||
<span style="display:flex;align-items:center;gap:6px;padding:0 20px">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="#1677ff"><path d="M21.422 15.358c-3.22-1.386-6.847-2.408-10.564-2.828 1.102-2.279 2.38-4.49 3.735-6.59H9.878c-.185-.413-.262-.912-.04-1.436.454-1.072 1.92-1.348 1.92-1.348s.162-.09.026-.207c-.137-.117-1.866-.313-2.666-.363-2.348-.155-4.99.22-5.733 1.181-1.14 1.48.067 2.925.401 3.337.337.412 1.256.498 1.256.498s-1.466.536-1.992 1.2c-.525.665-.264 1.383.13 1.664.394.281.756.388 1.07.482.707.21 1.818.431 2.795.555 1.454.184 2.957.1 4.312-.184 1.408-2.06 2.83-4.017 4.285-5.907l3.192 1.558c.289.142.66.028.827-.256a.63.63 0 0 0-.086-.74L15.734 7.56c.7-.878 1.426-1.727 2.18-2.537 1.938-2.083 4.298-3.876 6.377-4.707a12.29 12.29 0 0 0-6.648-1.99c-6.427 0-11.66 4.996-11.66 11.116 0 1.49.294 2.913.825 4.215-.374.314-.707.674-.99 1.075-2.316 3.277-.477 6.101 1.046 7.247 1.518 1.144 4.464 1.772 7.155.875 2.798-.93 5.256-3.103 6.822-5.531 1.654-2.563 2.549-5.435 2.549-8.367a12.9 12.9 0 0 0-.316-2.81c-1.178-.022-3.226.306-5.354 1.522z"/></svg>
|
||||
支付宝
|
||||
</span>
|
||||
</el-radio-button>
|
||||
<el-radio-button value="wechat">
|
||||
<span style="display:flex;align-items:center;gap:6px;padding:0 20px">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="#07c160"><path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178A1.17 1.17 0 0 1 4.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178 1.17 1.17 0 0 1-1.162-1.178c0-.651.52-1.18 1.162-1.18zm5.34 2.867c-1.797-.052-3.746.512-5.28 1.786-1.72 1.428-2.687 3.72-1.78 6.22.942 2.453 3.666 4.229 6.884 4.229.826 0 1.622-.12 2.361-.336a.722.722 0 0 1 .598.082l1.584.926a.271.271 0 0 0 .14.045c.134 0 .24-.11.24-.245 0-.06-.024-.12-.04-.178l-.325-1.233a.49.49 0 0 1 .178-.553C23.028 18.125 24 16.539 24 14.711c0-3.396-3.637-6.02-7.062-5.853zm-2.06 1.964c.535 0 .968.44.968.982a.975.975 0 0 1-.968.983.975.975 0 0 1-.969-.983c0-.542.434-.982.969-.982zm4.844 0c.535 0 .969.44.969.982a.975.975 0 0 1-.969.983.975.975 0 0 1-.968-.983c0-.542.433-.982.968-.982z"/></svg>
|
||||
微信支付
|
||||
</span>
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
<div>
|
||||
<el-button type="primary" size="large" :loading="payDialog.loading" @click="handleUpgrade">立即支付</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:center;padding:20px 0" v-else>
|
||||
<div v-if="payDialog.codeUrl">
|
||||
<p style="margin-bottom:16px;color:#666">请使用微信扫描下方二维码支付</p>
|
||||
<img :src="payDialog.codeUrl" style="width:200px;height:200px;border:1px solid #eee;border-radius:8px" />
|
||||
<p style="margin-top:12px;font-size:12px;color:#999">支付成功后自动生效</p>
|
||||
</div>
|
||||
<div v-else-if="payDialog.payUrl">
|
||||
<p style="margin-bottom:16px;color:#666">正在跳转支付宝...</p>
|
||||
<el-button type="primary" @click="openPayUrl">前往支付</el-button>
|
||||
</div>
|
||||
<el-button style="margin-top:16px" @click="payDialog.visible = false">关闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { getPlans, getSubscription, createOrder } from '@/api'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
@@ -41,6 +75,16 @@ const plans = ref([])
|
||||
const currentPlan = ref(null)
|
||||
const loadingId = ref(null)
|
||||
|
||||
const payDialog = reactive({
|
||||
visible: false,
|
||||
planId: null,
|
||||
payType: 'alipay',
|
||||
loading: false,
|
||||
orderCreated: false,
|
||||
payUrl: '',
|
||||
codeUrl: '',
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const [plansRes, subRes] = await Promise.all([getPlans(), getSubscription().catch(() => null)])
|
||||
@@ -53,14 +97,37 @@ onMounted(async () => {
|
||||
} catch { /* ignore */ }
|
||||
})
|
||||
|
||||
async function upgrade(planId) {
|
||||
loadingId.value = planId
|
||||
function showPayDialog(planId) {
|
||||
payDialog.planId = planId
|
||||
payDialog.payType = 'alipay'
|
||||
payDialog.orderCreated = false
|
||||
payDialog.payUrl = ''
|
||||
payDialog.codeUrl = ''
|
||||
payDialog.visible = true
|
||||
}
|
||||
|
||||
async function handleUpgrade() {
|
||||
payDialog.loading = true
|
||||
try {
|
||||
const res = await createOrder(planId, 'native')
|
||||
ElMessage.success('订单已创建' + (res.pay_url ? ',正在跳转支付...' : ''))
|
||||
if (res.pay_url) window.open(res.pay_url)
|
||||
} catch (e) { ElMessage.error(e?.detail || '升级失败') }
|
||||
finally { loadingId.value = null }
|
||||
const res = await createOrder(payDialog.planId, payDialog.payType)
|
||||
payDialog.orderCreated = true
|
||||
if (res.code_url) {
|
||||
payDialog.codeUrl = res.code_url
|
||||
} else if (res.pay_url) {
|
||||
payDialog.payUrl = res.pay_url
|
||||
window.open(res.pay_url)
|
||||
} else {
|
||||
ElMessage.success('订单已创建,请稍后查看')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.detail || '下单失败')
|
||||
} finally {
|
||||
payDialog.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
function openPayUrl() {
|
||||
if (payDialog.payUrl) window.open(payDialog.payUrl)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user