feat: refactor member to pay-per-use gravity purchase; mv webview to clipboard+browser

- member.vue: rewrite from subscription plans (free/growth/sprint) to
  H5-only pay-per-use gravity purchase with quantity selector + QR code
- user.vue: gravity card replacing quota card, add share/contribute/H5-buy
  entry points, plus gravity acquisition modal (share/contribute/buy)
- share.vue: layout fix (flex column), smarter copyLink with cached URL,
  WeChat timeline hint instead of open-type
- share.controller.ts: add GET /:shareCode redirect route (IP record + 302)
- interview.vue: guest mode fix, H5 buy modal, clipboard copy instead of
  webview for mini-program
- App.vue: handleH5UrlParams for ?token=&buy=gravity auto-login
- composables/useGravityPurchase.ts: reusable gravity purchase composable
- remove webview.vue (no longer used), replace with clipboard+browser flow
- AGENTS.md: sync all above changes, fix duplicate numbering
This commit is contained in:
yuzhiran
2026-06-20 20:49:15 +08:00
parent a1e1f0b3c3
commit 8ee27fdd32
11 changed files with 648 additions and 593 deletions
+6 -5
View File
@@ -150,7 +150,7 @@ const doAnalyze = async () => {
data: { ...profile },
header: { 'Authorization': `Bearer ${token.value}`, 'Content-Type': 'application/json' },
})
if (res.statusCode === 200) {
if (res.statusCode >= 200 && res.statusCode < 300) {
if (res.data.error) {
error.value = res.data.error
return
@@ -184,7 +184,7 @@ const doChat = async () => {
data: { message: msg, history: chatHistory.value.slice(0, -1) },
header: { 'Authorization': `Bearer ${token.value}`, 'Content-Type': 'application/json' },
})
if (res.statusCode === 200) {
if (res.statusCode >= 200 && res.statusCode < 300) {
chatHistory.value.push({ role: 'assistant', content: res.data.reply || (res.data.error || '') })
} else {
chatHistory.value.push({ role: 'assistant', content: '回复失败,请稍后重试' })
@@ -211,12 +211,13 @@ const goInterview = (position) => {
.hero-title { font-size: 40rpx; font-weight: 700; color: var(--color-text); margin-top: 16rpx; }
.hero-desc { font-size: 26rpx; color: var(--color-secondary); margin-top: 8rpx; }
.form-card { background: #fff; border-radius: var(--radius-lg); padding: 32rpx; box-shadow: var(--shadow-sm); }
.form-group { margin-bottom: 28rpx; }
.form-card { background: #fff; border-radius: var(--radius-lg); padding: 32rpx; box-shadow: var(--shadow-sm); overflow: hidden; }
.form-group { margin-bottom: 28rpx; width: 100%; }
.form-group:last-child { margin-bottom: 0; }
.form-label { font-size: 26rpx; font-weight: 600; color: var(--color-text); display: block; margin-bottom: 12rpx; }
.required { color: var(--color-error); }
.form-input { width: 100%; height: 80rpx; border: 2rpx solid var(--color-border); border-radius: var(--radius-md); padding: 0 24rpx; font-size: 26rpx; color: var(--color-text); box-sizing: border-box; background: #FAFBFC; }
.form-input { width: 100%; height: 80rpx; border: 2rpx solid var(--color-border); border-radius: var(--radius-md); padding: 0 24rpx; font-size: 26rpx; color: var(--color-text); box-sizing: border-box; background: #FAFBFC; max-width: 100%; }
picker { width: 100%; }
.select-trigger { display: flex; align-items: center; }
.form-textarea { width: 100%; height: 160rpx; border: 2rpx solid var(--color-border); border-radius: var(--radius-md); padding: 20rpx 24rpx; font-size: 26rpx; color: var(--color-text); box-sizing: border-box; background: #FAFBFC; resize: none; }