refactor: 项目架构重构 - 目录标准化 + 清理冗余

- 目录重命名: backend/wdkj-server/ → server/, frontend/ai-dimension/ → client/
- 删除 20+ 冗余文件(WDKJ 旧脚本、Windows 脚本、过时文档、设计稿)
- 更新 package.json 元数据(移除 wdkj 命名)
- 完善三级 .gitignore(根 + server + client)
- 重写 README.md 和 CHANGELOG.md
- 工具脚本移至 scripts/
This commit is contained in:
Yuzhiran Dev
2026-07-11 12:45:37 +08:00
parent 66ac576082
commit c1d6dd3b29
106 changed files with 17436 additions and 2311 deletions
+418
View File
@@ -0,0 +1,418 @@
<template>
<view class="page-payment">
<view class="status-bar-placeholder"></view>
<!-- 导航 -->
<view class="nav-bar">
<view class="nav-back" @click="goBack">‹</view>
<text class="nav-title">订阅升级</text>
<view class="nav-placeholder"></view>
</view>
<!-- 头部 -->
<view class="payment-header">
<text class="header-title">解锁 AI 维度 Pro</text>
<text class="header-desc">无限 AI 问答 · 全量知识解锁 · 每日趋势优先推送</text>
</view>
<!-- 方案选择 -->
<view class="plan-selector">
<view
class="plan-tag"
:class="{ active: selectedPlan === 'pro' }"
@click="selectedPlan = 'pro'"
>
<text class="plan-name">Pro</text>
<text class="plan-price">¥19.9<span class="unit">/月</span></text>
<text class="plan-badge">推荐</text>
</view>
<view
class="plan-tag vip"
:class="{ active: selectedPlan === 'vip' }"
@click="selectedPlan = 'vip'"
>
<text class="plan-name">VIP</text>
<text class="plan-price">¥39.9<span class="unit">/月</span></text>
<text class="plan-desc">含 Pro 全部权益</text>
</view>
</view>
<!-- 权益对比 -->
<view class="benefits">
<view class="benefit-item" v-for="(item, idx) in benefits" :key="idx">
<text class="b-icon">{{ item.icon }}</text>
<text class="b-text">{{ item.text }}</text>
</view>
</view>
<!-- 套餐选择 -->
<view class="duration-section">
<text class="duration-title">选择套餐</text>
<view class="duration-list">
<view
class="duration-item"
:class="{ active: selectedDuration === item.key }"
v-for="item in durations"
:key="item.key"
@click="selectedDuration = item.key"
>
<text class="d-label">{{ item.label }}</text>
<text class="d-price">¥{{ item.price }}</text>
<text class="d-save" v-if="item.save">省 {{ item.save }}</text>
</view>
</view>
</view>
<!-- 支付按钮 -->
<view class="pay-section">
<button class="pay-btn" :loading="loading" @click="handlePay">
立即支付 ¥{{ currentPrice }}
</button>
<text class="pay-agreement">
点击支付即同意《用户协议》和《隐私政策》
</text>
</view>
<!-- 底部 -->
<text class="footer-text">如有问题请联系客服 · 支持 7 天无理由退款</text>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { paymentApi } from '@/utils/api'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 方案
const selectedPlan = ref('pro')
// 套餐时长
const durations = ref([
{ key: 'pro-monthly', label: 'Pro 月付', price: 19.9 },
{ key: 'pro-quarterly', label: 'Pro 季付', price: 49.9, save: '省 ¥10' },
{ key: 'vip-monthly', label: 'VIP 月付', price: 39.9 },
{ key: 'vip-quarterly', label: 'VIP 季付', price: 99.9, save: '省 ¥20' }
])
const selectedDuration = ref('pro-monthly')
// 权益
const benefits = ref([
{ icon: '🤖', text: 'AI 问答无限次' },
{ icon: '📚', text: '全量知识内容解锁' },
{ icon: '📡', text: '每日趋势优先推送' },
{ icon: '⭐', text: '收藏夹无限容量' },
{ icon: '🎁', text: '专属会员标识' },
{ icon: '💬', text: '专属客服支持' }
])
// 当前价格
const currentPrice = computed(() => {
const item = durations.value.find(d => d.key === selectedDuration.value)
return item ? item.price : 19.9
})
const loading = ref(false)
async function handlePay() {
if (!userStore.isLoggedIn) {
uni.showToast({ title: '请先登录', icon: 'none' })
return
}
// 找到对应的 productId
const productMap = {
'pro-monthly': 'pro-monthly',
'pro-quarterly': 'pro-quarterly',
'vip-monthly': 'vip-monthly',
'vip-quarterly': 'vip-quarterly'
}
loading.value = true
try {
// 调用后端创建订单
const data = await paymentApi.createOrder(productMap[selectedDuration.value], userStore.token)
// 获取微信支付参数
const payParam = data.payParams || data.wechatParam || data.paymentData
if (payParam) {
// 调起微信支付
uni.requestPayment({
provider: 'wxpay',
timeStamp: payParam.timeStamp || payParam.timestamp,
nonceStr: payParam.nonceStr,
package: payParam.package || payParam.packAge,
signType: payParam.signType || 'MD5',
paySign: payParam.paySign,
success: () => {
uni.showToast({ title: '支付成功!', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 1500)
},
fail: (err) => {
if (err.errMsg.includes('cancel')) {
uni.showToast({ title: '已取消支付', icon: 'none' })
} else {
uni.showToast({ title: '支付失败,请重试', icon: 'none' })
}
}
})
} else {
uni.showToast({ title: '获取支付参数失败', icon: 'none' })
}
} catch (err) {
console.error('创建订单失败:', err)
uni.showToast({ title: err.message || '创建订单失败,请重试', icon: 'none' })
} finally {
loading.value = false
}
}
function goBack() {
uni.navigateBack()
}
</script>
<style lang="scss" scoped>
.page-payment {
min-height: 100vh;
padding: 0 20px;
background: var(--bg-primary);
}
.status-bar-placeholder { height: 44px; }
// 导航
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
}
.nav-back {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: var(--text-primary);
}
.nav-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
}
.nav-placeholder {
width: 30px;
}
// 头部
.payment-header {
text-align: center;
padding: 10px 0 20px;
}
.header-title {
font-size: 22px;
font-weight: 700;
color: var(--text-primary);
display: block;
}
.header-desc {
font-size: 12px;
color: var(--text-tertiary);
margin-top: 6px;
display: block;
}
// 方案选择
.plan-selector {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.plan-tag {
flex: 1;
padding: 16px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
text-align: center;
position: relative;
}
.plan-tag.active {
background: linear-gradient(135deg, rgba(124, 77, 255, 0.15), rgba(0, 0, 0, 0.2));
border-color: rgba(124, 77, 255, 0.3);
}
.plan-tag.vip.active {
background: linear-gradient(135deg, rgba(255, 152, 0, 0.15), rgba(0, 0, 0, 0.2));
border-color: rgba(255, 152, 0, 0.3);
}
.plan-name {
font-size: 18px;
font-weight: 700;
color: var(--text-primary);
display: block;
margin-bottom: 6px;
}
.plan-price {
font-size: 24px;
font-weight: 700;
color: var(--dim1-color);
display: block;
}
.plan-tag.vip .plan-price {
color: #ffa726;
}
.unit {
font-size: 12px;
color: var(--text-tertiary);
font-weight: 400;
}
.plan-badge {
position: absolute;
top: -8px;
right: 12px;
background: linear-gradient(135deg, #ff9800, #ff5722);
color: white;
font-size: 10px;
padding: 2px 10px;
border-radius: 10px;
}
.plan-desc {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
display: block;
}
// 权益
.benefits {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 14px;
padding: 16px;
margin-bottom: 20px;
}
.benefit-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
}
.benefit-item:not(:last-child) {
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.b-icon { font-size: 16px; }
.b-text {
font-size: 13px;
color: var(--text-secondary);
}
// 套餐时长
.duration-section {
margin-bottom: 20px;
}
.duration-title {
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 10px;
display: block;
}
.duration-list {
display: flex;
gap: 8px;
}
.duration-item {
flex: 1;
padding: 12px 8px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
text-align: center;
}
.duration-item.active {
background: rgba(124, 77, 255, 0.12);
border-color: rgba(124, 77, 255, 0.3);
}
.d-label {
font-size: 12px;
color: rgba(255, 255, 255, 0.6);
display: block;
margin-bottom: 4px;
}
.d-price {
font-size: 16px;
font-weight: 600;
color: var(--dim1-color);
display: block;
}
.d-save {
font-size: 10px;
color: #4db6ac;
display: block;
margin-top: 2px;
}
// 支付
.pay-section {
margin-top: 20px;
}
.pay-btn {
width: 100%;
height: 50px;
background: linear-gradient(135deg, var(--dim1-color), var(--dim3-color));
color: white;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
box-shadow: 0 4px 16px rgba(179, 136, 255, 0.3);
}
.pay-agreement {
font-size: 10px;
color: var(--text-muted);
text-align: center;
margin-top: 10px;
display: block;
}
.footer-text {
font-size: 10px;
color: var(--text-muted);
text-align: center;
margin-top: 20px;
display: block;
}
</style>