chore: establish V1.1 recovery baseline
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* RecommendCard 推荐卡片(规范 §22-25, §107-108)
|
||||
*
|
||||
* 推荐首页核心卡片,包含:头像 + 基本信息 + 推荐理由 + Like/Pass 操作。
|
||||
* 不变量(AGENTS.md):
|
||||
* - 第 2 条:Required 不可自动放宽——推荐理由仅作弱文字提示,
|
||||
* 不暴露"放宽了 X 条件才得到这个推荐"的字样,前端只显示后端返回的 reasons。
|
||||
* - 第 5 条:红娘不可替用户操作——本卡片仅由当前用户操作,不接受 matchmakerId 参数。
|
||||
*/
|
||||
const { STATUS_BADGE } = require('../../constants');
|
||||
|
||||
Component({
|
||||
options: { addGlobalClass: true },
|
||||
properties: {
|
||||
recommendation: { type: Object, value: null },
|
||||
// 是否显示操作按钮(详情页用,可能用底部固定按钮替代)
|
||||
showActions: { type: Boolean, value: true },
|
||||
// 是否禁用按钮(操作进行中)
|
||||
actionLoading: { type: Boolean, value: false },
|
||||
},
|
||||
data: {
|
||||
levelTextMap: {
|
||||
high: '很合适',
|
||||
medium: '比较合适',
|
||||
normal: '值得了解',
|
||||
},
|
||||
levelKindMap: {
|
||||
high: 'success',
|
||||
medium: 'info',
|
||||
normal: 'warning',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onViewDetail() {
|
||||
if (!this.data.recommendation) return;
|
||||
this.triggerEvent('view', { userId: this.data.recommendation.userId });
|
||||
},
|
||||
onLike() {
|
||||
if (this.data.actionLoading) return;
|
||||
if (!this.data.recommendation) return;
|
||||
this.triggerEvent('like', { userId: this.data.recommendation.userId });
|
||||
},
|
||||
onPass() {
|
||||
if (this.data.actionLoading) return;
|
||||
if (!this.data.recommendation) return;
|
||||
this.triggerEvent('pass', { userId: this.data.recommendation.userId });
|
||||
},
|
||||
onBlock(e) {
|
||||
// 阻止冒泡避免触发 view
|
||||
this.triggerEvent('block', { userId: this.data.recommendation.userId });
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"ui-avatar": "../avatar/index",
|
||||
"ui-status-badge": "../status-badge/index",
|
||||
"ui-button": "../button/index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<view class="ui-rec" wx:if="{{recommendation}}">
|
||||
<view class="ui-rec__header" bindtap="onViewDetail">
|
||||
<ui-avatar
|
||||
src="{{recommendation.avatarUrl}}"
|
||||
size="xl"
|
||||
verified="{{recommendation.verified}}"
|
||||
reviewed="{{recommendation.reviewed}}"
|
||||
default-text="{{recommendation.displayName}}"
|
||||
/>
|
||||
<view class="ui-rec__info">
|
||||
<view class="ui-rec__name-row">
|
||||
<text class="ui-rec__name">{{recommendation.displayName}}</text>
|
||||
<text class="ui-rec__age">{{recommendation.age}}岁</text>
|
||||
</view>
|
||||
<view class="ui-rec__meta">
|
||||
<text wx:if="{{recommendation.heightCm}}">{{recommendation.heightCm}}cm · </text>
|
||||
<text>{{recommendation.regionName}}</text>
|
||||
<text wx:if="{{recommendation.educationCode}}"> · {{recommendation.educationCode}}</text>
|
||||
</view>
|
||||
<view wx:if="{{recommendation.occupationCode}}" class="ui-rec__meta">
|
||||
{{recommendation.occupationCode}}
|
||||
</view>
|
||||
<view wx:if="{{recommendation.maritalStatus || recommendation.hasChildren}}" class="ui-rec__meta">
|
||||
<text wx:if="{{recommendation.maritalStatus}}">{{recommendation.maritalStatus}}</text>
|
||||
<text wx:if="{{recommendation.hasChildren}}"> · 有孩</text>
|
||||
</view>
|
||||
<view wx:if="{{recommendation.photoUrls}}" class="ui-rec__photo-count">
|
||||
{{recommendation.photoUrls.length}} 张生活照
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
wx:if="{{recommendation.recommendationLevel}}"
|
||||
class="ui-rec__level ui-rec__level--{{recommendation.recommendationLevel}}"
|
||||
>
|
||||
{{levelTextMap[recommendation.recommendationLevel]}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
wx:if="{{recommendation.recommendationReasons && recommendation.recommendationReasons.length}}"
|
||||
class="ui-rec__reasons"
|
||||
>
|
||||
<view class="ui-rec__reasons-title">为什么推荐给你</view>
|
||||
<view
|
||||
wx:for="{{recommendation.recommendationReasons}}"
|
||||
wx:key="*this"
|
||||
class="ui-rec__reason"
|
||||
>✓ {{item}}</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{recommendation.intro}}" class="ui-rec__intro">
|
||||
{{recommendation.intro}}
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showActions}}" class="ui-rec__actions">
|
||||
<ui-button
|
||||
variant="ghost"
|
||||
size="md"
|
||||
disabled="{{actionLoading}}"
|
||||
class="ui-rec__btn-pass"
|
||||
bindtap="onPass"
|
||||
>不合适</ui-button>
|
||||
<ui-button
|
||||
variant="primary"
|
||||
size="md"
|
||||
loading="{{actionLoading}}"
|
||||
class="ui-rec__btn-like"
|
||||
bindtap="onLike"
|
||||
>喜欢</ui-button>
|
||||
</view>
|
||||
|
||||
<view class="ui-rec__more">
|
||||
<text class="ui-rec__more-text" catchtap="onBlock">拉黑该用户</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,113 @@
|
||||
.ui-rec {
|
||||
background: var(--color-bg-card);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-3);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.ui-rec__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
}
|
||||
.ui-rec__info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: var(--spacing-3);
|
||||
}
|
||||
.ui-rec__name-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
.ui-rec__name {
|
||||
font-size: var(--font-size-2xl);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
.ui-rec__age {
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
.ui-rec__meta {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: var(--line-height-normal);
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
.ui-rec__photo-count {
|
||||
margin-top: var(--spacing-2);
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.ui-rec__level {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: var(--radius-full);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.ui-rec__level--high {
|
||||
background: var(--color-success-light);
|
||||
color: var(--color-success);
|
||||
}
|
||||
.ui-rec__level--medium {
|
||||
background: var(--color-info-light);
|
||||
color: var(--color-info);
|
||||
}
|
||||
.ui-rec__level--normal {
|
||||
background: var(--color-warning-light);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
.ui-rec__reasons {
|
||||
margin-top: var(--spacing-3);
|
||||
padding: var(--spacing-3);
|
||||
background: var(--color-primary-light);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.ui-rec__reasons-title {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-primary);
|
||||
font-weight: var(--font-weight-medium);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.ui-rec__reason {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: var(--line-height-relaxed);
|
||||
}
|
||||
.ui-rec__intro {
|
||||
margin-top: var(--spacing-3);
|
||||
padding-top: var(--spacing-3);
|
||||
border-top: 1rpx solid var(--color-divider);
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: var(--line-height-relaxed);
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ui-rec__actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
margin-top: var(--spacing-4);
|
||||
}
|
||||
.ui-rec__btn-pass {
|
||||
flex: 1;
|
||||
}
|
||||
.ui-rec__btn-like {
|
||||
flex: 2;
|
||||
}
|
||||
.ui-rec__more {
|
||||
margin-top: var(--spacing-3);
|
||||
text-align: center;
|
||||
}
|
||||
.ui-rec__more .ui-rec__more-text {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-tertiary);
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
}
|
||||
Reference in New Issue
Block a user