release: freeze 2026-10-15 validated release baseline
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
This commit is contained in:
@@ -1 +1 @@
|
||||
<view class="service-page"><ui-loading wx:if="{{loading}}" mode="skeleton" rows="6"/><ui-error-state wx:elif="{{error}}" title="活动信息加载失败" description="请检查网络后重试" bind:retry="retry"/><block wx:elif="{{activity}}"><view class="activity-hero"><view class="activity-badge">{{activity.typeLabel || '公益活动'}}</view><text class="activity-title">{{activity.title}}</text><text class="activity-slogan">真实相识 · 公益免费 · 尊重意愿</text></view><view class="content"><view class="block"><text class="block-title">活动信息</text><view class="card"><view class="feature-row"><view class="feature-icon">时</view><view class="feature-copy"><text class="feature-title">{{activity.startText}}</text><text class="feature-desc">请提前 15 分钟到场签到</text></view></view><view class="feature-row"><view class="feature-icon">址</view><view class="feature-copy"><text class="feature-title">{{activity.locationName}}</text><text class="feature-desc">{{activity.address}}</text></view></view><view class="feature-row"><view class="feature-icon">额</view><view class="feature-copy"><text class="feature-title">{{activity.quotaText}}</text><text class="feature-desc">报名提交后需等待工作人员审核</text></view><text class="tag">公益免费</text></view></view></view><view class="block"><text class="block-title">活动介绍</text><view class="card"><text class="body-text">{{activity.description}}</text></view></view><view class="block"><text class="block-title">报名须知</text><view class="card"><view wx:for="{{activity.eligibility}}" wx:key="*this" class="rule-row"><text>{{index+1}}</text><text>{{item}}</text></view><view class="notice">不同活动类型的资格要求可能不同,最终以本活动页面和审核结果为准。</view></view></view><view class="block"><text class="block-title">主办信息</text><view class="card"><text class="feature-title">{{activity.organizer}}</text><text class="feature-desc">主办单位关系不会自动授予用户资料访问权限。</text></view></view></view><view class="fixed-action"><view wx:if="{{activity.registrationStatus === 'not_registered'}}" class="primary-btn" bindtap="register">报名参加活动</view><view wx:else class="secondary-btn" bindtap="openMyActivities">查看我的报名</view></view></block></view>
|
||||
<view class="service-page"><ui-loading wx:if="{{loading}}" mode="skeleton" rows="6"/><ui-error-state wx:elif="{{error}}" title="活动信息加载失败" description="请检查网络后重试" bind:retry="retry"/><block wx:elif="{{activity}}"><view class="activity-hero"><view class="activity-badge">{{activity.typeLabel || '公益活动'}}</view><text class="activity-title">{{activity.title}}</text><text class="activity-slogan">真实相识 · 公益免费 · 尊重意愿</text></view><view class="content"><view class="block"><text class="block-title">活动信息</text><view class="card"><text wx:if="{{activity.displayId}}" class="feature-desc">活动编号:{{activity.displayId}}</text><view class="feature-row"><view class="feature-icon">时</view><view class="feature-copy"><text class="feature-title">{{activity.startText}}</text><text class="feature-desc">请提前 15 分钟到场签到</text></view></view><view class="feature-row"><view class="feature-icon">址</view><view class="feature-copy"><text class="feature-title">{{activity.locationName}}</text><text class="feature-desc">{{activity.address}}</text></view></view><view class="feature-row"><view class="feature-icon">额</view><view class="feature-copy"><text class="feature-title">{{activity.quotaText}}</text><text class="feature-desc">报名提交后需等待工作人员审核</text></view><text class="tag">公益免费</text></view></view></view><view class="block"><text class="block-title">活动介绍</text><view class="card"><text class="body-text">{{activity.description}}</text></view></view><view class="block"><text class="block-title">报名须知</text><view class="card"><view wx:for="{{activity.eligibility}}" wx:key="*this" class="rule-row"><text>{{index+1}}</text><text>{{item}}</text></view><view class="notice">不同活动类型的资格要求可能不同,最终以本活动页面和审核结果为准。</view></view></view><view class="block"><text class="block-title">主办信息</text><view class="card"><text class="feature-title">{{activity.organizer}}</text><text class="feature-desc">主办单位关系不会自动授予用户资料访问权限。</text></view></view></view><view class="fixed-action"><view wx:if="{{activity.registrationStatus === 'not_registered'}}" class="primary-btn" bindtap="register">报名参加活动</view><view wx:else class="secondary-btn" bindtap="openMyActivities">查看我的报名</view></view></block></view>
|
||||
@@ -71,6 +71,10 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/help-center/help-center?tab=privacy' })
|
||||
},
|
||||
|
||||
onOperatorEntry() {
|
||||
wx.navigateTo({ url: ROUTE.OPERATOR.LOGIN })
|
||||
},
|
||||
|
||||
onViewTerms() {
|
||||
wx.navigateTo({ url: '/pages/help-center/help-center?tab=terms' })
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
</view>
|
||||
|
||||
<view class="login-content">
|
||||
<button bindtap="onOperatorEntry" disabled="{{loggingIn}}">红娘工作人员入口</button>
|
||||
<button
|
||||
class="login-btn {{loggingIn ? 'loading' : ''}}"
|
||||
bindtap="onLoginTap"
|
||||
|
||||
@@ -38,7 +38,7 @@ Page({
|
||||
try {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${API_BASE_URL}/operator/auth/login`,
|
||||
url: `${API_BASE_URL}/auth/operator/login`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
username: this.data.username,
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
* 红娘服务进度页 U35
|
||||
*/
|
||||
const api = require('../../utils/api');
|
||||
const { matchmakerService } = require('../../services');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
service: null,
|
||||
loading: true
|
||||
loading: true,
|
||||
confirming: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -22,8 +24,10 @@ Page({
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const token = wx.getStorageSync('accessToken');
|
||||
const res = await api.get(`/matchmaker/services/${serviceId}`, {}, { Authorization: `Bearer ${token}` });
|
||||
this.setData({ service: res, loading: false });
|
||||
const services = await api.get('/matchmaker/services', {}, { Authorization: `Bearer ${token}` });
|
||||
const service = Array.isArray(services)
|
||||
? services.find(item => (item.serviceId || item.id) === serviceId) : null;
|
||||
this.setData({ service: service || null, loading: false });
|
||||
} catch (err) {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
@@ -44,6 +48,20 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
async onConfirmService() {
|
||||
if (!this.data.service || this.data.service.status !== 'waiting_user' || this.data.confirming) return;
|
||||
this.setData({ confirming: true });
|
||||
try {
|
||||
await matchmakerService.confirmService(this.data.service.serviceId || this.data.service.id);
|
||||
wx.showToast({ title: '已确认', icon: 'success' });
|
||||
await this.loadService(this.data.service.serviceId || this.data.service.id);
|
||||
} catch (err) {
|
||||
wx.showToast({ title: (err && err.message) || '确认失败,请稍后重试', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ confirming: false });
|
||||
}
|
||||
},
|
||||
|
||||
getStatusText(status) {
|
||||
const map = {
|
||||
'submitted': '已提交,等待分配',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
</view>
|
||||
|
||||
<view class="info-card">
|
||||
<view class="info-row"><text class="info-label">服务编号</text><text class="info-value">{{service.displayId || service.serviceDisplayId}}</text></view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">服务类型</text>
|
||||
<text class="info-value">{{getServiceTypeText(service.serviceType)}}</text>
|
||||
@@ -49,6 +50,12 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{service.status === 'waiting_user'}}" class="confirm-card">
|
||||
<text class="confirm-title">红娘已提交服务方案</text>
|
||||
<text class="confirm-desc">确认后将恢复服务跟进。</text>
|
||||
<button class="btn-confirm" loading="{{confirming}}" disabled="{{confirming}}" bindtap="onConfirmService">{{confirming ? '确认中...' : '确认服务方案'}}</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!loading && !service}}" class="empty">
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
const { matchmakerStore, matchStore, appStore } = require('../../stores');
|
||||
const { matchmakerService } = require('../../services');
|
||||
const { ROUTE, to } = require('../../constants');
|
||||
|
||||
Page({
|
||||
data: { loading: true, actionLoadingId: '', recommendations: [] },
|
||||
|
||||
onShow() { this.load(); },
|
||||
|
||||
async load() {
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const result = await matchmakerStore.fetchRecommendations();
|
||||
const items = (result && Array.isArray(result) ? result : matchmakerStore.state.recommendations || [])
|
||||
.map((item) => ({
|
||||
...item,
|
||||
userId: item.userId || item.targetUserId,
|
||||
displayName: item.displayName || '红娘推荐对象',
|
||||
recommendationReasons: item.reason ? [item.reason] : [],
|
||||
}));
|
||||
this.setData({ recommendations: items, loading: false });
|
||||
items.filter((item) => item.status === 'pending_view' && item.id).forEach((item) => {
|
||||
matchmakerService.markRecommendationViewed(item.id).catch(() => {});
|
||||
});
|
||||
} catch (error) {
|
||||
this.setData({ loading: false });
|
||||
appStore.toastError(error, '加载红娘推荐失败');
|
||||
}
|
||||
},
|
||||
|
||||
onView(e) {
|
||||
const userId = e.detail && e.detail.userId;
|
||||
if (userId) wx.navigateTo({ url: to.detail(userId) });
|
||||
},
|
||||
|
||||
async onLike(e) {
|
||||
await this.respond(e.detail && e.detail.userId, 'like');
|
||||
},
|
||||
|
||||
async onPass(e) {
|
||||
await this.respond(e.detail && e.detail.userId, 'pass');
|
||||
},
|
||||
|
||||
async respond(userId, action) {
|
||||
if (!userId || this.data.actionLoadingId) return;
|
||||
this.setData({ actionLoadingId: userId });
|
||||
try {
|
||||
const result = action === 'like' ? await matchStore.like(userId) : await matchStore.pass(userId);
|
||||
this.setData({ recommendations: this.data.recommendations.filter((item) => item.userId !== userId) });
|
||||
if (action === 'like' && result && result.mutualMatch && result.matchedConversationId) {
|
||||
wx.navigateTo({ url: to.chat(result.matchedConversationId) });
|
||||
} else {
|
||||
appStore.toastSuccess(action === 'like' ? '已表达喜欢' : '已暂不考虑');
|
||||
}
|
||||
} catch (error) {
|
||||
appStore.toastError(error);
|
||||
} finally {
|
||||
this.setData({ actionLoadingId: '' });
|
||||
}
|
||||
},
|
||||
|
||||
onBack() { wx.navigateBack(); },
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"navigationBarTitleText":"红娘推荐","usingComponents":{"ui-nav-bar":"/components/nav-bar/index","ui-recommend-card":"/components/recommend-card/index","ui-loading":"/components/loading/index","ui-empty":"/components/empty/index"}}
|
||||
@@ -0,0 +1,9 @@
|
||||
<view class="page">
|
||||
<ui-nav-bar title="红娘推荐" show-back="{{true}}" bind:back="onBack" />
|
||||
<view class="intro">这是红娘根据你的需求提供的候选建议,是否表达喜欢由你本人决定。</view>
|
||||
<ui-loading wx:if="{{loading}}" mode="skeleton" rows="3" />
|
||||
<view wx:elif="{{recommendations.length}}" class="list">
|
||||
<ui-recommend-card wx:for="{{recommendations}}" wx:key="id" recommendation="{{item}}" action-loading="{{actionLoadingId === item.userId}}" bind:view="onView" bind:like="onLike" bind:pass="onPass" />
|
||||
</view>
|
||||
<ui-empty wx:else illustration="heart" title="暂时没有红娘推荐" description="红娘有新的建议后会显示在这里" />
|
||||
</view>
|
||||
@@ -0,0 +1,3 @@
|
||||
.page { min-height: 100vh; padding: 0 24rpx 40rpx; background: var(--color-primary-lighter); box-sizing: border-box; }
|
||||
.intro { margin: 24rpx 0; padding: 24rpx; border-radius: 20rpx; background: var(--color-bg-card); color: var(--color-text-secondary); font-size: 26rpx; line-height: 1.6; }
|
||||
.list { border-radius: 24rpx; overflow: hidden; background: var(--color-bg-card); }
|
||||
@@ -0,0 +1,75 @@
|
||||
const api = require('../../utils/api');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
serviceId: '',
|
||||
detail: null,
|
||||
followups: [],
|
||||
followupTypes: ['followup', '需求了解', '候选建议', '活动协助', '其他'],
|
||||
loading: true,
|
||||
submitting: false,
|
||||
form: { followupType: 'followup', content: '', nextFollowupAt: '' },
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
const serviceId = options && options.id;
|
||||
if (!serviceId) {
|
||||
wx.showToast({ title: '服务记录不存在', icon: 'none' });
|
||||
wx.navigateBack();
|
||||
return;
|
||||
}
|
||||
this.setData({ serviceId });
|
||||
this.loadDetail();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (this.data.serviceId && !this.data.loading) this.loadDetail();
|
||||
},
|
||||
|
||||
tokenHeaders() {
|
||||
const token = wx.getStorageSync('operatorToken');
|
||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||
},
|
||||
|
||||
async loadDetail() {
|
||||
this.setData({ loading: true });
|
||||
try {
|
||||
const detail = await api.get(`/operator/matchmaker/services/${this.data.serviceId}`, {}, this.tokenHeaders());
|
||||
this.setData({ detail: detail || null, followups: (detail && detail.followups) || [] });
|
||||
} catch (error) {
|
||||
if (error && (error.code === 'FORBIDDEN' || error.code === 'NOT_FOUND' || error.code === 'DATA_SCOPE_DENIED')) {
|
||||
wx.showModal({ title: '服务权限已变更', content: '该服务对象已结束或不再属于当前服务范围。', showCancel: false,
|
||||
success: () => wx.navigateBack() });
|
||||
} else {
|
||||
wx.showToast({ title: (error && error.message) || '加载失败', icon: 'none' });
|
||||
}
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
onTypeChange(e) { this.setData({ 'form.followupType': this.data.followupTypes[Number(e.detail.value)] || 'followup' }); },
|
||||
onContentInput(e) { this.setData({ 'form.content': e.detail.value }); },
|
||||
onNextFollowupInput(e) { this.setData({ 'form.nextFollowupAt': e.detail.value }); },
|
||||
|
||||
async submitFollowup() {
|
||||
if (this.data.submitting) return;
|
||||
const content = (this.data.form.content || '').trim();
|
||||
if (!content) { wx.showToast({ title: '请填写服务摘要', icon: 'none' }); return; }
|
||||
this.setData({ submitting: true });
|
||||
try {
|
||||
await api.post(`/operator/matchmaker/services/${this.data.serviceId}/followup`, {
|
||||
followupType: this.data.form.followupType,
|
||||
content,
|
||||
nextFollowupAt: this.data.form.nextFollowupAt || undefined,
|
||||
}, this.tokenHeaders(), api.idempotencyKey('matchmaker-followup', this.data.serviceId));
|
||||
wx.showToast({ title: '已记录服务', icon: 'success' });
|
||||
this.setData({ 'form.content': '', 'form.nextFollowupAt': '' });
|
||||
await this.loadDetail();
|
||||
} catch (error) {
|
||||
wx.showToast({ title: (error && error.message) || '提交失败,请重试', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ submitting: false });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"navigationBarTitleText":"服务对象详情","usingComponents":{}}
|
||||
@@ -0,0 +1,39 @@
|
||||
<view class="page">
|
||||
<view wx:if="{{loading}}" class="state">加载中...</view>
|
||||
<block wx:elif="{{detail}}">
|
||||
<view class="card profile-card">
|
||||
<view class="title">服务对象</view>
|
||||
<view class="status">服务编号:{{detail.serviceDisplayId || detail.displayId}}</view>
|
||||
<view wx:if="{{detail.userDisplayNumber}}" class="status">用户编号:{{detail.userDisplayNumber}}</view>
|
||||
<view wx:if="{{detail.userProfile}}" class="profile-grid">
|
||||
<text>展示名:{{detail.userProfile.displayName || '未设置'}}</text>
|
||||
<text>年龄:{{detail.userProfile.age || '未设置'}}</text>
|
||||
<text>地区:{{detail.userProfile.regionName || '未设置'}}</text>
|
||||
<text>婚姻状态:{{detail.userProfile.maritalStatus || '未设置'}}</text>
|
||||
</view>
|
||||
<view class="status">服务状态:{{detail.status}}</view>
|
||||
<view wx:if="{{detail.description}}" class="description">服务需求:{{detail.description}}</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="title">记录服务</view>
|
||||
<picker mode="selector" range="{{followupTypes}}" value="0" bindchange="onTypeChange">
|
||||
<view class="picker">服务类型:{{form.followupType}}</view>
|
||||
</picker>
|
||||
<textarea class="textarea" maxlength="1000" placeholder="只记录完成公益服务所必需的信息" value="{{form.content}}" bindinput="onContentInput"></textarea>
|
||||
<input class="input" placeholder="下次跟进时间(可选,如 2026-10-14 15:30)" value="{{form.nextFollowupAt}}" bindinput="onNextFollowupInput" />
|
||||
<button type="primary" loading="{{submitting}}" disabled="{{submitting}}" bindtap="submitFollowup">保存服务记录</button>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="title">服务历史</view>
|
||||
<view wx:if="{{followups.length}}">
|
||||
<view wx:for="{{followups}}" wx:key="id" class="history-item">
|
||||
<view>{{item.followupType}} · {{item.createdAt}}</view>
|
||||
<view class="history-content">{{item.content}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else class="state">暂无服务记录</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
@@ -0,0 +1 @@
|
||||
.page{min-height:100vh;background:#f5f6fa;padding:24rpx;box-sizing:border-box}.card{background:#fff;border-radius:12rpx;padding:28rpx;margin-bottom:20rpx}.title{font-size:32rpx;font-weight:600;color:#20243a;margin-bottom:20rpx}.profile-grid{display:grid;grid-template-columns:1fr 1fr;gap:16rpx;color:#5d637d;font-size:26rpx}.status{margin-top:20rpx;color:#e96b6b;font-size:27rpx}.description{margin-top:16rpx;color:#5d637d;line-height:1.6}.picker,.input,.textarea{border:1rpx solid #e1e4ee;border-radius:8rpx;padding:20rpx;margin-bottom:18rpx;color:#5d637d}.textarea{width:100%;height:180rpx;box-sizing:border-box}.history-item{padding:18rpx 0;border-bottom:1rpx solid #edf0f5;color:#727990;font-size:24rpx}.history-content{margin-top:8rpx;color:#30364e;font-size:27rpx;line-height:1.6}.state{text-align:center;color:#8b91a6;padding:80rpx 20rpx}
|
||||
@@ -33,6 +33,17 @@ Page({
|
||||
contactAuths: [],
|
||||
contactAuthCount: 0,
|
||||
submittingAuth: false,
|
||||
referralTasks: [],
|
||||
recommendations: [],
|
||||
referralDialogVisible: false,
|
||||
referralTargetIndex: 0,
|
||||
referralTargetLabels: [],
|
||||
referralTypeIndex: 0,
|
||||
referralTypes: ['服务站', '公益活动'],
|
||||
referralTypeCodes: ['service_station', 'activity'],
|
||||
referralTargetId: '',
|
||||
referralNote: '',
|
||||
submittingReferral: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -64,26 +75,28 @@ Page({
|
||||
}
|
||||
|
||||
// 加载工作台数据 + 本人服务对象分配(ZC-MM-002:不再调用 /pending,普通红娘无该权限)
|
||||
const [dashRes, assignmentRes] = await Promise.all([
|
||||
const [dashRes, assignmentRes, referralRes, recommendationRes] = await Promise.all([
|
||||
api.get('/operator/matchmaker/dashboard', {}, { Authorization: `Bearer ${token}` }),
|
||||
api.get('/operator/matchmaker/assignments/my', {}, { Authorization: `Bearer ${token}` }),
|
||||
api.get('/referrals/operator/history', {}, { Authorization: `Bearer ${token}` }),
|
||||
api.get('/operator/matchmaker/recommendations', {}, { Authorization: `Bearer ${token}` }),
|
||||
]);
|
||||
|
||||
if (dashRes.success) {
|
||||
if (dashRes) {
|
||||
this.setData({
|
||||
pendingCount: dashRes.data.pendingCount || 0,
|
||||
inProgressCount: dashRes.data.inProgressCount || 0,
|
||||
todayCount: dashRes.data.todayCount || 0,
|
||||
completedCount: dashRes.data.completedCount || 0,
|
||||
pendingCount: dashRes.pendingCount || 0,
|
||||
inProgressCount: dashRes.inProgressCount || 0,
|
||||
todayCount: dashRes.todayCount || 0,
|
||||
completedCount: dashRes.completedCount || 0,
|
||||
});
|
||||
}
|
||||
|
||||
if (assignmentRes.success) {
|
||||
const assignments = assignmentRes.data || [];
|
||||
if (assignmentRes) {
|
||||
const assignments = assignmentRes || [];
|
||||
// 联系授权申请目标 = 本人生效中的分配对象(B1:只能对分配对象发起)
|
||||
const targets = assignments.map((a) => ({
|
||||
userId: a.userId,
|
||||
label: '用户 ' + String(a.userId).slice(0, 8) + '…(' + (a.assignmentReason || '服务对象') + ')',
|
||||
label: (a.userDisplayNumber || '待实名编号') + '(' + (a.userDisplayName || '服务对象') + ')',
|
||||
}));
|
||||
this.setData({
|
||||
myAssignments: assignments,
|
||||
@@ -93,6 +106,25 @@ Page({
|
||||
});
|
||||
this.loadContactAuthorizations();
|
||||
}
|
||||
if (referralRes) {
|
||||
this.setData({
|
||||
referralTasks: (referralRes || []).map((item) => ({
|
||||
...item,
|
||||
typeLabel: item.type === 'activity' ? '公益活动' : '服务站',
|
||||
targetLabel: item.type === 'activity'
|
||||
? '公益活动转介'
|
||||
: this.serviceTypeLabel(item.serviceType),
|
||||
statusLabel: item.userConfirmedAt ? '用户已确认' : '待用户确认',
|
||||
timeText: this.formatTime(item.createdAt || item.registeredAt),
|
||||
})),
|
||||
});
|
||||
}
|
||||
if (recommendationRes) {
|
||||
this.setData({ recommendations: (recommendationRes || []).map((item) => ({
|
||||
...item,
|
||||
statusLabel: this.recommendationStatusLabel(item.status),
|
||||
})) });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载工作台数据失败:', err);
|
||||
} finally {
|
||||
@@ -103,12 +135,66 @@ Page({
|
||||
onViewService(e) {
|
||||
const serviceId = e.currentTarget.dataset.id;
|
||||
if (!serviceId) {
|
||||
// 纯用户级分配(无关联服务单)无处可跳,不拼空参数
|
||||
const index = Number(e.currentTarget.dataset.index);
|
||||
const item = this.data.myAssignments[index] || {};
|
||||
wx.showModal({
|
||||
title: item.userDisplayNumber || '服务对象',
|
||||
content: `${item.userDisplayName || '未设置姓名'}${item.userAge ? `,${item.userAge}岁` : ''}${item.userRegionName ? `,${item.userRegionName}` : ''}\n当前分配未关联服务单,请联系组长补建服务记录。`,
|
||||
showCancel: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
wx.navigateTo({ url: `/pages/matchmaker-service-detail/matchmaker-service-detail?id=${serviceId}` });
|
||||
},
|
||||
|
||||
openReferral() {
|
||||
if (!this.data.myAssignments.length) {
|
||||
wx.showToast({ title: '暂无可转介的服务对象', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({
|
||||
referralDialogVisible: true,
|
||||
referralTargetIndex: 0,
|
||||
referralTargetLabels: this.data.myAssignments.map((item) =>
|
||||
`${item.userDisplayNumber || '待实名编号'}(${item.userDisplayName || '服务对象'})`),
|
||||
referralTypeIndex: 0,
|
||||
referralTargetId: '',
|
||||
referralNote: '',
|
||||
});
|
||||
},
|
||||
|
||||
closeReferral() { this.setData({ referralDialogVisible: false }); },
|
||||
noop() {},
|
||||
onReferralTargetChange(e) { this.setData({ referralTargetIndex: Number(e.detail.value) }); },
|
||||
onReferralTypeChange(e) { this.setData({ referralTypeIndex: Number(e.detail.value) }); },
|
||||
onReferralTargetIdInput(e) { this.setData({ referralTargetId: e.detail.value }); },
|
||||
onReferralNoteInput(e) { this.setData({ referralNote: e.detail.value }); },
|
||||
|
||||
async submitReferral() {
|
||||
if (this.data.submittingReferral) return;
|
||||
const assignment = this.data.myAssignments[this.data.referralTargetIndex];
|
||||
const targetId = (this.data.referralTargetId || '').trim();
|
||||
if (!assignment || !assignment.userId) { wx.showToast({ title: '请选择服务对象', icon: 'none' }); return; }
|
||||
if (!targetId) { wx.showToast({ title: '请填写活动或服务站编号', icon: 'none' }); return; }
|
||||
this.setData({ submittingReferral: true });
|
||||
try {
|
||||
const token = wx.getStorageSync('operatorToken');
|
||||
const type = this.data.referralTypeCodes[this.data.referralTypeIndex];
|
||||
const path = type === 'activity' ? '/referrals/activity' : '/referrals/service-station';
|
||||
const payload = type === 'activity'
|
||||
? { userId: assignment.userId, activityId: targetId }
|
||||
: { userId: assignment.userId, locationId: targetId, serviceType: 'counselor_consultation', note: this.data.referralNote.trim() };
|
||||
await api.post(path, payload, { Authorization: `Bearer ${token}` }, api.idempotencyKey(`matchmaker-referral:${assignment.userId}:${targetId}`));
|
||||
wx.showToast({ title: '转介已提交,等待用户确认', icon: 'none' });
|
||||
this.setData({ referralDialogVisible: false });
|
||||
await this.loadDashboard();
|
||||
} catch (error) {
|
||||
wx.showToast({ title: (error && error.message) || '转介提交失败', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ submittingReferral: false });
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== ZC-MM-003 / B1:受控联系授权 ====================
|
||||
|
||||
/** 本人授权列表(GET /operator/matchmaker/contact-authorizations/my) */
|
||||
@@ -117,8 +203,8 @@ Page({
|
||||
const token = wx.getStorageSync('operatorToken');
|
||||
const res = await api.get('/operator/matchmaker/contact-authorizations/my',
|
||||
{}, { Authorization: `Bearer ${token}` });
|
||||
if (res.success) {
|
||||
const items = (res.data.list || []).map((item) => Object.assign({}, item, {
|
||||
if (res) {
|
||||
const items = ((res.list || [])).map((item) => Object.assign({}, item, {
|
||||
channelLabel: this.contactChannelLabel(item.channel),
|
||||
statusLabel: this.contactStatusLabel(item.status),
|
||||
expireText: item.expiresAt ? this.formatTime(item.expiresAt) : '',
|
||||
@@ -145,6 +231,20 @@ Page({
|
||||
return labels[code] || code || '';
|
||||
},
|
||||
|
||||
serviceTypeLabel(code) {
|
||||
const labels = {
|
||||
counselor_consultation: '婚恋咨询',
|
||||
premarital_counseling: '婚前辅导',
|
||||
legal_consultation: '法律咨询',
|
||||
};
|
||||
return labels[code] || '服务站服务';
|
||||
},
|
||||
|
||||
recommendationStatusLabel(code) {
|
||||
const labels = { pending_view: '待用户查看', viewed: '用户已查看', liked: '用户已喜欢', passed: '用户已跳过', confirmed_mutual: '已双向确认', expired: '已过期' };
|
||||
return labels[code] || code || '未知状态';
|
||||
},
|
||||
|
||||
onContactTargetChange(e) { this.setData({ contactTargetIndex: Number(e.detail.value) }); },
|
||||
onContactChannelChange(e) { this.setData({ contactChannelIndex: Number(e.detail.value) }); },
|
||||
onContactPurposeInput(e) { this.setData({ contactPurpose: e.detail.value }); },
|
||||
@@ -163,7 +263,7 @@ Page({
|
||||
const res = await api.post('/operator/matchmaker/contact-authorizations', {
|
||||
userId: target.userId, channel: channel, purpose: purpose,
|
||||
}, { Authorization: `Bearer ${token}` });
|
||||
if (res.success) {
|
||||
if (res) {
|
||||
wx.showToast({ title: '申请已提交,等待审批', icon: 'none' });
|
||||
this.setData({ contactPurpose: '' });
|
||||
this.loadContactAuthorizations();
|
||||
@@ -190,7 +290,7 @@ Page({
|
||||
const token = wx.getStorageSync('operatorToken');
|
||||
const resp = await api.post(`/operator/matchmaker/contact-authorizations/${id}/revoke`,
|
||||
{ reason: reason }, { Authorization: `Bearer ${token}` });
|
||||
if (resp.success) {
|
||||
if (resp) {
|
||||
wx.showToast({ title: '已撤销', icon: 'none' });
|
||||
this.loadContactAuthorizations();
|
||||
}
|
||||
|
||||
@@ -16,28 +16,36 @@
|
||||
<view class="stats-container">
|
||||
<view class="stat-card" hover-class="stat-hover">
|
||||
<view class="stat-icon pending">📋</view>
|
||||
<view class="stat-value">{{pendingCount}}</view>
|
||||
<view class="stat-label">待分配</view>
|
||||
<view class="stat-info">
|
||||
<view class="stat-value">{{pendingCount}}</view>
|
||||
<view class="stat-label">待分配</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-card" hover-class="stat-hover">
|
||||
<view class="stat-icon progress">⏳</view>
|
||||
<view class="stat-value">{{inProgressCount}}</view>
|
||||
<view class="stat-label">进行中</view>
|
||||
<view class="stat-info">
|
||||
<view class="stat-value">{{inProgressCount}}</view>
|
||||
<view class="stat-label">进行中</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-card" hover-class="stat-hover">
|
||||
<view class="stat-icon completed">✅</view>
|
||||
<view class="stat-value">{{completedCount}}</view>
|
||||
<view class="stat-label">已完成</view>
|
||||
<view class="stat-info">
|
||||
<view class="stat-value">{{completedCount}}</view>
|
||||
<view class="stat-label">已完成</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-card" hover-class="stat-hover">
|
||||
<view class="stat-icon today">📅</view>
|
||||
<view class="stat-value">{{todayCount}}</view>
|
||||
<view class="stat-label">今日</view>
|
||||
<view class="stat-info">
|
||||
<view class="stat-value">{{todayCount}}</view>
|
||||
<view class="stat-label">今日</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的服务对象分配 -->
|
||||
<view class="section" wx:if="{{!loading}}">
|
||||
<view class="section section-compact" wx:if="{{!loading}}">
|
||||
<view class="section-header">
|
||||
<view class="section-title-wrap">
|
||||
<text class="section-dot"></text>
|
||||
@@ -52,6 +60,7 @@
|
||||
wx:key="id"
|
||||
class="service-card"
|
||||
data-id="{{item.serviceId}}"
|
||||
data-index="{{index}}"
|
||||
bindtap="onViewService"
|
||||
>
|
||||
<view class="service-left">
|
||||
@@ -60,18 +69,78 @@
|
||||
<view class="service-tag">{{assignmentStatusLabel(item.status)}}</view>
|
||||
<view class="service-time">{{formatTime(item.assignedAt)}}</view>
|
||||
</view>
|
||||
<view class="service-desc">{{item.assignmentReason}}</view>
|
||||
<view class="service-name">{{item.userDisplayName || '未设置姓名'}} <text wx:if="{{item.userDisplayNumber}}">{{item.userDisplayNumber}}</text></view>
|
||||
<view class="service-desc">{{item.userAge ? item.userAge + '岁 · ' : ''}}{{item.userRegionName || '地区未设置'}}</view>
|
||||
</view>
|
||||
<button class="detail-btn" size="mini" catchtap="onViewService" data-id="{{item.serviceId}}" data-index="{{index}}">查看详情</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" wx:else>
|
||||
<view class="empty-state empty-compact" wx:else>
|
||||
<view class="empty-illustration">📭</view>
|
||||
<view class="empty-text">暂无服务对象分配</view>
|
||||
<view class="empty-hint">分配给你的服务对象将在这里展示</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section section-compact" wx:if="{{!loading}}">
|
||||
<view class="section-header">
|
||||
<view class="section-title-wrap">
|
||||
<text class="section-dot"></text>
|
||||
<text class="section-title">转介任务</text>
|
||||
</view>
|
||||
<text class="section-count">共{{referralTasks.length}}条</text>
|
||||
</view>
|
||||
<view class="auth-list" wx:if="{{referralTasks.length}}">
|
||||
<view class="auth-card" wx:for="{{referralTasks}}" wx:key="id">
|
||||
<view class="auth-row">
|
||||
<text class="auth-channel">{{item.typeLabel}}</text>
|
||||
<text class="auth-status">{{item.statusLabel}}</text>
|
||||
</view>
|
||||
<view class="auth-purpose">{{item.targetLabel}}</view>
|
||||
<view class="auth-meta">{{item.timeText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty-state empty-compact" wx:else>
|
||||
<view class="empty-text">暂无转介记录</view>
|
||||
<view class="empty-hint">可从服务对象分配发起公益活动或服务站转介</view>
|
||||
</view>
|
||||
<button class="submit-btn referral-open-btn" bindtap="openReferral">发起转介</button>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{referralDialogVisible}}" class="dialog-mask">
|
||||
<view class="dialog-card" catchtap="noop">
|
||||
<view class="dialog-title">发起转介</view>
|
||||
<picker mode="selector" range="{{referralTargetLabels}}" value="{{referralTargetIndex}}" bindchange="onReferralTargetChange">
|
||||
<view class="form-row"><text class="form-label">服务对象</text><text class="form-value">{{referralTargetLabels[referralTargetIndex]}}</text></view>
|
||||
</picker>
|
||||
<picker mode="selector" range="{{referralTypes}}" value="{{referralTypeIndex}}" bindchange="onReferralTypeChange">
|
||||
<view class="form-row"><text class="form-label">转介类型</text><text class="form-value">{{referralTypes[referralTypeIndex]}}</text></view>
|
||||
</picker>
|
||||
<input class="form-input" value="{{referralTargetId}}" bindinput="onReferralTargetIdInput" placeholder="填写活动或服务站编号" />
|
||||
<input wx:if="{{referralTypeIndex === 0}}" class="form-input" value="{{referralNote}}" bindinput="onReferralNoteInput" placeholder="备注(可选)" />
|
||||
<view class="dialog-actions">
|
||||
<button size="mini" bindtap="closeReferral">取消</button>
|
||||
<button size="mini" type="primary" loading="{{submittingReferral}}" bindtap="submitReferral">提交转介</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section" wx:if="{{!loading}}">
|
||||
<view class="section-header">
|
||||
<view class="section-title-wrap"><text class="section-dot"></text><text class="section-title">推荐进展</text></view>
|
||||
<text class="section-count">共{{recommendations.length}}条</text>
|
||||
</view>
|
||||
<view class="auth-list" wx:if="{{recommendations.length}}">
|
||||
<view class="auth-card" wx:for="{{recommendations}}" wx:key="id">
|
||||
<view class="auth-row"><text class="auth-channel">候选建议</text><text class="auth-status">{{item.statusLabel}}</text></view>
|
||||
<view class="auth-purpose">推荐对象:{{item.targetUserDisplayNumber || '待生成编号'}}</view>
|
||||
<view class="auth-meta">推荐理由:{{item.reason || '未填写'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty-state" wx:else><view class="empty-hint">暂无推荐进展,用户将自行决定喜欢或暂不考虑</view></view>
|
||||
</view>
|
||||
|
||||
<!-- 联系授权(ZC-MM-003 / B1:受控联系,用户单独同意后才可生效) -->
|
||||
<view class="section" wx:if="{{!loading}}">
|
||||
<view class="section-header">
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
font-size:22rpx;
|
||||
}
|
||||
|
||||
/* 数据概览 - 2x2 网格 */
|
||||
/* 数据概览 - 2x2 紧凑网格(原高度 ~234rpx → 压缩到 ~80rpx,约 1/3) */
|
||||
.stats-container {
|
||||
display:flex;
|
||||
flex-wrap:wrap;
|
||||
@@ -66,31 +66,51 @@
|
||||
|
||||
.stat-card {
|
||||
width:calc(50% - 16rpx);
|
||||
margin:0 8rpx 24rpx 8rpx;
|
||||
margin:0 8rpx 16rpx 8rpx;
|
||||
background:var(--color-bg-card);
|
||||
border-radius:24rpx;
|
||||
padding:40rpx 20rpx;
|
||||
text-align:center;
|
||||
box-shadow:0 8rpx 32rpx rgba(0,0,0,0.1);
|
||||
border-radius:20rpx;
|
||||
padding:16rpx 20rpx;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:16rpx;
|
||||
box-shadow:0 4rpx 16rpx rgba(0,0,0,0.08);
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size:48rpx;
|
||||
margin-bottom:16rpx;
|
||||
font-size:36rpx;
|
||||
flex:none;
|
||||
width:64rpx;
|
||||
height:64rpx;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
background:var(--color-primary-light);
|
||||
border-radius:16rpx;
|
||||
}
|
||||
|
||||
.stat-card .stat-icon.progress { background:#FFF6E6; }
|
||||
.stat-card .stat-icon.completed { background:#EDF8F1; }
|
||||
.stat-card .stat-icon.today { background:#EEF5FF; }
|
||||
|
||||
.stat-info {
|
||||
flex:1;
|
||||
min-width:0;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size:56rpx;
|
||||
font-size:40rpx;
|
||||
font-weight:700;
|
||||
color:var(--color-text-strong);
|
||||
line-height:1.2;
|
||||
margin-bottom:8rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size:26rpx;
|
||||
font-size:22rpx;
|
||||
color:var(--color-text-tertiary);
|
||||
margin-top:4rpx;
|
||||
}
|
||||
|
||||
/* 列表区块 */
|
||||
@@ -152,6 +172,7 @@
|
||||
border-radius:16rpx;
|
||||
border:1rpx solid var(--color-divider);
|
||||
}
|
||||
.detail-btn{flex:none;margin:0;background:#2f7df6;color:#fff;font-size:22rpx;padding:8rpx 16rpx;line-height:1.6;border-radius:8rpx}
|
||||
|
||||
.service-left {
|
||||
flex:1;
|
||||
@@ -222,6 +243,27 @@
|
||||
color:var(--color-text-disabled);
|
||||
}
|
||||
|
||||
/* 紧凑区块:仅“我的服务对象分配 / 转介任务”两栏,垂直高度约为原来的一半 */
|
||||
.section-compact {
|
||||
padding:24rpx 32rpx;
|
||||
}
|
||||
.section-compact .section-header {
|
||||
margin-bottom:12rpx;
|
||||
}
|
||||
.empty-compact {
|
||||
padding:12rpx 40rpx;
|
||||
}
|
||||
.empty-compact .empty-illustration {
|
||||
font-size:48rpx;
|
||||
margin-bottom:4rpx;
|
||||
}
|
||||
.empty-compact .empty-text {
|
||||
margin-bottom:2rpx;
|
||||
}
|
||||
.section-compact .referral-open-btn {
|
||||
margin-top:8rpx;
|
||||
}
|
||||
|
||||
/* 加载中 */
|
||||
.loading-wrap {
|
||||
display:flex;
|
||||
@@ -281,3 +323,8 @@ min-width:var(--tap-min);}
|
||||
.auth-purpose { font-size: 26rpx; color: #5e6d82; margin-top: 8rpx; word-break: break-all; }
|
||||
.auth-meta { font-size: 22rpx; color: #b0b3b8; margin-top: 8rpx; }
|
||||
.revoke-btn { margin-top: 12rpx; background: #f4f4f5; color: #606266; }
|
||||
.referral-open-btn{margin-top:20rpx;width:100%}
|
||||
.dialog-mask{position:fixed;z-index:99;left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;padding:32rpx;box-sizing:border-box}
|
||||
.dialog-card{width:100%;background:#fff;border-radius:20rpx;padding:28rpx;box-sizing:border-box}
|
||||
.dialog-title{font-size:32rpx;font-weight:600;color:#20243a;margin-bottom:20rpx}
|
||||
.dialog-actions{display:flex;justify-content:flex-end;gap:16rpx;margin-top:20rpx}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* - 推荐:红娘推荐列表跳 fate-center 页(AGENTS 第 5 条:红娘不可替用户操作)
|
||||
*/
|
||||
import { matchmakerStore, appStore } from '../../stores';
|
||||
import { ROUTE, to, buildPath, MATCHMAKER_SERVICE_STATUS, MATCHMAKER_SERVICE_STATUS_LABEL } from '../../constants';
|
||||
import { ROUTE, to, MATCHMAKER_SERVICE_STATUS, MATCHMAKER_SERVICE_STATUS_LABEL } from '../../constants';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -89,7 +89,7 @@ Page({
|
||||
onViewRecommendations(e: any) {
|
||||
const serviceId = e.detail.serviceId;
|
||||
if (!serviceId) return;
|
||||
wx.navigateTo({ url: buildPath(ROUTE.MAIN.FATE_CENTER, { id: serviceId }) });
|
||||
wx.navigateTo({ url: ROUTE.SERVICE.MATCHMAKER_RECOMMENDATIONS });
|
||||
},
|
||||
|
||||
onHistoryTap(e: any) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<view class="profile-copy">
|
||||
<view class="profile-name-row"><text class="profile-name">{{profile.displayName || '未填写昵称'}}</text><text class="profile-status">{{relationshipText}}</text></view>
|
||||
<text class="profile-meta">资料完整度 {{profileCompleteness}}%</text>
|
||||
<text wx:if="{{profile.userDisplayNumber || profile.displayNumber}}" class="profile-meta">用户编号:{{profile.userDisplayNumber || profile.displayNumber}}</text>
|
||||
<view class="profile-progress"><view style="width:{{profileCompleteness}}%"></view></view>
|
||||
<view class="profile-badges"><ui-status-badge kind="{{isRealNameVerified ? 'success' : 'warning'}}" label="{{isRealNameVerified ? '已实名' : '未实名'}}" /><ui-status-badge kind="{{isProfileApproved ? 'success' : 'warning'}}" label="{{isProfileApproved ? '资料已审核' : '资料待审核'}}" /></view>
|
||||
</view>
|
||||
@@ -76,4 +77,7 @@
|
||||
<view class="logout-row" bindtap="onLogout">退出登录</view>
|
||||
<ui-danger-action-dialog visible="{{showLogoutDialog}}" action="{{logoutAction}}" bind:confirm="onConfirmLogout" bind:cancel="onCancelLogout" />
|
||||
</block>
|
||||
<view class="list-group">
|
||||
<view class="list-row" bindtap="onSwitchRole"><text class="list-label">红娘工作人员入口</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
const { referralService } = require('../../services');
|
||||
|
||||
const STATUS_LABEL = {
|
||||
submitted: '待您确认',
|
||||
pending_confirmation: '待您确认',
|
||||
confirmed: '已确认',
|
||||
cancelled: '已取消',
|
||||
completed: '已完成',
|
||||
};
|
||||
|
||||
Page({
|
||||
data: { loading: true, error: '', referrals: [], confirmingId: '' },
|
||||
|
||||
onShow() { this.load(); },
|
||||
|
||||
async load() {
|
||||
this.setData({ loading: true, error: '' });
|
||||
try {
|
||||
const list = await referralService.getMyReferralHistory();
|
||||
this.setData({
|
||||
referrals: (list || []).map((item) => ({
|
||||
id: item.id,
|
||||
referralDisplayId: item.referralDisplayId,
|
||||
targetType: item.type,
|
||||
title: item.type === 'activity' ? '公益活动转介' : '服务站转介',
|
||||
detail: item.type === 'activity' ? (item.activityDisplayId || '公益活动') : (item.serviceType || '公益服务'),
|
||||
status: item.userConfirmedAt ? 'confirmed' : (item.status || 'pending_confirmation'),
|
||||
statusText: item.userConfirmedAt ? '已确认' : (STATUS_LABEL[item.status] || '待您确认'),
|
||||
createdAt: item.createdAt || item.registeredAt || '',
|
||||
})),
|
||||
loading: false,
|
||||
});
|
||||
} catch (error) {
|
||||
this.setData({ loading: false, error: (error && error.message) || '加载失败,请稍后重试' });
|
||||
}
|
||||
},
|
||||
|
||||
async confirm(e) {
|
||||
const item = this.data.referrals[Number(e.currentTarget.dataset.index)];
|
||||
if (!item || item.status === 'confirmed' || this.data.confirmingId) return;
|
||||
this.setData({ confirmingId: item.id });
|
||||
try {
|
||||
await referralService.confirmReferral(item.id, item.targetType);
|
||||
wx.showToast({ title: '已确认', icon: 'success' });
|
||||
await this.load();
|
||||
} catch (error) {
|
||||
wx.showToast({ title: (error && error.message) || '确认失败,请重试', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ confirmingId: '' });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"navigationBarTitleText":"公益服务确认","usingComponents":{}}
|
||||
@@ -0,0 +1,12 @@
|
||||
<view class="page">
|
||||
<view class="hero"><text class="title">待确认的公益服务</text><text class="desc">红娘提供的服务建议,需要由你本人确认后才会生效。</text></view>
|
||||
<view wx:if="{{loading}}" class="empty">正在加载</view>
|
||||
<view wx:elif="{{error}}" class="empty">{{error}}</view>
|
||||
<view wx:elif="{{referrals.length}}" class="list">
|
||||
<view wx:for="{{referrals}}" wx:key="id" class="item">
|
||||
<view class="item-main"><text class="item-title">{{item.title}}</text><text class="item-detail">转介编号:{{item.referralDisplayId}}</text><text class="item-detail">{{item.detail}}</text><text class="item-date">{{item.createdAt}}</text></view>
|
||||
<view class="item-action"><text class="status">{{item.statusText}}</text><button wx:if="{{item.status !== 'confirmed'}}" size="mini" data-index="{{index}}" bindtap="confirm" loading="{{confirmingId === item.id}}">确认转介</button></view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else class="empty">暂无待确认的转介</view>
|
||||
</view>
|
||||
@@ -0,0 +1,11 @@
|
||||
.page { min-height: 100vh; padding: 32rpx; background: var(--color-primary-lighter); box-sizing: border-box; }
|
||||
.hero, .item { background: var(--color-bg-card); border-radius: 24rpx; padding: 28rpx; }
|
||||
.hero { margin-bottom: 24rpx; }
|
||||
.title, .item-title { display: block; color: var(--color-text-strong); font-weight: bold; font-size: 32rpx; }
|
||||
.desc, .item-detail, .item-date, .status, .empty { display: block; color: var(--color-text-tertiary); font-size: 26rpx; line-height: 1.6; }
|
||||
.desc { margin-top: 12rpx; }
|
||||
.item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
|
||||
.item-detail, .item-date { margin-top: 8rpx; }
|
||||
.item-action { display: flex; flex-direction: column; align-items: flex-end; gap: 12rpx; margin-left: 20rpx; }
|
||||
.status { color: var(--color-primary-solid); white-space: nowrap; }
|
||||
.empty { padding: 120rpx 20rpx; text-align: center; }
|
||||
@@ -1 +1 @@
|
||||
<view class="service-page"><view class="hero"><text class="eyebrow">善缘公益服务</text><text class="hero-title">需要帮助时,我们在这里</text><text class="hero-desc">从线下服务站到专业咨询,所有服务尊重个人意愿与隐私。</text></view><view class="content"><view class="block"><text class="block-title">选择需要的服务</text><text class="block-subtitle">每项服务均清晰说明范围与流程</text><view class="card"><view class="feature-row" data-url="/pages/service-station/service-station" bindtap="go"><view class="feature-icon">站</view><view class="feature-copy"><text class="feature-title">善缘服务站</text><text class="feature-desc">线下咨询、资料协助与活动服务</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/counseling-intro/counseling-intro" bindtap="go"><view class="feature-icon">咨</view><view class="feature-copy"><text class="feature-title">婚恋咨询</text><text class="feature-desc">由咨询人员提供关系与情绪支持</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/premarital-intro/premarital-intro" bindtap="go"><view class="feature-icon">伴</view><view class="feature-copy"><text class="feature-title">婚前辅导</text><text class="feature-desc">帮助伴侣认真讨论共同生活</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/service-program/service-program" bindtap="go"><view class="feature-icon">益</view><view class="feature-copy"><text class="feature-title">专项公益服务</text><text class="feature-desc">面向特定群体的公益支持项目</text></view><text class="arrow">›</text></view></view></view></view></view>
|
||||
<view class="service-page"><view class="hero"><text class="eyebrow">善缘公益服务</text><text class="hero-title">需要帮助时,我们在这里</text><text class="hero-desc">从线下服务站到专业咨询,所有服务尊重个人意愿与隐私。</text></view><view class="content"><view class="block"><text class="block-title">选择需要的服务</text><text class="block-subtitle">每项服务均清晰说明范围与流程</text><view class="card"><view class="feature-row" data-url="/pages/service-station/service-station" bindtap="go"><view class="feature-icon">站</view><view class="feature-copy"><text class="feature-title">善缘服务站</text><text class="feature-desc">线下咨询、资料协助与活动服务</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/counseling-intro/counseling-intro" bindtap="go"><view class="feature-icon">咨</view><view class="feature-copy"><text class="feature-title">婚恋咨询</text><text class="feature-desc">由咨询人员提供关系与情绪支持</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/premarital-intro/premarital-intro" bindtap="go"><view class="feature-icon">伴</view><view class="feature-copy"><text class="feature-title">婚前辅导</text><text class="feature-desc">帮助伴侣认真讨论共同生活</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/service-program/service-program" bindtap="go"><view class="feature-icon">益</view><view class="feature-copy"><text class="feature-title">专项公益服务</text><text class="feature-desc">面向特定群体的公益支持项目</text></view><text class="arrow">›</text></view><view class="feature-row" data-url="/pages/referral-confirmation/referral-confirmation" bindtap="go"><view class="feature-icon">确</view><view class="feature-copy"><text class="feature-title">待确认服务</text><text class="feature-desc">查看红娘为你发起的服务建议</text></view><text class="arrow">›</text></view></view></view></view></view>
|
||||
|
||||
Reference in New Issue
Block a user