Add landing page, referral system, usage quotas, search API management, and yearly pricing

- Separate workspace landing from login for better UX
- Referral system rewards both parties with Pro days
- Quota enforcement prevents abuse without breaking endpoints
- 7-day free trial with auto-downgrade on expiry
- Admin-managed search provider config (SearXNG, Bing)
- 15% discount on annual subscriptions
- MCP search server wrapping opencode search
- Fix discovery module field name mismatch causing 422
This commit is contained in:
TradeMate Dev
2026-05-26 11:40:13 +08:00
parent 52dba37f22
commit bed5c7abef
39 changed files with 1988 additions and 152 deletions
+10 -11
View File
@@ -1,13 +1,13 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="8">
<el-col :xs="24" :sm="8">
<el-card shadow="never">
<div style="text-align:center;padding:20px 0">
<el-avatar :size="72" style="background:#409eff;font-size:28px">{{ (user?.username || 'U')[0].toUpperCase() }}</el-avatar>
<h3 style="margin:12px 0 4px">{{ user?.username || '用户' }}</h3>
<el-tag size="small" :type="tierType(user?.tier)">{{ user?.tier || 'free' }}</el-tag>
<el-tag v-if="user?.role === 'admin'" type="danger" size="small" style="margin-left:4px">管理员</el-tag>
<el-avatar :size="72" style="background:#1890ff;font-size:28px">{{ (auth.user?.username || 'U')[0].toUpperCase() }}</el-avatar>
<h3 style="margin:12px 0 4px">{{ auth.user?.username || '用户' }}</h3>
<el-tag size="small" :type="tierType(auth.user?.tier)">{{ auth.user?.tier || 'free' }}</el-tag>
<el-tag v-if="auth.user?.role === 'admin'" type="danger" size="small" style="margin-left:4px">管理员</el-tag>
</div>
<el-divider style="margin:8px 0" />
<div class="profile-menu">
@@ -29,7 +29,7 @@
</div>
</el-card>
</el-col>
<el-col :span="16">
<el-col :xs="24" :sm="16">
<el-card shadow="never">
<template #header><span>编辑资料</span></template>
<el-form :model="form" label-width="80" size="large">
@@ -71,7 +71,6 @@ import { updateProfile, changePassword } from '@/api'
import { ElMessage } from 'element-plus'
const auth = useAuthStore()
const user = auth.user
const saving = ref(false)
const showPassword = ref(false)
const pwLoading = ref(false)
@@ -81,10 +80,10 @@ const pwForm = reactive({ old_password: '', new_password: '' })
function tierType(t) { return { free: 'warning', pro: 'primary', enterprise: 'success' }[t] || 'info' }
onMounted(() => {
if (user.value) {
form.username = user.value.username || ''
form.email = user.value.email || ''
form.phone = user.value.phone || ''
if (auth.user) {
form.username = auth.user.username || ''
form.email = auth.user.email || ''
form.phone = auth.user.phone || ''
}
})