chore: establish V1.1 recovery baseline

This commit is contained in:
2026-09-09 22:05:00 +08:00
commit b46b48eb33
947 changed files with 155471 additions and 0 deletions
@@ -0,0 +1,63 @@
/**
* PreferenceEditor 择偶条件编辑器(规范 §30)
*
* 每条条件有两个 importance 等级:
* - required:必须符合(AGENTS 第 2 条硬约束)
* - preferred:最好符合(软约束,后端可适度放宽匹配)
*
* 不变量(AGENTS.md 第 2 条):用户已勾选"必须符合"的字段不可被前端自动放宽为
* "最好符合",UI 必须显式提示用户此操作将影响匹配精度。
*/
Component({
options: { addGlobalClass: true },
properties: {
preference: { type: Object, value: null },
readonly: { type: Boolean, value: false },
},
data: {
importanceLabelMap: {
required: '必须符合',
preferred: '最好符合',
},
},
methods: {
// 切换某字段的 importance:required ↔ preferred
onToggleImportance(e) {
if (this.data.readonly) return;
const { field } = e.currentTarget.dataset;
const pref = this.data.preference || {};
const current = pref[field];
if (!current) return;
const next = current === 'required' ? 'preferred' : 'required';
this.triggerEvent('change', {
field,
importance: next,
// 必须符合 → 最好符合,需用户二次确认(AGENTS 第 2 条)
needConfirmRelax: current === 'required',
});
},
onAgeChange(e) {
if (this.data.readonly) return;
const { bound } = e.currentTarget.dataset;
this.triggerEvent('change', {
field: bound === 'min' ? 'ageMin' : 'ageMax',
value: Number(e.detail.value),
});
},
onHeightChange(e) {
if (this.data.readonly) return;
const { bound } = e.currentTarget.dataset;
this.triggerEvent('change', {
field: bound === 'min' ? 'heightMin' : 'heightMax',
value: Number(e.detail.value) || null,
});
},
onAcceptChildrenChange(e) {
if (this.data.readonly) return;
this.triggerEvent('change', {
field: 'acceptChildren',
value: e.detail.value,
});
},
},
});
@@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"ui-input": "../input/index",
"ui-tag": "../tag/index"
}
}
@@ -0,0 +1,124 @@
<view class="ui-pref" wx:if="{{preference}}">
<view class="ui-pref__hint">
标记为"必须符合"的条件将作为硬约束,不会自动放宽;
"最好符合"为软偏好,可能匹配到略不满足的对象。
</view>
<!-- 年龄 -->
<view class="ui-pref__row">
<view class="ui-pref__label">年龄</view>
<view class="ui-pref__range">
<input
class="ui-pref__num"
type="number"
value="{{preference.ageMin}}"
placeholder="不限"
data-bound="min"
bindinput="onAgeChange"
/>
<text class="ui-pref__dash">~</text>
<input
class="ui-pref__num"
type="number"
value="{{preference.ageMax}}"
placeholder="不限"
data-bound="max"
bindinput="onAgeChange"
/>
</view>
<view
class="ui-pref__importance ui-pref__importance--{{preference.ageImportance}}"
data-field="ageImportance"
bindtap="onToggleImportance"
>{{importanceLabelMap[preference.ageImportance]}}</view>
</view>
<!-- 身高 -->
<view class="ui-pref__row">
<view class="ui-pref__label">身高</view>
<view class="ui-pref__range">
<input
class="ui-pref__num"
type="number"
value="{{preference.heightMin}}"
placeholder="不限"
data-bound="min"
bindinput="onHeightChange"
/>
<text class="ui-pref__dash">~</text>
<input
class="ui-pref__num"
type="number"
value="{{preference.heightMax}}"
placeholder="不限"
data-bound="max"
bindinput="onHeightChange"
/>
</view>
<view
class="ui-pref__importance ui-pref__importance--{{preference.heightImportance}}"
data-field="heightImportance"
bindtap="onToggleImportance"
>{{importanceLabelMap[preference.heightImportance]}}</view>
</view>
<!-- 地区 -->
<view class="ui-pref__row">
<view class="ui-pref__label">地区</view>
<view class="ui-pref__tags">
<ui-tag
wx:for="{{preference.region}}"
wx:key="*this"
label="{{item}}"
variant="primary"
/>
</view>
<view
class="ui-pref__importance ui-pref__importance--{{preference.regionImportance}}"
data-field="regionImportance"
bindtap="onToggleImportance"
>{{importanceLabelMap[preference.regionImportance]}}</view>
</view>
<!-- 婚姻状况 -->
<view class="ui-pref__row">
<view class="ui-pref__label">婚姻状况</view>
<view class="ui-pref__tags">
<ui-tag
wx:for="{{preference.maritalStatusAccepted}}"
wx:key="*this"
label="{{item}}"
variant="primary"
/>
</view>
</view>
<!-- 学历 -->
<view class="ui-pref__row">
<view class="ui-pref__label">学历</view>
<view class="ui-pref__tags">
<ui-tag
wx:for="{{preference.educationAccepted}}"
wx:key="*this"
label="{{item}}"
variant="primary"
/>
</view>
</view>
<!-- 是否接受有孩 -->
<view class="ui-pref__row">
<view class="ui-pref__label">子女</view>
<picker
class="ui-pref__picker"
value="{{preference.acceptChildren}}"
range="{{['any', 'accept', 'reject']}}"
range-key=""
bindchange="onAcceptChildrenChange"
>
<view class="ui-pref__picker-value">
{{preference.acceptChildren === 'any' ? '不限' : preference.acceptChildren === 'accept' ? '可接受' : '不接受'}}
</view>
</picker>
</view>
</view>
@@ -0,0 +1,72 @@
.ui-pref__hint {
padding: var(--spacing-2) var(--spacing-3);
background: var(--color-warning-light);
border-radius: var(--radius-sm);
font-size: var(--font-size-xs);
color: var(--color-warning);
line-height: var(--line-height-normal);
margin-bottom: var(--spacing-3);
}
.ui-pref__row {
display: flex;
align-items: center;
padding: var(--spacing-3) 0;
border-bottom: 1rpx solid var(--color-divider);
}
.ui-pref__label {
width: 140rpx;
font-size: var(--font-size-md);
color: var(--color-text-secondary);
flex-shrink: 0;
}
.ui-pref__range {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.ui-pref__num {
flex: 1;
min-width: 0;
height: 64rpx;
padding: 0 var(--spacing-2);
border: 1rpx solid var(--color-border);
border-radius: var(--radius-sm);
font-size: var(--font-size-md);
text-align: center;
}
.ui-pref__dash {
margin: 0 var(--spacing-2);
color: var(--color-text-tertiary);
}
.ui-pref__tags {
flex: 1;
min-width: 0;
display: flex;
flex-wrap: wrap;
gap: var(--spacing-1);
}
.ui-pref__picker {
flex: 1;
}
.ui-pref__picker-value {
padding: var(--spacing-2) 0;
font-size: var(--font-size-md);
color: var(--color-text-primary);
}
.ui-pref__importance {
margin-left: var(--spacing-2);
padding: 6rpx 16rpx;
border-radius: var(--radius-full);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
flex-shrink: 0;
}
.ui-pref__importance--required {
background: var(--color-danger-light);
color: var(--color-danger);
}
.ui-pref__importance--preferred {
background: var(--color-bg-disabled);
color: var(--color-text-tertiary);
}