release: freeze V1.2 demo acceptance 2026-10-25
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Waiting to run
CI/CD Pipeline / Backend Build & Test (push) Waiting to run
CI/CD Pipeline / Admin Web Build (push) Waiting to run
CI/CD Pipeline / Mini Program Compile Check (push) Waiting to run
CI/CD Pipeline / Migration Validation (push) Blocked by required conditions
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Waiting to run
CI/CD Pipeline / E2E (live stack) (push) Blocked by required conditions

This commit is contained in:
WorkBuddy
2026-10-25 14:29:29 +08:00
parent f6744d3f91
commit 377429995c
137 changed files with 4136 additions and 175 deletions
+4
View File
@@ -77,6 +77,10 @@ Page({
wx.navigateTo({ url: ROUTE.OPERATOR.LOGIN })
},
onRegisterEntry() {
wx.navigateTo({ url: '/pages/register/register' })
},
onViewTerms() {
wx.navigateTo({ url: '/pages/help-center/help-center?tab=terms' })
},
+1
View File
@@ -16,6 +16,7 @@
>
微信一键登录
</button>
<button bindtap="onRegisterEntry" disabled="{{loggingIn}}">手机号注册</button>
<view class="login-hint" wx:if="{{error}}">
<text class="error-text">{{error}}</text>
+1
View File
@@ -126,6 +126,7 @@ Page({
onGoVerification() { wx.navigateTo({ url: ROUTE.AUTH.VERIFICATION }); },
onGoPrivacy() { wx.navigateTo({ url: ROUTE.SETTINGS.PRIVACY }); },
onGoServiceConsent() { wx.navigateTo({ url: '/pages/service-consent/service-consent' }); },
onGoBlacklist() { wx.navigateTo({ url: ROUTE.SETTINGS.BLACKLIST }); },
onGoAccountStatus() { wx.navigateTo({ url: ROUTE.SETTINGS.ACCOUNT_STATUS_PAGE }); },
+1
View File
@@ -60,6 +60,7 @@
<ui-section-header title="安全与隐私" subtitle="隐私、黑名单与账号" />
<view class="list-group secondary-list">
<view class="list-row" bindtap="onGoPrivacy"><text class="list-label">隐私设置</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoServiceConsent"><text class="list-label">服务授权</text><text class="list-value">查看与撤回</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoBlacklist"><text class="list-label">黑名单</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoSafety"><text class="list-label">举报与安全</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoSettings"><text class="list-label">账号设置</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
+3
View File
@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "注册善缘丹阳"
}
+45
View File
@@ -0,0 +1,45 @@
const { authService } = require('../../services')
Page({
data: {
name: '', organization: '', organizationPhone: '', phone: '', code: '',
sending: false, submitting: false, countdown: 0, error: '',
},
onInput(event: WechatMiniprogram.Input) {
this.setData({ [event.currentTarget.dataset.field]: event.detail.value, error: '' })
},
async onSendCode() {
const { phone } = this.data
if (!/^1\d{10}$/.test(phone)) return this.setData({ error: '请输入正确的手机号' })
this.setData({ sending: true, error: '' })
try {
await authService.sendPhoneCode(phone)
this.setData({ countdown: 60 })
const timer = setInterval(() => {
if (this.data.countdown <= 1) {
clearInterval(timer)
this.setData({ countdown: 0 })
} else this.setData({ countdown: this.data.countdown - 1 })
}, 1000)
} catch (error: any) {
this.setData({ error: error?.message || '验证码发送失败' })
} finally { this.setData({ sending: false }) }
},
async onRegister() {
const { name, organization, organizationPhone, phone, code } = this.data
if (!name || !organization || !organizationPhone || !/^1\d{10}$/.test(phone) || !code) {
return this.setData({ error: '请完整填写注册信息' })
}
this.setData({ submitting: true, error: '' })
try {
const verification = await authService.verifyPhone(phone, code)
await authService.register({ name, organization, organizationPhone, phone, verificationTransaction: verification.verificationTransaction })
wx.showModal({ title: '提交成功', content: '注册申请已提交,请按页面提示完成资料完善。', showCancel: false, success: () => wx.navigateBack() })
} catch (error: any) {
this.setData({ error: error?.message || '注册失败,请重试' })
} finally { this.setData({ submitting: false }) }
},
})
+13
View File
@@ -0,0 +1,13 @@
<view class="page">
<view class="title">注册善缘丹阳</view>
<input placeholder="姓名" value="{{name}}" data-field="name" bindinput="onInput" />
<input placeholder="所属组织" value="{{organization}}" data-field="organization" bindinput="onInput" />
<input placeholder="组织联系电话" value="{{organizationPhone}}" data-field="organizationPhone" bindinput="onInput" />
<input placeholder="手机号" type="number" value="{{phone}}" data-field="phone" bindinput="onInput" />
<view class="code-row">
<input placeholder="验证码" type="number" value="{{code}}" data-field="code" bindinput="onInput" />
<button size="mini" bindtap="onSendCode" disabled="{{sending || countdown > 0}}">{{countdown > 0 ? countdown + '秒后重试' : '获取验证码'}}</button>
</view>
<button class="primary" bindtap="onRegister" loading="{{submitting}}" disabled="{{submitting}}">提交注册</button>
<text wx:if="{{error}}" class="error">{{error}}</text>
</view>
+8
View File
@@ -0,0 +1,8 @@
.page { padding: 48rpx 40rpx; background: #f4f5fa; min-height: 100vh; }
.title { font-size: 44rpx; font-weight: 600; margin-bottom: 36rpx; color: #172a72; }
input { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 20rpx; }
.code-row { display: flex; gap: 16rpx; align-items: center; }
.code-row input { flex: 1; }
.code-row button { flex: 0 0 auto; }
.primary { margin-top: 28rpx; background: #f4005a; color: #fff; }
.error { display: block; color: #d4380d; margin-top: 20rpx; }
@@ -0,0 +1,31 @@
const { serviceConsentService } = require('../../services');
Page({
data: { documents: [], saving: false, error: '', canAgree: false, hasAgreed: false },
onShow() { this.load(); },
async load() {
try {
const documents = await serviceConsentService.getState();
this.setData({ documents, canAgree: documents.length === 2, hasAgreed: documents.some(item => item.agreed), error: '' });
} catch (err) { this.setData({ error: err.message || '授权状态加载失败' }); }
},
async onAgree() {
if (!this.data.canAgree || this.data.saving) return;
this.setData({ saving: true, error: '' });
try {
const versions = {};
this.data.documents.forEach(item => { versions[item.consentType] = item.documentVersion; });
await serviceConsentService.agree(versions);
wx.showToast({ title: '授权已确认', icon: 'success' });
await this.load();
} catch (err) { this.setData({ error: err.message || '授权确认失败' }); }
finally { this.setData({ saving: false }); }
},
onWithdraw() {
wx.showActionSheet({ itemList: ['撤回婚恋服务授权', '撤回隐私与安全授权'], success: ({ tapIndex }) => this.withdraw(tapIndex === 0 ? 'service_terms' : 'privacy_safety') });
},
async withdraw(consentType) {
try { await serviceConsentService.withdraw(consentType); wx.showToast({ title: '已撤回', icon: 'success' }); await this.load(); }
catch (err) { this.setData({ error: err.message || '撤回失败' }); }
},
});
@@ -0,0 +1 @@
{"navigationBarTitleText":"服务授权","usingComponents":{}}
@@ -0,0 +1,11 @@
<view class="page service-consent-page">
<view class="hero"><text class="title">服务授权</text><text class="subtitle">确认两份授权后,才能使用匹配、搜索、活动和公益服务。</text></view>
<view class="notice">两份授权分别记录。任一撤回后,相关服务会立即暂停。</view>
<view class="document" wx:for="{{documents}}" wx:key="consentType">
<view class="document-main"><text class="document-title">{{item.consentType === 'service_terms' ? '婚恋服务授权书' : '隐私与安全授权书'}}</text><text class="document-version">版本 {{item.documentVersion}}</text></view>
<text class="document-status {{item.agreed ? 'agreed' : 'required'}}">{{item.agreed ? '已确认' : '待确认'}}</text>
</view>
<button class="primary" loading="{{saving}}" disabled="{{saving || !canAgree}}" bindtap="onAgree">确认两份授权</button>
<view class="withdraw-row" wx:if="{{hasAgreed}}"><text>需要停止服务?</text><text class="withdraw" bindtap="onWithdraw">撤回授权</text></view>
<view class="error" wx:if="{{error}}">{{error}}</view>
</view>
@@ -0,0 +1 @@
.service-consent-page{padding:32rpx;background:#f4f5fa;min-height:100vh;box-sizing:border-box}.hero{padding:18rpx 4rpx 28rpx}.title{display:block;font-size:42rpx;font-weight:700;color:#172a72}.subtitle{display:block;margin-top:14rpx;color:#68708f;line-height:1.6}.notice{padding:22rpx;background:#fff5e8;color:#8a5b20;border-radius:12rpx;line-height:1.6}.document{margin-top:20rpx;padding:26rpx 22rpx;background:#fff;border-radius:12rpx;display:flex;align-items:center;justify-content:space-between}.document-main{display:flex;flex-direction:column;gap:10rpx}.document-title{font-size:30rpx;color:#20284d}.document-version{font-size:24rpx;color:#8b91aa}.document-status{font-size:26rpx}.agreed{color:#18864b}.required{color:#d26b20}.primary{margin-top:36rpx;background:#f4005a;color:#fff;border-radius:12rpx}.withdraw-row{margin-top:28rpx;text-align:center;color:#858ba3}.withdraw{margin-left:10rpx;color:#d44c4c}.error{margin-top:22rpx;color:#c0392b;text-align:center}