Compare commits

...

34 Commits

Author SHA1 Message Date
rain 355f47976a 修改未完成 2026-07-13 17:31:11 +08:00
rain 965fff1621 docs: accept A01 visual baseline 2026-07-13 16:27:23 +08:00
rain 3d36fbba13 docs: plan A01 reference asset rebuild 2026-07-13 16:08:13 +08:00
rain 7fd66db51e docs: specify A01 reference asset rebuild 2026-07-13 16:06:04 +08:00
rain 778cd8e7bf fix: add A01 ornamental auth assets 2026-07-13 16:00:22 +08:00
rain 83d58268ac fix: refine A01 runtime visual calibration 2026-07-13 15:41:51 +08:00
rain f7d6d3cfb1 fix: calibrate A01 entry visual layout 2026-07-13 15:35:16 +08:00
rain bd50f15d5c docs: plan A01 runtime visual calibration 2026-07-13 15:28:16 +08:00
rain a0207ba2cf docs: specify A01 runtime visual calibration 2026-07-13 15:25:02 +08:00
rain 5a3adbc5d0 fix: rebuild A01 as responsive page 2026-07-13 14:57:57 +08:00
rain 225c5272b6 fix: match A01 v3 visual reference 2026-07-13 14:47:28 +08:00
rain b1b635c6ae docs: plan A01 v3 visual correction 2026-07-13 14:38:37 +08:00
rain 134ae75c2b feat: add static A01 A02 auth UI 2026-07-13 14:31:19 +08:00
rain 89dd20cec0 docs: plan A01 A02 static UI implementation 2026-07-13 14:16:34 +08:00
rain bf4c6dc5fa docs: authorize A01 A02 local UI 2026-07-13 14:10:29 +08:00
rain c41d155ffe docs: archive A01 static size evidence 2026-07-13 13:56:03 +08:00
rain 86a41bcebc assets: add A01 login design resources 2026-07-13 13:39:27 +08:00
rain 5507ba96b4 docs: define A01 grid and entry states 2026-07-13 13:18:04 +08:00
rain f684ae3f92 docs: plan A01 design evidence 2026-07-13 13:12:46 +08:00
rain 1c9830c550 docs: refine A01 login entry hierarchy 2026-07-13 13:00:34 +08:00
rain 040c2cb37e docs: approve A01 login guide design 2026-07-13 12:23:10 +08:00
rain aacd503e2f docs: plan A01 login guide 2026-07-13 11:22:52 +08:00
rain 35364f3818 docs: define A01 login guide 2026-07-13 11:04:12 +08:00
rain 974369587a feat: make root header status-bar safe 2026-07-13 10:31:52 +08:00
rain 086917224a docs: allow local third-party brand assets 2026-07-13 10:16:01 +08:00
rain b61ee990cd docs: add WeChat login design scope 2026-07-13 10:13:39 +08:00
rain a68f2a51ad fix: improve G01 list and create action legibility 2026-07-13 10:05:01 +08:00
rain 6db93d8c1b feat: align G01 shared visual shell 2026-07-13 09:56:45 +08:00
rain 79c195cb0d feat: restructure G01 information hierarchy 2026-07-13 09:54:39 +08:00
rain ab8f9384c3 feat: replace G01 create cloud ornament 2026-07-13 09:50:56 +08:00
rain 00d4af2918 test: define G01 v5 structural alignment 2026-07-13 09:39:16 +08:00
rain b078d70b7f docs: plan G01 v5 structural alignment 2026-07-13 09:35:49 +08:00
rain 5b828eb0fd docs: revise G01 visual alignment scope 2026-07-13 09:24:54 +08:00
rain 85ee447040 docs: define G01 v5 visual alignment 2026-07-13 09:19:28 +08:00
124 changed files with 4294 additions and 871 deletions
+11 -10
View File
@@ -1,3 +1,4 @@
<!-- 公共组件根页面底部导航仅维护家谱家族我的三项已确认入口及其透明图标 -->
<template> <template>
<view class="app-tabbar"> <view class="app-tabbar">
<view v-for="item in items" :key="item.key" class="tab-item" @click="switchTab(item)"> <view v-for="item in items" :key="item.key" class="tab-item" @click="switchTab(item)">
@@ -11,9 +12,9 @@
const props = defineProps({ active: { type: String, required: true } }) const props = defineProps({ active: { type: String, required: true } })
const items = [ const items = [
{ key: 'genealogy', label: '家谱', path: '/pages/genealogy/index', icon: '/static/assets/icons/tab/genealogy-v4.png', activeIcon: '/static/assets/icons/tab/genealogy-active-v4.png' }, { key: 'genealogy', label: '家谱', path: '/pages/genealogy/g01-my-genealogies', icon: '/static/assets/foundation/transparent/tab-genealogy.png', activeIcon: '/static/assets/foundation/transparent/tab-genealogy-active.png' },
{ key: 'family', label: '家族', path: '/pages/family/index', icon: '/static/assets/icons/tab/family-v4.png', activeIcon: '/static/assets/icons/tab/family-active-v4.png' }, { key: 'family', label: '家族', path: '/pages/family/f01-family-feed', icon: '/static/assets/foundation/transparent/tab-family.png', activeIcon: '/static/assets/foundation/transparent/tab-family-active.png' },
{ key: 'profile', label: '我的', path: '/pages/profile/index', icon: '/static/assets/icons/tab/profile-v4.png', activeIcon: '/static/assets/icons/tab/profile-active-v4.png' } { key: 'profile', label: '我的', path: '/pages/profile/m01-profile-home', icon: '/static/assets/foundation/transparent/tab-profile.png', activeIcon: '/static/assets/foundation/transparent/tab-profile-active.png' }
] ]
const switchTab = (item) => { const switchTab = (item) => {
@@ -29,7 +30,7 @@ const switchTab = (item) => {
left: 0; left: 0;
z-index: 20; z-index: 20;
display: flex; display: flex;
height: 128rpx; height: 112rpx;
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
box-sizing: content-box; box-sizing: content-box;
border-top: 1rpx solid $gold-soft; border-top: 1rpx solid $gold-soft;
@@ -38,24 +39,24 @@ const switchTab = (item) => {
.tab-item { .tab-item {
display: flex; display: flex;
min-height: 96rpx; min-height: 88rpx;
flex: 1; flex: 1;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 10rpx 0 8rpx; padding: 4rpx 0;
box-sizing: border-box; box-sizing: border-box;
} }
.tab-icon { .tab-icon {
width: 56rpx; width: 64rpx;
height: 56rpx; height: 64rpx;
margin-bottom: 5rpx; margin-bottom: 2rpx;
} }
.tab-label { .tab-label {
color: $ink-muted; color: $ink-muted;
font-size: 28rpx; font-size: 30rpx;
letter-spacing: 2rpx; letter-spacing: 2rpx;
line-height: 1.2; line-height: 1.2;
} }
+174 -46
View File
@@ -1,23 +1,54 @@
<!-- 公共组件G01 已创建已加入家谱的题签列表项保持透明题签框叠在纸纹底图上 -->
<template> <template>
<view class="genealogy-card" :class="{ 'genealogy-card--current': selected }" @click="$emit('select', genealogy)"> <view
<image class="row-frame" src="/static/assets/backgrounds/genealogy-list-slip-frame.png" mode="scaleToFill" /> class="genealogy-card"
:class="{ 'genealogy-card--current': selected }"
@click="$emit('select', genealogy)"
>
<image
class="row-frame"
src="/static/assets/modules/genealogy/transparent/list-slip-frame.png"
mode="scaleToFill"
/>
<view class="surname-seal"> <view class="surname-seal">
<image class="surname-seal-frame" src="/static/assets/icons/genealogy/seal-row-frame-v1.png" mode="scaleToFill" /> <image
class="surname-seal-frame"
src="/static/assets/modules/genealogy/transparent/row-seal-frame.png"
mode="scaleToFill"
/>
<view class="surname-seal-copy"> <view class="surname-seal-copy">
<text class="seal-title">家谱</text> <text class="seal-title">家谱</text>
</view> </view>
</view> </view>
<view class="card-copy"> <view class="card-main">
<text class="card-name">{{ genealogy.name }}</text> <view class="card-title-row">
<text class="card-name">{{ genealogy.name }}</text>
<view class="card-side">
<text class="card-role">{{ role }}</text>
<image
class="card-chevron"
src="/static/assets/foundation/transparent/chevron-right.png"
mode="aspectFit"
/>
</view>
</view>
<view class="card-detail-row"> <view class="card-detail-row">
<view class="card-metas"> <view class="card-metas">
<view class="card-meta-item"> <view class="card-meta-item">
<image class="card-meta-icon" src="/static/assets/icons/common/location-v1.png" mode="aspectFit" /> <image
class="card-meta-icon"
src="/static/assets/foundation/transparent/meta-location.png"
mode="aspectFit"
/>
<text class="card-meta">{{ genealogy.location }}</text> <text class="card-meta">{{ genealogy.location }}</text>
</view> </view>
<view class="card-meta-item"> <view class="card-meta-item">
<image class="card-meta-icon" src="/static/assets/icons/common/member-meta-v1.png" mode="aspectFit" /> <image
class="card-meta-icon"
src="/static/assets/foundation/transparent/meta-member.png"
mode="aspectFit"
/>
<text class="card-meta">{{ genealogy.memberCount }} 位成员</text> <text class="card-meta">{{ genealogy.memberCount }} 位成员</text>
</view> </view>
</view> </view>
@@ -26,11 +57,6 @@
</view> </view>
</view> </view>
</view> </view>
<view class="card-side">
<text class="card-role">{{ role }}</text>
<image class="card-chevron" src="/static/assets/icons/common/chevron-right-v2.png" mode="aspectFit" />
</view>
</view> </view>
</template> </template>
@@ -38,23 +64,25 @@
defineProps({ defineProps({
genealogy: { type: Object, required: true }, genealogy: { type: Object, required: true },
role: { type: String, required: true }, role: { type: String, required: true },
selected: { type: Boolean, default: false } selected: { type: Boolean, default: false },
}) });
defineEmits(['select']) defineEmits(["select"]);
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.genealogy-card { .genealogy-card {
position: relative; position: relative;
display: flex; display: flex;
min-height: 160rpx; min-height: 156rpx;
align-items: center; align-items: center;
padding: 20rpx 24rpx; padding: 18rpx 24rpx;
box-sizing: border-box; box-sizing: border-box;
background: transparent; background: transparent;
} }
.genealogy-card--current { background: transparent; } .genealogy-card--current {
background: transparent;
}
.row-frame { .row-frame {
position: absolute; position: absolute;
@@ -72,8 +100,8 @@ defineEmits(['select'])
position: relative; position: relative;
z-index: 1; z-index: 1;
display: flex; display: flex;
width: 76rpx; width: 72rpx;
height: 118rpx; height: 112rpx;
flex: 0 0 auto; flex: 0 0 auto;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -81,13 +109,41 @@ defineEmits(['select'])
color: #fff5df; color: #fff5df;
} }
.surname-seal-frame { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 0; width: 100%; height: 100%; } .surname-seal-frame {
.surname-seal-copy { position: relative; z-index: 1; display: flex; height: 82rpx; align-items: center; justify-content: center; } position: absolute;
.seal-title { color: #fff5df; font-family: 'STKaiti', 'KaiTi', serif; font-size: 30rpx; font-weight: 700; letter-spacing: 2rpx; line-height: 1; writing-mode: vertical-rl; } top: 0;
.genealogy-card:not(.genealogy-card--current) .surname-seal-frame { opacity: .32; } right: 0;
.genealogy-card:not(.genealogy-card--current) .seal-title { color: $ink-muted; } bottom: 0;
left: 0;
z-index: 0;
width: 100%;
height: 100%;
}
.surname-seal-copy {
position: relative;
z-index: 1;
display: flex;
height: 82rpx;
align-items: center;
justify-content: center;
}
.seal-title {
color: #fff5df;
font-family: "STKaiti", "KaiTi", serif;
font-size: 30rpx;
font-weight: 700;
letter-spacing: 2rpx;
line-height: 1;
writing-mode: vertical-rl;
}
.genealogy-card:not(.genealogy-card--current) .surname-seal-frame {
opacity: 0.32;
}
.genealogy-card:not(.genealogy-card--current) .seal-title {
color: $ink-muted;
}
.card-copy { .card-main {
position: relative; position: relative;
z-index: 1; z-index: 1;
display: flex; display: flex;
@@ -96,43 +152,115 @@ defineEmits(['select'])
flex-direction: column; flex-direction: column;
} }
.card-name, .card-name {
.card-meta {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.card-name { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 37rpx; font-weight: 700; } .card-meta {
.card-detail-row { display: flex; min-width: 0; align-items: center; justify-content: space-between; margin-top: 12rpx; } white-space: nowrap;
.card-metas { display: flex; min-width: 0; align-items: center; } }
.card-meta-item { display: flex; min-width: 0; align-items: center; margin-right: 14rpx; }
.card-meta-icon { width: 24rpx; height: 24rpx; margin-right: 5rpx; } .card-title-row {
.card-meta { color: $ink-muted; font-size: 22rpx; } display: flex;
.card-updated { display: flex; flex: 0 0 auto; align-items: center; margin-left: 10rpx; color: $ink-muted; font-size: 20rpx; white-space: nowrap; } min-width: 0;
align-items: center;
justify-content: space-between;
}
.card-name {
color: $ink;
font-family: "STKaiti", "KaiTi", serif;
font-size: 37rpx;
font-weight: 700;
}
.card-detail-row {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
margin-top: 12rpx;
}
.card-metas {
display: flex;
min-width: 0;
align-items: center;
}
.card-meta-item {
display: flex;
min-width: 0;
align-items: center;
margin-right: 14rpx;
}
.card-meta-icon {
width: 28rpx;
height: 28rpx;
margin-right: 6rpx;
}
.card-meta {
color: $ink-muted;
font-size: 26rpx;
}
.card-updated {
display: flex;
flex: 0 0 auto;
align-items: center;
margin-left: 10rpx;
color: $ink-muted;
font-size: 22rpx;
white-space: nowrap;
}
.card-side { .card-side {
position: relative; position: relative;
z-index: 1; z-index: 1;
display: flex; display: flex;
width: 124rpx; min-width: 0;
flex: 0 0 auto; flex: 0 0 auto;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
margin-left: 10rpx; margin-left: 20rpx;
} }
.card-role { color: $ink-muted; font-size: 24rpx; white-space: nowrap; } .card-role {
.card-chevron { width: 34rpx; height: 34rpx; margin-left: 14rpx; } color: $ink-muted;
font-size: 24rpx;
white-space: nowrap;
}
.card-chevron {
width: 34rpx;
height: 34rpx;
margin-left: 14rpx;
}
@media screen and (max-width: 340px) { @media screen and (max-width: 340px) {
.genealogy-card { min-height: 148rpx; padding-right: 18rpx; padding-left: 18rpx; } .genealogy-card {
.surname-seal { width: 68rpx; height: 106rpx; margin-right: 16rpx; } min-height: 148rpx;
.card-name { font-size: 32rpx; } padding-right: 18rpx;
.card-meta { font-size: 20rpx; } padding-left: 18rpx;
.card-updated { display: none; } }
.card-side { width: 92rpx; margin-left: 8rpx; } .surname-seal {
.card-chevron { width: 28rpx; height: 28rpx; margin-left: 8rpx; } width: 66rpx;
height: 100rpx;
margin-right: 16rpx;
}
.card-name {
font-size: 32rpx;
}
.card-meta {
font-size: 22rpx;
}
.card-updated {
display: none;
}
.card-side {
margin-left: 12rpx;
}
.card-chevron {
width: 28rpx;
height: 28rpx;
margin-left: 8rpx;
}
} }
</style> </style>
+18 -16
View File
@@ -1,10 +1,12 @@
<!-- 公共组件根页朱砂页头与普通返回页头根页背景图层由统一基础资产提供 -->
<template> <template>
<view class="page-header" :class="{ 'page-header--root': root }"> <view class="page-header" :class="{ 'page-header--root': root }">
<image v-if="root" class="header-texture" src="/static/assets/backgrounds/header-cinnabar-texture-v2.jpg" mode="scaleToFill" /> <!-- 根页头部使用不透明朱砂底图与透明祠堂线稿两层完整资产 -->
<image v-if="root" class="header-hall" src="/static/assets/backgrounds/header-hall-lineart.png" mode="aspectFit" /> <image v-if="root" class="header-texture" src="/static/assets/foundation/opaque/root-header-cinnabar.jpg" mode="scaleToFill" />
<image v-if="root" class="header-hall" src="/static/assets/foundation/transparent/root-header-hall.png" mode="aspectFit" />
<view class="header-side header-side--left"> <view class="header-side header-side--left">
<view v-if="root" class="header-icon-button" @click="$emit('brand')"> <view v-if="root" class="header-icon-button" @click="$emit('brand')">
<image class="header-logo" src="/static/assets/icons/brand/jiapu-seal-logo-header.png" mode="aspectFit" /> <image class="header-logo" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
</view> </view>
<view v-else class="header-back" @click="goBack">{{ backLabel }}</view> <view v-else class="header-back" @click="goBack">{{ backLabel }}</view>
</view> </view>
@@ -13,7 +15,7 @@
<view class="header-side header-side--right"> <view class="header-side header-side--right">
<view v-if="root" class="header-icon-button header-notice" @click="$emit('notice')"> <view v-if="root" class="header-icon-button header-notice" @click="$emit('notice')">
<image class="header-notice-icon" src="/static/assets/icons/common/notice-v3.png" mode="aspectFit" /> <image class="header-notice-icon" src="/static/assets/foundation/transparent/notice.png" mode="aspectFit" />
<view v-if="unreadCount > 0" class="notice-dot"></view> <view v-if="unreadCount > 0" class="notice-dot"></view>
</view> </view>
<view v-else class="header-action" @click="$emit('action')">{{ action }}</view> <view v-else class="header-action" @click="$emit('action')">{{ action }}</view>
@@ -47,8 +49,8 @@ const goBack = () => uni.navigateBack()
.page-header--root { .page-header--root {
position: relative; position: relative;
z-index: 3; z-index: 3;
height: 184rpx; height: calc(124rpx + var(--status-bar-height, 0px));
padding-top: env(safe-area-inset-top); padding-top: var(--status-bar-height, 0px);
background-color: #b52e22; background-color: #b52e22;
overflow: hidden; overflow: hidden;
} }
@@ -67,12 +69,12 @@ const goBack = () => uni.navigateBack()
.header-hall { .header-hall {
position: absolute; position: absolute;
bottom: -8rpx; bottom: -2rpx;
left: -34rpx; left: -48rpx;
z-index: 1; z-index: 1;
width: 368rpx; width: 476rpx;
height: 147rpx; height: 166rpx;
opacity: .16; opacity: .29;
pointer-events: none; pointer-events: none;
} }
@@ -139,14 +141,14 @@ const goBack = () => uni.navigateBack()
font-family: 'STKaiti', 'KaiTi', serif; font-family: 'STKaiti', 'KaiTi', serif;
font-size: 48rpx; font-size: 48rpx;
letter-spacing: 5rpx; letter-spacing: 5rpx;
transform: translateY(8rpx); transform: translateY(2rpx);
} }
.page-header--root .header-logo { .page-header--root .header-logo {
width: 88rpx; width: 80rpx;
height: 88rpx; height: 80rpx;
transform: translate(-8rpx, -12rpx); transform: translate(-6rpx, -6rpx);
} }
.page-header--root .header-notice { transform: translateY(10rpx); } .page-header--root .header-notice { transform: translateY(4rpx); }
</style> </style>
+18
View File
@@ -0,0 +1,18 @@
# A-01 Runtime Visual QA
- Source visual truth: `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png`
- Target state: `412 × 915`, agreement unchecked.
- Static contract: passed on 2026-07-13.
- Runtime screenshot: current `412 × 915` capture accepted by the user on 2026-07-13 as the A-01 visual baseline.
## Evidence
- The accepted `412 × 915` capture preserves a continuous paper-and-ink background, complete button frames, the intended entry hierarchy and a single-line agreement row.
- The user explicitly accepted this capture as the current A-01 visual baseline; no additional visual tuning is authorized in this phase.
## Accepted scope
- This result accepts A-01's current visual baseline only.
- Existing local behavior remains unchanged: agreement selection enables the A-02 login route; other entries remain local prepared-state prompts.
final result: passed
@@ -0,0 +1,82 @@
# A-01 启动 / 登录引导设计记录
> 状态:A-01 文字注册入口方案、四尺寸静态验收、本地 UI 与 412 × 915 运行时视觉基准已完成并获用户验收;A-02 双标签本地 UI 已完成。协议页去向、A-03 至 A-06 的设计、认证接口与 Android 全尺寸回归验收仍未完成,D2 继续进行中。
>
> 本页设计事实的唯一归档位置为本文件。旧的 `2026-07-13-a01-login-guide-design.md` 与 `2026-07-13-a01-login-guide.md` 仅保留为失效历史,不可作为实现或推进下一页的依据。
## 1. 已归档的美术锚点
- 文件:[`screens/A01-启动登录引导-效果稿-v1-显式协议.png`](screens/A01-启动登录引导-效果稿-v1-显式协议.png)
- 效果稿 v2[`screens/A01-启动登录引导-效果稿-v2-独立注册.png`](screens/A01-启动登录引导-效果稿-v2-独立注册.png),记录此前“四个独立按钮”的尝试;该入口结构已失效,不作当前方案依据。
- 当前效果稿 v3[`screens/A01-启动登录引导-效果稿-v3-文字注册入口.png`](screens/A01-启动登录引导-效果稿-v3-文字注册入口.png),保留第 2 节的入口层级归档;它不再定义运行时像素坐标。
- 当前运行时视觉基准:[`screens/A01-启动登录引导-栅格验收-412x915.png`](screens/A01-启动登录引导-栅格验收-412x915.png),用于 A-01 协议未勾选状态的同尺寸像素比对;`pages/auth/entry.vue` 必须使用真实页面结构还原,不得将整张效果图作为页面内容。
- 用途:保留本轮已确认的“家祠卷轴”欢迎页美术方向,不作为可直接还原的代码布局图,也不作为功能图标资产。
- 画面方向:朱砂家祠红头、暖宣纸主体、古金细线与云纹、墨褐标题、两侧淡墨建筑/竹影与底部山水。登录欢迎页后续必须使用独立的红头/宣纸背景组合,不复用 G01 的列表页大山水底图。
## 2. 已确认的页面结构与交互
- 标题区保留“家谱 / 为家族留存可传承的记忆”,使用美术锚点中的朱砂家祠红头、暖宣纸、古金云纹、墨褐文字与淡墨建筑/竹影层次。
- 操作区只保留两个大按钮和一个文字入口:朱砂主按钮“登录”、古金描边按钮“微信登录”,以及其下方的“还没有账号?注册账号”。“注册账号”仅为朱砂红可点击文字,不再作为独立大按钮。
- 入口顺序:登录为主入口;微信登录为次入口;注册账号为辅助文字入口。
- 已勾选协议后,“登录”前往 `A-02`,且 `A-02` 内固定使用“账号密码登录 / 手机验证码登录”双标签切换,默认显示账号密码登录。微信授权中、取消与失败回跳状态仍归 `A-03`,在 A-03 设计完成前当前入口只提示“微信登录功能准备中”。
- 注册账号入口仍归 `A-04`;忘记密码仍归 `A-05`。这两页完成设计前,当前入口只提示页面准备中,不伪造跳转。
- 协议必须为用户主动勾选:初始未勾选,可分别打开《家谱用户协议》和《隐私政策》。不得使用“登录即表示同意”或任何默认同意文案;未勾选时登录操作应明确提示用户先确认协议。
## 3. 固定布局与状态边界
- 页面采用纵向滚动;`320 × 568` 下两个按钮、注册文字和协议行可以滚动,但任一按钮文字、注册文字、协议行和底部内容不得被系统导航区遮挡。
- A-01 的登录与微信功能图标均需采用独立本地透明 PNG;微信标志只使用官方或许可来源的原色透明 PNG,不自行重绘或改色。账号密码与手机号验证码的表单图标归 A-02 设计时确定。
- A-01 只设计欢迎、入口与协议未勾选提示。账号密码与手机号验证码表单均归 `A-02`,微信授权结果归 `A-03`,注册归 `A-04`,忘记密码归 `A-05`
- A-01 正常态必须展示两个按钮、注册文字入口和未勾选协议;点击任一需要授权的入口但未勾选协议时,仅提示“请先阅读并同意相关协议”,不视为用户已同意。
- 页面根容器宽度必须为 100%,不可写死为 `750rpx` 或使用整张效果图。固定设计画布仍以 `750rpx` 为测量基准,运行时由真实 DOM 按当前屏宽响应式还原。
#### 412 × 915 运行时校准表
| 元素 | 412 × 915 像素基准 | 750rpx 实现基准 |
| --- | --- | --- |
| 朱砂家祠头图 | 高 139px | 高 253rpx |
| 红头谱印 | 比头图顶边低约 38px | `top: 70rpx` |
| 标题区起点 | 红头后约 82px | `padding-top: 150rpx` |
| 标题两侧云纹 | 紧贴标题两侧,不扩张标题组宽度 | 独立透明素材 `auth-title-cloud-v1.png``64rpx × 40rpx`,标题左右各 12rpx |
| 标题分隔饰线 | 宽约 234px,中心为对称小结 | 宽 426rpx;中结使用独立透明素材 `auth-divider-knot-v1.png`,上、下间距独立 |
| 两个按钮 | x=60px,宽约 291px,高约 65px | `margin: 95rpx 110rpx 0`,高 118rpx |
| 两按钮间距 | 约 28px | `margin-top: 51rpx` |
| 按钮内框与内容组 | 内框距外边约 3px,四角有卷草角花;图标与文字整体偏左 | `inset: 6rpx`;四角使用同一张 `auth-button-corner-v1.png` 镜像;内容组 `translateX(-20rpx)` |
| 注册入口 | 微信按钮后约 56px;无左右短线 | `margin-top: 98rpx` |
| 协议行 | 注册入口后约 77px;复选框与全部文案同一水平线 | `margin-top: 141rpx`,字号与行高均为 20rpx / 30rpx,复选框 30rpx`align-items: center``white-space: nowrap`,加安全区 |
- 上表是 A-01 运行时布局数值的唯一所有者。320 × 568 时页面允许纵向滚动,根容器不得以 `overflow: hidden` 截断协议行或系统安全区;底部内边距包含 `env(safe-area-inset-bottom)`
- 仅作静态设计验证,不代表 uni-app 运行时验收;页面实现后仍须在 Android 320 × 568、360 × 640、360 × 800、412 × 915 上重新验收。
| 状态 | 触发 | A-01 可见结果 | 去向 |
| --- | --- | --- | --- |
| 初始未勾选 | 首次进入 | 两个按钮、注册文字与空复选框 | 留在 A-01 |
| 未同意协议 | 点击登录、微信登录或注册账号 | 提示“请先阅读并同意相关协议”,不跳转、不改变勾选状态 | 留在 A-01 |
| 登录已确认协议 | 点击“登录” | 结束 A-01 引导 | A-02,默认账号密码登录标签 |
| 微信已确认协议 | 点击“微信登录” | 提示“微信登录功能准备中” | 留在 A-01,等待 A-03 设计完成 |
| 注册已确认协议 | 点击“注册账号”文字 | 提示“注册页面准备中” | 留在 A-01,等待 A-04 设计完成 |
### A-01 资产清单
| 资产 | 用途 | 来源或生成提示 | 尺寸与验收 |
| --- | --- | --- | --- |
| `docs/design/screens/A01-启动登录引导-效果稿-v3-文字注册入口.png` | A-01 视觉比对依据 | 已归档并由用户确认的 v3 效果稿 | `852 × 1846px` PNG;仅作真实 DOM 页面还原和运行时验收对照,不直接作为页面内容 |
| `static/assets/backgrounds/auth-ancestral-header-v1.png``auth-rice-paper-v1.jpg``auth-ink-scenery-v1.png` | A-01 分层页面背景 | 2026-07-13 内置图像生成 | 当前 A-01 使用;不得复用 G-01 背景 |
| `static/assets/icons/auth/login-outline-v1.png``static/assets/icons/auth/wechat-licensed-v1.png` | A-01 两个按钮图标 | 登录图标复用项目既有资产;微信图标来源与许可见旧记录 | 当前 A-01 使用;微信图标免费使用须在应用“关于/设置”及应用商店描述中保留 Icons8 署名链接 |
| `static/assets/icons/auth/auth-button-corner-v1.png` | 两个入口按钮的四角卷草装饰 | 2026-07-13 根据 412 × 915 视觉基准生成,去绿幕后保存 | `160 × 160px` 透明 PNG;同一张素材镜像至四角 |
| `static/assets/icons/auth/auth-divider-knot-v1.png` | 标题上下金线的中心对称中结 | 2026-07-13 根据 412 × 915 视觉基准生成,去绿幕后保存 | `160 × 96px` 透明 PNG;不得改用长祥云 |
| `static/assets/icons/auth/auth-title-cloud-v1.png` | “家谱”标题左右祥云 | 2026-07-13 根据 412 × 915 视觉基准生成,去绿幕后保存 | `200 × 120px` 透明 PNG;右侧只允许镜像该素材 |
## 4. 静态设计验收与剩余缺口
- 静态设计验收已覆盖 320 × 568、360 × 640、360 × 800、412 × 915;四个尺寸均保持两个大按钮与单行文字注册入口。`412 × 915` 图是 A-01 协议未勾选状态的运行时像素基准;A-01 本地页以真实 DOM 还原其层级和比例,运行时仍须截图对比,不得以整图页面替代。
- 归档文件:[320 × 568](screens/A01-启动登录引导-栅格验收-320x568.png)、[360 × 640](screens/A01-启动登录引导-栅格验收-360x640.png)、[360 × 800](screens/A01-启动登录引导-栅格验收-360x800.png)、[412 × 915](screens/A01-启动登录引导-栅格验收-412x915.png)。
- 此结论只验证设计稿与固定栅格,不替代 uni-app 页面实现后的 Android 运行时验收。
- 2026-07-13 用户已验收当前连续背景、完整按钮边框与单行协议区的 `412 × 915` 运行截图,A-01 当前视觉基准标记为完成;本轮不再授权继续微调视觉细节。
- 页面实现阶段仍须按本记录在 Android 实机或同等运行环境复核四个尺寸的安全区、滚动与文字折行。
- A-01 的协议最终页面去向及 A-03 至 A-06 的设计仍未完成;这些后续范围不影响当前 A-01 视觉基准的验收结论。
## 5. 推进约束
- `A-01``A-02` 的本地 UI 已按本记录及实现合同落地;A-02 的最终效果稿与 Android 运行时验收仍待补齐,不能据此推进 A-03。
- 当前 A-01 视觉基准已完成;后续只在协议页面、认证接口或新页面范围获得明确授权后继续推进。
- 用户已于 2026-07-13 授权落地 A-01 欢迎入口与 A-02 双标签登录页的本地 UI;仍不接接口、不执行真实登录、不改 Android 打包配置。实现细节的唯一所有者见 `docs/superpowers/specs/2026-07-13-a01-a02-static-ui-implementation-design.md`
+52 -248
View File
@@ -1,265 +1,69 @@
# D1安卓视觉规范与页面壳 # D1 安卓视觉地基规范
> 对应《规划》:P-02、P-03、P-04 > 状态:P-00 实施中。
> 适用范围:Android 手机端全部页面 > 本文件是 Android 页面视觉、资产与公共组件的唯一施工规范;与历史设计记录冲突时,以本文件和用户最新确认优先。
> 设计锚点:用户提供的“汤氏家谱世系图”参考图
## 1. 设计锚点与不可偏离项 ## 1. 冻结视觉锚点
参考图不是只借用“红色”,而是整套视觉语言的依据。后续每一页必须具备以下共性: - A01 启动/登录引导:`screens/A01-启动登录引导-栅格验收-412x915.png`
- G01 我的家谱:`screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`
1. **家祠红头图**:核心家谱页面使用朱砂红顶部,左侧为低对比古建筑线描,标题使用金色或暖白色 两页已获用户验收。P-00 可以重整文件、组件和资产路径,但在 412 × 915 下不得改变两页可见布局、色彩、纹样、按钮或背景组合
2. **宣纸正文区**:主内容始终落在暖白纸面上,背景只允许极浅的山峦、竹影、亭台或纸纤维纹理,不能影响文字阅读。
3. **墨字与古金线**:正文使用墨褐色,分隔、牌位、题签和次级结构使用古金细线。
4. **礼制化状态**:当前选中、重要操作与未读提醒使用朱砂;不可用荧光色、大面积渐变或通用蓝色按钮替代。
5. **牌位不是卡片**:世系树、人物录核心信息使用竖式牌位;其他列表保留纸签和金线的秩序感,不能变成白底大圆角信息卡。
### 1.1 一比一还原的验收定义 ## 2. 视觉语言
“一比一”指以参考图为唯一视觉锚点,不能只借用红色后另行发挥。以 `360 × 800dp` 设计画布验收时,必须同时还原:朱砂红头的高度与留白、祠堂线描的位置和低对比度、金色标题比例、宣纸与山水底纹、古金牌位线框、朱砂选中描边、纸面底部抽屉与底部导航的层级效果。 | 元素 | 规则 |
不同业务页的内容结构可以不同,但不能把世系图页面原样复制到每个页面;它们必须在同一套材质、颜色、字体、线框和动效逻辑中变化。为适配 `320 × 568` 等小屏,元素可流式换行或缩放,**不得**为追求固定像素而造成重叠或不可点击。
每张后续设计稿都要和参考图并排核对上述八项;没有通过核对,不能标为设计完成。
## 2. Android 基础尺寸
设计坐标以 `750rpx` 宽度为基准。实现时允许流式收缩,但不得缩小关键点击区。
| 元素 | 设计值 | 小屏规则 |
| --- | ---: | --- |
| 页面左右安全边距 | `32rpx` | `320px` 小屏可缩至 `24rpx`。 |
| 页面标题 | `34rpx` | 不换成两行;长标题使用省略号。 |
| 主标题 | `48rpx` | 家谱名最大两行,超出截断。 |
| 正文 | `28rpx` | 不低于 `24rpx`。 |
| 辅助文字 | `24rpx` | 仅用于时间、状态、说明。 |
| 主按钮高度 | `88rpx` | 最小点击高度,不因小屏减小。 |
| 图标触控框 | `88rpx × 88rpx` | 图标视觉大小可为 `4048rpx`。 |
| 列表行高度 | `104rpx` 起 | 多行信息自动增高。 |
| 底部操作栏 | `128rpx` 起 | 加系统导航保底内边距。 |
## 3. 色彩、字体与图标
### 3.1 色彩
| Token | 色值 | 使用位置 |
| --- | --- | --- |
| `paper-page` | `#F6F0E5` | 页面整体纸底。 |
| `paper-surface` | `#FFF9EE` | 抽屉、表单、列表承载面。 |
| `cinnabar` | `#A33B2B` | 主操作、选中描边、徽章。 |
| `cinnabar-deep` | `#7E251C` | 顶部红底的层次和按下态。 |
| `ink` | `#342A24` | 标题、主正文。 |
| `ink-muted` | `#7A6958` | 次级正文、时间。 |
| `antique-gold` | `#B58A4B` | 牌位描边、细分割线、装饰。 |
| `gold-soft` | `#E4D4B8` | 弱分割线、未选中轮廓。 |
| `danger` | `#B63A2C` | 删除、审核拒绝;不额外引入鲜红。 |
绿色不进入页面的背景、导航、主按钮或状态色。若用户照片中含绿色,保持原图,不再叠加绿色 UI 元素。
### 3.2 字体
- 页面标题、家谱名、人物姓名:系统衬线回退链 `serif`;仅用于短标题和牌位姓名,体现家谱的书卷感。
- 正文、表单、数字、按钮:Android 系统无衬线字体 `sans-serif`;优先保证低版本设备的字形和行高稳定。
- 不下载网络字体,不依赖单一厂商字体,不用细于常规字重的中文字体。
- 姓名为两至四字时居中竖排;超过四字改为横排或两行,禁止压缩字距造成难读。
### 3.3 透明 PNG 图标规范
- 图标文件一律是 RGBA PNG,四角透明;不把乳白或绿色底色烘焙进图标。
- 功能图标以官方 `24dp × 24dp` 视口为唯一留白标准,导出为 `96 × 96px`(4 倍)透明 PNG;不得再从大幅生成图中裁出图标、不得人为叠加 `25%` 以上的额外空白。图标的边距只保留官方图形本身的标准边距。
- 正常态使用墨褐或古金单色;选中态使用朱砂;不使用渐变、投影或多色填充。
- 图标命名采用 `功能-状态.png`,例如 `tree-active.png``notice-default.png``profile-active.png`
- 首批需要的图标:返回、搜索、更多、通知、家谱、家族、我的、世系图、列表、编辑、新增、相册、礼仪、消息、设置、关闭。
### 3.4 资产优先规则
- 所有具备明确动作或状态的元素必须使用透明 PNG 图标:导航、返回、搜索、通知、更多、关闭、编辑、新增、上传、相册、成员、世系、礼仪、设置、删除、分享、点赞、评论。
- 禁止使用 Emoji、Unicode 符号、临时文字(例如“返回”“更多”)替代功能图标;功能图标旁的文字仅用于解释,不承担唯一识别职责。
- 文字只保留在用户必须阅读或选择的内容上:页面标题、家谱名称、成员资料、表单标签、状态说明与按钮动作。
- 每一个图标必须有默认态与需要时的选中态;选中态仅改变为朱砂色,不改变图形轮廓,保证低端 Android 设备上也能一眼识别。
- **后续强制规则**:任何后续页面设计中新增加的图标,必须同步生成对应的 RGBA 透明 PNG,保存到项目 `static/assets/icons/` 后才能将该页面标记为“设计完成”。设计图中的图形、文字占位、Emoji 或系统默认图标均不能替代实际资产。
- 每个新增图标都要在资产清单登记:用途、文件路径、默认 / 选中状态、设计页编号和透明背景检查结果;页面实现只能引用清单中的已验收资产。
### 3.4.1 功能图标的标准骨架与视觉风格
- Android / Material 的在线规范用于锁定**图标语义、24dp 视口、触控区、默认 / 选中状态和可识别性**,不是要求本项目的每个图标都长成默认黑色 Material 图标。
- 项目 Logo、祠堂纹样、宣纸、山水竹影属于品牌与背景插画,可以定制生成;顶部和通用操作图标允许采用本项目的“古金双线 + 克制云纹收口”风格,风格锚点见 [公共操作图标风格锚点](references/公共操作图标-风格锚点.png)。
- 自定义图标仍须保留标准语义:返回一眼识别为左箭头、搜索为放大镜、通知为铃铛、更多为三点、关闭为 X;不得因装饰而改变动作含义。
- 底部导航固定为三项且每项同时显示图标与短标签:`家谱``家族``我的`。现有 Tab 图标不通过验收,必须重设计为等视觉重量的简洁轮廓;不能出现云纹、枝叶和双层复杂装饰。选中项使用同一图形的朱砂色与填充 / 加粗状态,非选中项用墨褐或古金描边。
- 顶部与通用操作采用标准语义:返回、搜索、通知、更多、关闭、新增、编辑、上传、删除、分享、设置。图标可定制风格,但不以文字、Emoji 或难以识别的自造图形替代。
### 3.4.2 按钮图标风格分级
- **大部分按钮与顶部操作**:返回、搜索、通知、更多、关闭、新增、编辑、上传、删除、分享、设置,以及家谱 / 家族模块中的快捷入口,采用“古金双线 + 轻云纹收口”风格。
- **主操作按钮**:图标可置于“新建家谱、保存、发布、提交”等文字前;图标为古金双线简化版,按钮本身仍以朱砂描边或朱砂实底表达主次,不用纯文字按钮。
- **底部 Tab**:不得直接使用复杂云纹版本;使用更简洁的同色系轮廓,优先保证 24dp 识别度和三个目的地的差异。
- **状态与危险操作**:成功、失败、删除、无权限等不使用花纹干扰信息;保留标准清晰的符号,颜色按状态区分。
> 来源依据:Google Material Symbols 官方库提供适用于 Android 的 PNG / SVG 图标、`FILL` 状态轴和 2048dp 光学尺寸;Android 官方将导航栏用于 3–5 个同等级目的地。项目以这些规则做底线,并在其上保留家祠卷轴的视觉个性。
### 3.5 图标资产清单与路径
所有最终资产均保存在 `static/assets/icons/`,由页面直接引用;不使用在线 URL。
| 资产组 | 文件 | 状态 |
| --- | --- | --- |
| 项目标识 | `brand/jiapu-seal-logo.png` | 自定义生成,待缩小尺寸验收 |
| 顶部操作 | `common/back.png``common/search.png``common/notice.png``common/more.png``common/close.png` | 保留已确认的古金双线风格,待逐个紧凑导出 |
| 底部导航 | `tab/genealogy.png``tab/family.png``tab/profile.png` 及各自 `-active.png` | 待按标准 24dp 语义重绘为简洁同风格版本 |
| 家谱操作 | `genealogy/tree.png``members.png``generation-poem.png``application.png``create.png``edit.png` | 页面设计时按 Material Symbols 官方源导出 |
| 家族内容 | `family/feed.png``article.png``album.png``ceremony.png``gift.png``archive.png` | 页面设计时按 Material Symbols 官方源导出 |
| 通用操作 | `action/add.png``upload.png``delete.png``share.png``like.png``comment.png``setting.png` | 待按古金双线风格逐个紧凑导出 |
### 3.5 已否决的资产(不得引用)
`static/assets/icons/common/``static/assets/icons/tab/``static/assets/icons/action/` 当前是从大幅绿色底图裁切的原始试验文件,仍不得直接引用。其问题是**画布和缩放方式**,不是用户已确认的顶部操作图标美术方向。
**根因记录**:这些文件来自带大块绿色空白的 3 × 2 AI 图稿,再按格子裁切;生成图没有稳定的标准视口,导致图标周围出现过多空白、主体大小不一致。顶部操作图标的古金双线与轻云纹风格保留;Tab 图标的复杂枝叶、过多装饰和不统一轮廓不保留。
**新生产方式**:每个图标单独制作并紧凑导出到 `96px` PNG,实际图形占官方 24dp 视口的正常范围;不得再批量盲裁。顶部 / 操作图标以风格锚点为准,Tab 按简洁语义重新制作。每个文件在 `46rpx` 实际显示尺寸检查后才可引用。
### 3.6 功能图标验收
功能图标只有同时满足以下条件,才可标记为“已生成”:
1. 语义、视口与状态符合 Google Material Symbols / Android 规范;若为本项目定制图标,记录其对应的标准语义与风格锚点。
2. 默认 / 选中态为同一语义符号,保持 `24dp` 标准画布与固定留白;不得改变动作含义来冒充选中态。
3. 项目内文件为 RGBA 透明 PNG,四角透明;默认墨褐、选中朱砂;在纸白底和红头上均满足清晰可见。
4.`46rpx` Tab 显示尺寸及 `320 × 568` 小屏中检查可识别性、标签不换行、点击区不小于 `88rpx`
### 3.7 背景资产规则
- 所有装饰性背景都必须生成并保存在项目 `static/assets/backgrounds/`;禁止依赖网络背景图,也不能只用临时 CSS 渐变假装纸纹。
- 背景分层使用:`header-hall-lineart.png` 用于朱砂顶部;`paper-rice-texture.jpg` 用于宣纸底;`footer-mountain-bamboo.png` 用于页面底部的淡山水竹影。页面按需组合,不把它们烘焙成一张全屏超大图。
- 祠堂线描、山水、竹影等叠加背景应使用透明 PNG;宣纸纸纹使用压缩后的 JPG。所有背景必须无文字、无 Logo、无人物、无绿色调。
- 为兼容老款 Android:顶部纹样显示尺寸不超过 `750 × 288rpx`,底部山水显示尺寸不超过 `750 × 360rpx`;背景只在首屏和固定区域加载,不在长列表中重复解码。
- 背景完成验收需检查:`320 × 568` 下不遮挡标题或操作、纸纹不影响文字对比度、图片已压缩到适合移动端的尺寸。
- **页面背景完成条件**:每一个页面必须在其设计记录中写清“红头 / 纸纹 / 山水竹影 / 侧边竹影 / 无”的组合与对应资产路径;没有背景组合的页面不得标记为“设计完成”。背景可以复用基础资产,但不能是未设计的纯色空白页面或临时 CSS 渐变。
| 页面类型 | 必须使用的背景组合 |
| --- | --- | | --- | --- |
| 家谱总览、世系树、成员档案 | 朱砂红头 + 祠堂线描 + 宣纸纸纹;底部或抽屉使用淡山水竹影。 | | 页面基调 | 暖宣纸、朱砂、古金、墨褐、淡墨山水竹影;禁止绿色页面背景。 |
| 家族动态、谱文、相册、礼仪、消息 | 紧凑朱砂题签 + 宣纸纸纹;内容尾部按需叠加竹影或山水。 | | 文字 | 标题与重要文字用楷体语气;正文首先保证 Android 小屏可读性。 |
| 创建、编辑、申请、设置、反馈 | 紧凑题签 + 宣纸纸纹;表单区域保持干净,不让山水压住输入内容。 | | 主操作 | 朱砂完整按钮图片,含自身纹理、边框与四角;不可由 CSS 色块和零散图片拼装。 |
| 登录、注册、找回密码 | 独立生成的红头 / 纸纹欢迎背景;不能直接复用列表页大山水。 | | 次操作 | 纸白完整按钮图片,含自身金边、四角与纹理;不可借用主按钮角标。 |
| 无数据、失败、审核中 | 宣纸纸纹 + 对应低对比卷轴 / 印章背景资产;不能只显示文字。 | | 页面背景 | 使用完整不透明纸纹/页头/面板图片,必要时可叠加透明山水或竹影 PNG;不能使用截图拼接。 |
| 装饰 | 云纹、谱印、分隔纹、图标、边框均为透明 PNG;不使用 CSS/SVG 临时绘制。 |
| 背景资产 | 路径 | 用途 | 状态 | ## 3. 资产分类与目录
| --- | --- | --- | --- |
| 祠堂线描 | `backgrounds/header-hall-lineart.png` | 家谱红头左侧或底部纹样 | 生成中 |
| 宣纸纸纹 | `backgrounds/paper-rice-texture.jpg` | 全局纸面底色 | 未生成 |
| 山水竹影 | `backgrounds/footer-mountain-bamboo.png` | 列表页、抽屉页底部装饰 | 未生成 |
## 4. 公共页面壳 所有最终资产放在 `static/assets/`,路径必须表达所属范围、背景属性和语义:
### 4.1 核心家谱页面壳 ```text
static/assets/
foundation/
opaque/ # 自带底色/纹理:纸纹、页头、完整按钮、卡片、导航
transparent/ # 透明 PNG:Logo、图标、云纹、边框、分隔纹
modules/
genealogy/
opaque/
transparent/
tree/
opaque/
transparent/
family/
opaque/
transparent/
```
适用于:家谱总览、世系图、成员目录、人物档案 - `opaque` 资产不能依赖页面 CSS 背景色才显示完整外观
- `transparent` 资产必须是 RGBA PNG;其使用位置要在资产清单中写明宿主不透明底图。
- 每项资产都在 P00 清单中登记用途、显示尺寸、引用组件、引用页面和保留/删除状态。
- 不再创建长期使用的 `-v1``-v2``-source``-copy` 文件;同一语义只保留一个已验收版本。
- 顶部 `256288rpx`:朱砂红,左下放置低对比祠堂屋檐线描,禁止使用满屏建筑照片。 ## 4. 页面与组件
- 根页面左上角放项目 Logo(祠堂屋檐 + “谱”字印章,透明 PNG,视觉 `40rpx`、触控区 `88rpx`);详情页该位置优先保留返回按钮,Logo 收入建筑纹样中的小印记。
- 搜索、通知、更多均为 `88rpx` 触控框;图标为暖白或金色线稿 PNG。通知铃铛仅在未读消息数大于 0 时显示朱砂小圆点;未读为 0 时不显示圆点和数字。
- 家谱名称位于视觉中心,最大两行;名称下可放地域或代系提示,但不能与图标重叠。
- 红头结束后立即进入纸面 Tab/分段导航,使用古金底线标记当前项。
### 4.2 普通列表与详情页面壳 - 页面文件:`<页面编号>-<语义名>.vue`;没有确认页面编号的临时骨架要标明 `skeleton`,不能伪装成完成页面。
- 公共组件只收纳已被两个或以上确认页面使用的稳定结构:页面纸纹壳、顶部栏、底部导航、完整图片按钮、完整图片卡片、状态容器。
- 页面与组件开头写中文用途注释;模板按背景、页头、主体、操作、状态分段;资产图层和复杂交互写中文约束说明。
- CSS 只负责布局、尺寸、文字、可见/隐藏与交互状态;不能再用 CSS 绘制边角、金线、云纹、分隔结、纸纹、按钮或卡片外观。
适用于:家族动态、谱文、相册、人物录、礼仪、消息、我的。 ## 5. Android 约束
- 顶部使用 `144176rpx` 紧凑朱砂题签,保留建筑线描的局部纹样 - 核验尺寸:320 × 568、360 × 640、360 × 800、412 × 915
- 内容区采用纸白承载面与古金细分割线,列表项不使用夸张圆角;圆角最大 `16rpx` - 正文不小于 24rpx;主操作最小高度 88rpx;可点击区不小于约 44dp
- 每页只保留一个视觉主操作:新增、发布、保存或提交。它固定在底部操作栏或右下角,不能同时出现两个醒目的红按钮 - 使用 Flex、边距和基础布局;不依赖 CSS Grid、`flex gap`、复杂滤镜、大面积动画或全屏截图背景
- 顶部和底部预留状态栏、三键/手势导航安全区;固定底栏不得遮挡页面操作。
### 4.3 表单与编辑页面壳 ## 6. 验收和代码纪律
适用于:创建家谱、录入始祖、申请加入、发布内容、编辑成员、反馈、设置 - 资产先验收,页面后验收;资产不通过不进入页面
- 所有页面只实现明确设计与交互;不增加推测性的 JS、接口、依赖、变量、方法和分支。
- 顶部为紧凑题签,正文使用逐行纸签式表单,标签在左、输入值在右或下一行 - 每次结构迁移必须同时更新页面路径、`pages.json`、跳转、测试和文档;旧路径确认无引用后立即删除
- 必填项用朱砂小印记表示;校验说明用暖灰褐,不使用大面积红色报错框 - 每次完成后执行受影响审计、编译检查与 `git diff --check`A01、G01 还需做截图回归
- 键盘出现时页面可滚动,底部“保存 / 提交”保持可操作。
### 4.4 底部导航与抽屉
- 三 Tab:家谱、家族、我的。纸白底、顶部古金细线;当前图标和文字用朱砂,未选中用墨褐。
- 无底部 Tab 的详情页,底部保留返回区域或单主操作栏,不能出现空白黑条。
- 成员、相册、礼仪的详情采用半屏至全屏底部抽屉:纸白底、顶部金色短握条、右上关闭按钮、淡竹影或山影作为低对比装饰。
## 5. 核心组件规则
### 5.1 世系人物牌位
- 牌位为暖白底、古金双线边框、上下折角;不使用普通矩形卡片。
- 始祖牌位比同层成员高一级;当前选中人物外框换成朱砂双线,并使用小圆点连接标识。
- 牌内信息顺序:姓名 → 字 / 号 → 生卒或年份;信息不足时显示“生卒不详”,不留大段空白。
- 代际标签固定在树左侧,不随横向滚动丢失;横向树区域与底部档案抽屉互不遮挡。
### 5.2 纸签列表
- 左侧为功能图标或缩略图,中间为标题和两行信息,右侧是状态或箭头。
- 每行以古金细线分隔;红色仅用于“待审核”“未读”“进行中”等需要注意的状态。
- 长标题最多两行;日期、人数、地域等元信息单独一行,不与操作按钮抢空间。
### 5.3 主操作与次操作
- 主操作:朱砂描边或朱砂实底,圆角 `44rpx` 的胶囊形;文字白色或纸白。
- 次操作:纸白底、古金描边、墨褐文字。
- 删除和退出:先显示纸面确认抽屉,再允许使用 `danger` 色;不可直接用红色文字替代确认流程。
### 5.4 空状态与异常
- 空状态使用纸纹中的淡印章、卷轴、山影或未展开牌位,不使用卡通人物。
- 网络失败使用“纸页未送达”图标、明确重试按钮;不把错误码直接展示给用户。
- 审核中使用朱砂印章和预计提示;无权限使用合拢卷轴与申请/返回入口。
## 6. 首屏结构:家谱首页(G-01)
G-01 是所有设计页的公共还原样板,不等同于最终代码。
1. 顶部:朱砂祠堂红头,左侧项目 Logo,中央为“我的家谱”,右侧通知 PNG 图标;只有未读消息时才带朱砂小圆点。
2. 主区:当前家谱以横向家谱题签展示名称、地域、成员数和身份;这个题签的朱砂印章代表“当前选中的家谱”,不是另一种家谱颜色。没有家谱时替换为卷轴空状态。
3. 快捷区:世系图、成员、字辈诗、申请审核四项,使用透明 PNG 图标与古金分隔。
4. 家谱列表:明确分为“我创建的”和“我加入的”两个标题区;每项使用同一套纸签和古金线,不用红色 / 金色印章暗示未说明的身份差异。当前选中项可用朱砂细边标记。
5. 底部:三 Tab;主操作“创建家谱”在内容末尾或空状态中出现,避免遮挡 Tab。
### 6.1 G-01 数据文案规则
- 家谱名称后不得出现无意义横线、破折号、模拟字符或“待补充”占位。
- 有数据时按真实字段显示,例如“河南省 · 洛阳市”“158 位成员”“管理员”;无数据时整行隐藏,或显示明确的“地区待完善 / 成员待录入”。
- “当前家谱”与下方列表不得无说明地重复同一名称;当前题签显示选中的家谱,下方列表用于切换,选中项需要清晰标记。
### 6.2 G-01 固定设计栅格(360 × 800dp
这是后续页面还原的硬尺寸基准,不再交给整页 AI 图自行猜测:
| 区域 | 尺寸 / 间距 | 约束 |
| --- | --- | --- |
| 系统状态栏 | `24dp` | 只预留系统空间,不放页面内容。 |
| 家祠红头 | `112dp`(含标题区) | Logo 左 `16dp`,标题居中,铃铛右 `16dp`;红头结束后不得出现大块空白。 |
| 当前家谱题签 | 顶距 `16dp`,高 `136dp`,左右 `16dp` | 只展示当前家谱一次;名称、成员数、地域、身份均为真实字段。 |
| 快捷入口 | 题签后 `16dp`,行高 `84dp` | 四项等宽;图标实际 `24dp`,标签 `14sp`,不使用大号装饰图。 |
| 分隔与列表标题 | 上距 `16dp`,标题区 `36dp` | 细古金线 + “我创建的 / 我加入的”,不能留无内容的整屏空白。 |
| 家谱列表项 | 每项 `72dp`,项间 `8dp` | 一项只显示名称、必要元信息、右箭头;不重复当前题签内容。 |
| 新建家谱 | 列表后 `16dp`,高 `48dp` | 朱砂描边胶囊按钮,不用巨大的上下留白烘托。 |
| 底部导航 | `64dp` + 系统导航保底 | 纸白底、三项等宽,图标 `24dp`、标签 `12sp`、每项触控区至少 `48dp`。 |
上述内容总高度在 `360 × 800dp` 首屏内保持紧凑;数据较多时由内容区滚动,而不是拉大首屏留白。`320 × 568` 下题签和快捷入口仍在首屏可见,列表向下滚动。
### 6.3 已否决的 G-01 生成整页图
此前 AI 生成的“我的家谱”整页图**保留为美术锚点**:[G01 我的家谱美术锚点](references/G01-我的家谱-美术锚点.png)。它明确了本项目应该保留的整体效果:朱砂祠堂红头、暖金标题、宣纸纸纹、金线题签、淡山水竹影、纸白底部导航和庄重的色彩层级。
该图**不直接作为尺寸与功能验收稿**,因此不计入 G-01 的“页面设计完成”。需要按 6.2 的固定栅格修正:红头后与题签之间的过大留白、快捷入口与列表之间的过大留白、缺失的左上 Logo、当前家谱与列表的重复、无意义横线占位、以及非标准来源图标。
从此以后,页面设计采用“双锚点”:本图锁定美术氛围和视觉效果;6.2 固定栅格锁定尺寸、内容密度与 Android 小屏可用性。AI 只可生成 Logo、背景等独立视觉资产,不能决定页面间距、文字排版、列表高度或交互层级。
### 6.4 标准来源验收备注
- Material Symbols 官方指南:<https://developers.google.com/fonts/docs/material_symbols>
- Android Navigation Bar 官方指南:<https://developer.android.com/develop/ui/compose/components/navigation-bar>
## 7. 设计状态记录
- P-02:完成。颜色、字体、尺寸、透明 PNG 图标规范已锁定。
- P-03:完成。核心、普通、表单三类页面壳及底部导航、抽屉已锁定。
- P-04:完成。加载、空数据、失败、无权限、审核中与确认操作规则已锁定。
- 下一阶段:D2 账户与首次使用页面;全部页面均按本规范出高保真稿后,才进入 uni-app 还原。
+30 -2
View File
@@ -1,12 +1,12 @@
# G-01:我的家谱设计记录 # G-01:我的家谱设计记录
> 状态:资产准备中 > 状态:设计验收完成(2026-07-13,用户确认)
> 美术锚点:[G01 我的家谱美术锚点](references/G01-我的家谱-美术锚点.png) > 美术锚点:[G01 我的家谱美术锚点](references/G01-我的家谱-美术锚点.png)
> 布局锚点:[D1 安卓视觉规范与页面壳](D1_安卓视觉规范与页面壳.md#62-g-01-固定设计栅格360--800dp) > 布局锚点:[D1 安卓视觉规范与页面壳](D1_安卓视觉规范与页面壳.md#62-g-01-固定设计栅格360--800dp)
## 页面目标 ## 页面目标
让已登录用户一眼查看当前家谱、进入四个高频入口、切换已创建 / 已加入家谱,并在无家谱创建或搜索加入。 让已登录用户一眼查看当前家谱、进入四个高频入口、切换已创建 / 已加入家谱无家谱创建或搜索加入状态归属 `G-02`
## 背景组合 ## 背景组合
@@ -104,3 +104,31 @@
- 当前题签恢复标题与元信息之间的细金线;短字段确保 430px 验收宽度内三组元信息同一行显示。 - 当前题签恢复标题与元信息之间的细金线;短字段确保 430px 验收宽度内三组元信息同一行显示。
- 列表谱印固定为竖排“家谱”;右侧角色与箭头改为同一水平基线;“更新于”保留为文本,移除参考稿中不存在的日历图标和对应闲置资产。 - 列表谱印固定为竖排“家谱”;右侧角色与箭头改为同一水平基线;“更新于”保留为文本,移除参考稿中不存在的日历图标和对应闲置资产。
- 新增字段/基线契约先失败后通过。完整验证再次通过:G-01 视觉契约、PNG 透明角审计、编译审计、SCSS 注入审计、Vue 3 入口审计。仍待用户提供 HBuilderX 新截图完成视觉验收。 - 新增字段/基线契约先失败后通过。完整验证再次通过:G-01 视觉契约、PNG 透明角审计、编译审计、SCSS 注入审计、Vue 3 入口审计。仍待用户提供 HBuilderX 新截图完成视觉验收。
## 2026-07-13 第七轮结构、屋檐与祥云校正
- 当前家谱题签由“谱印 + 右侧文案列”改为两层结构:上层为谱印和名称,下层为金线分隔后的地点、成员数、管理员三组元信息;三组元信息不再受谱印列宽挤压。
- 家谱列表由独立的左侧内容列与右侧操作列改为单一主内容区:第一行是标题、角色与箭头,第二行是地点、成员数与“更新于”日期;`320px` 宽度仅隐藏日期,保留其余信息。
- 根页头收窄至 `164rpx`;放大并提高现有祠堂屋檐线稿的可见度,保持其位于 Logo 和标题之后。Tabbar 容器收窄,图标和标签同步提升视觉重量。
- 内容左右边距收至 `32rpx`,快捷入口到列表的无效留白减少;山水竹影扩展至两条列表和新建按钮后方,仍低于所有文字和操作层。
- `action/create-cloud-v1.png` 已由独立透明 PNG `action/create-cloud-v2.png` 替换;v2 使用项目生成的古金线描祥云,四角透明且有效图形审计通过,旧资产已移除。
- 已通过:更新后的 G-01 视觉契约、PNG 透明审计和编译审计。仍待 HBuilderX 的 `360 × 800``320 × 568` 截图与 v5 参考稿并排确认,G-01 不标记为设计完成。
## 2026-07-13 第八轮预览字号校正
- 根据 HBuilderX 预览,列表第二行的地点、成员数与更新时间分别提升至可读字号,元信息图标同步放大;`320px` 继续只隐藏更新时间。
- 新建家谱按钮保留单行文字:缩小云纹和加号的占位宽度,并为按钮文字设置不换行约束,避免“新建家谱”折为两行。
- 已通过更新后的 G-01 视觉契约与编译审计;仍待下一张 HBuilderX 截图复核实际观感。
## 2026-07-13 第九轮顶部状态栏兼容
- 用户确认根页 `124rpx` 的可视朱砂导航高度符合视觉稿;不再扩大其内容区。
- 根页头总高度改为可视导航高度加 `var(--status-bar-height, 0px)`,并以相同变量作为顶部内边距;状态栏不会压缩 Logo、标题和通知入口。
- 宣纸纸纹起点使用同一计算高度,避免状态栏存在时红头结束处出现错层。
- Android App 配置朱砂状态栏背景和浅色系统图标;仍需在有刘海、无刘海与三键导航 Android 真机复核。
## 2026-07-13 最终视觉验收
- 用户已提供 `360 × 800` 正常页、`320 × 568` 顶部与滚动到底部的响应式预览截图:顶部、题签、四个快捷入口、两条列表、新建按钮、山水与 Tabbar 均无重叠;`320 × 568` 下新建按钮可完整滚动至 Tabbar 上方。
- 用户已提供 HBuilderX Web 浏览器中 `Pixel 2 XL` 的常规屏预览:页面内容、山水和 Tabbar 均无裁切。
- 用户于 2026-07-13 明确确认“没事你标记吧可以了”,据此将 G-01 标记为设计验收完成。该确认不等同于 Android 原生包、真机刘海/三键导航或接口联调验收,以上工作仍按总规划暂停。
@@ -0,0 +1,143 @@
# P00 页面结构与资产清单
> 状态:页面与现有资产迁移已完成;A01 完整按钮候选待设计图复核,两页截图回归待办。
> 目的:让页面、路由和资产都有唯一、可读、可审计的归属;本文件是 P-00 的执行清单。
## 1. 页面命名规则
最终页面文件遵循 `<页面编号>-<语义名>.vue`
- 账户:`a01-entry.vue``a02-login.vue`
- 家谱:`g01-my-genealogies.vue``g03-create-genealogy.vue`
- 世系:`t01-tree-overview.vue``t03-member-profile.vue`
- 家族:`f01-family-feed.vue``f02-publish-feed.vue`
- 消息:`n01-message-center.vue`
- 我的:`m01-profile-home.vue`
尚未被全量设计确认、但为了保留现有功能骨架的通用页面,必须在文件名中使用 `skeleton`,不能使用含义不明的 `index.vue``list.vue``detail.vue`
## 2. 当前 15 个路由的迁移映射
| 当前路由 | 当前可确认用途 | 目标文件 | 处理状态 |
| --- | --- | --- | --- |
| `pages/auth/a01-entry` | A01 启动/登录引导 | `pages/auth/a01-entry.vue` | 已迁移;视觉冻结;完整按钮资产待补 |
| `pages/auth/a02-login` | A02 账号登录 | `pages/auth/a02-login.vue` | 已迁移;未验收 |
| `pages/genealogy/g01-my-genealogies` | G01 我的家谱 | `pages/genealogy/g01-my-genealogies.vue` | 已迁移;视觉冻结 |
| `pages/genealogy/g03-create-genealogy` | G03 创建家谱 | `pages/genealogy/g03-create-genealogy.vue` | 已迁移;未验收 |
| `pages/genealogy/g04-first-ancestor` | G04 始祖录入 | `pages/genealogy/g04-first-ancestor.vue` | 已迁移;未验收 |
| `pages/genealogy/g05-genealogy-overview` | G05 家谱总览 | `pages/genealogy/g05-genealogy-overview.vue` | 已迁移;未验收 |
| `pages/genealogy/g06-search-genealogies` | G06 公开家谱搜索 | `pages/genealogy/g06-search-genealogies.vue` | 已迁移;未验收 |
| `pages/genealogy/g10-application-review` | G10 管理员入谱审核 | `pages/genealogy/g10-application-review.vue` | 已迁移;未验收 |
| `pages/tree/t01-tree-overview` | T01 世系树总览 | `pages/tree/t01-tree-overview.vue` | 已迁移;未验收 |
| `pages/tree/t03-member-profile` | T03 成员档案 | `pages/tree/t03-member-profile.vue` | 已迁移;未验收 |
| `pages/family/f01-family-feed` | F01 家族动态 | `pages/family/f01-family-feed.vue` | 已迁移;未验收 |
| `pages/family/f02-publish-feed` | F02 发布动态 | `pages/family/f02-publish-feed.vue` | 已迁移;未验收 |
| `pages/skeleton/content-list-skeleton` | 多类型内容列表功能骨架 | `pages/skeleton/content-list-skeleton.vue` | 已迁移;不能计为设计页 |
| `pages/notification/n01-message-center` | N01 消息中心 | `pages/notification/n01-message-center.vue` | 已迁移;未验收 |
| `pages/profile/m01-profile-home` | M01 我的首页 | `pages/profile/m01-profile-home.vue` | 已迁移;未验收 |
迁移要求:同一轮更新页面文件、`pages.json`、所有跳转路径、测试和中文文件头注释;未完成全部引用更新时,不得删除旧路径。
## 3. 最终资产目录
```text
static/assets/
foundation/
opaque/
page-paper.jpg
auth-page-paper.jpg
auth-header.png
root-header-cinnabar.jpg
transparent/
brand-seal.png
auth-title-cloud.png
auth-divider-knot.png
auth-button-corner.png
...
modules/
genealogy/
opaque/
transparent/
tree/
opaque/
transparent/
family/
opaque/
transparent/
```
当前已迁入的资产以第 4 节台账为准。`auth-primary-button.png``auth-secondary-button.png` 尚未生成:在它们验收并替换 A01 的旧角标拼接实现前,A01 不能按“完整资产规则”关闭地基验收。
## 4. 当前资产台账
以下为当前唯一可用资产路径。尺寸为原始像素;页面中的显示尺寸仍以组件/页面样式为准。
| 路径 | 属性/尺寸 | 用途 | 引用位置 | 状态 |
| --- | --- | --- | --- | --- |
| `foundation/opaque/auth-page-paper.jpg` | opaque / 750×1334 | 认证页纸纹底色 | A01、A02 | 保留 |
| `foundation/opaque/auth-header.png` | opaque / 750×196 | 认证页红色页头 | A01、A02 | 保留 |
| `foundation/opaque/page-paper.jpg` | opaque / 852×1846 | 根页宣纸底图 | G01 | 保留 |
| `foundation/opaque/root-header-cinnabar.jpg` | opaque / 720×184 | 根页朱砂页头 | `PageHeader` | 保留 |
| `foundation/transparent/auth-ink-scenery.png` | transparent / 750×1334 | 认证页淡墨叠层 | A01、A02 | 保留 |
| `foundation/transparent/auth-title-cloud.png` | transparent / 200×120 | A01 标题左右云纹 | A01 | 保留 |
| `foundation/transparent/auth-divider-knot.png` | transparent / 160×96 | A01 文案分隔结 | A01 | 保留 |
| `foundation/transparent/auth-button-corner.png` | transparent / 160×160 | A01 旧按钮角标;待完整按钮替换 | A01 | 过渡保留 |
| `foundation/opaque/a01-primary-button-candidate.png` | opaque / 1983×793 | A01 主按钮完整图片候选;未接入 | 无 | 待设计图复核 |
| `foundation/opaque/a01-secondary-button-candidate.png` | opaque / 1983×793 | A01 次按钮完整图片候选;未接入 | 无 | 待设计图复核 |
| `foundation/transparent/auth-login-outline.png` | transparent / 96×96 | 登录图标 | A01 | 保留 |
| `foundation/transparent/auth-wechat.png` | transparent / 96×96 | 微信图标 | A01 | 保留 |
| `foundation/transparent/brand-seal.png` | transparent / 240×288 | 品牌谱印 | A01、A02、`PageHeader` | 保留 |
| `foundation/transparent/root-header-hall.png` | transparent / 750×300 | 根页祠堂线稿 | `PageHeader` | 保留 |
| `foundation/transparent/footer-mountain-bamboo.png` | transparent / 750×360 | 根页底部山水竹影 | G01 | 保留 |
| `foundation/transparent/notice.png` | transparent / 96×96 | 通知图标 | `PageHeader` | 保留 |
| `foundation/transparent/chevron-right.png` | transparent / 96×96 | 列表右箭头 | `GenealogyCard` | 保留 |
| `foundation/transparent/meta-location.png` | transparent / 96×96 | 地点元信息 | G01、`GenealogyCard` | 保留 |
| `foundation/transparent/meta-member.png` | transparent / 96×96 | 成员元信息 | G01、`GenealogyCard` | 保留 |
| `foundation/transparent/meta-admin.png` | transparent / 96×96 | 管理员元信息 | G01 | 保留 |
| `foundation/transparent/tab-genealogy.png` | transparent / 96×96 | 家谱 Tab 默认态 | `AppTabbar` | 保留 |
| `foundation/transparent/tab-genealogy-active.png` | transparent / 96×96 | 家谱 Tab 选中态 | `AppTabbar` | 保留 |
| `foundation/transparent/tab-family.png` | transparent / 96×96 | 家族 Tab 默认态 | `AppTabbar` | 保留 |
| `foundation/transparent/tab-family-active.png` | transparent / 96×96 | 家族 Tab 选中态 | `AppTabbar` | 保留 |
| `foundation/transparent/tab-profile.png` | transparent / 96×96 | 我的 Tab 默认态 | `AppTabbar` | 保留 |
| `foundation/transparent/tab-profile-active.png` | transparent / 96×96 | 我的 Tab 选中态 | `AppTabbar` | 保留 |
| `modules/genealogy/transparent/current-slip-frame.png` | transparent / 720×272 | 当前家谱题签框 | G01 | 保留 |
| `modules/genealogy/transparent/list-slip-frame.png` | transparent / 720×144 | 家谱列表题签框 | `GenealogyCard` | 保留 |
| `modules/genealogy/transparent/section-divider.png` | transparent / 720×30 | G01 分区金线 | G01 | 保留 |
| `modules/genealogy/transparent/current-seal-frame.png` | transparent / 144×208 | 当前家谱谱印框 | G01 | 保留 |
| `modules/genealogy/transparent/row-seal-frame.png` | transparent / 112×160 | 列表谱印框 | `GenealogyCard` | 保留 |
| `modules/genealogy/transparent/create-cloud.png` | transparent / 192×96 | 新建入口云纹 | G01 | 保留 |
| `modules/genealogy/transparent/add.png` | transparent / 96×96 | 新建入口加号 | G01 | 保留 |
| `modules/genealogy/transparent/shortcut-tree.png` | transparent / 96×96 | 世系图入口 | G01 | 保留 |
| `modules/genealogy/transparent/shortcut-members.png` | transparent / 96×96 | 成员入口 | G01 | 保留 |
| `modules/genealogy/transparent/shortcut-generation-poem.png` | transparent / 96×96 | 字辈诗入口 | G01 | 保留 |
| `modules/genealogy/transparent/shortcut-application.png` | transparent / 96×96 | 审核入口 | G01 | 保留 |
本轮已迁入 35 项在用资产,删除 24 项运行时零引用的旧副本。两张 A01 候选由图像生成工具制作,不是设计图裁切,当前比例约为 2.5:1,尚不符合现有按钮约 3.6:1 的显示比例,严禁直接接入或作为验收通过依据。旧版设计记录中的历史路径不再是资产依据;后续只按本台账引用和维护。
## 5. 图片属性规则
| 类型 | 文件格式与内容 | 典型用途 | 页面使用规则 |
| --- | --- | --- | --- |
| 不透明底图 | JPG 或无透明区域 PNG;包含完整底色、纹理、边框和四角 | 页面纸纹、朱砂页头、完整主/次按钮、卡片、导航 | 直接作为对应区域的完整视觉面,不再靠 CSS 补色或叠加边角 |
| 透明叠加图 | RGBA PNG;四周透明,主体边界干净 | Logo、图标、祥云、谱印、分隔纹、边框 | 明确记录叠在何种不透明底图上;不单独承担底色 |
禁止项:整页截图、设计图局部裁切、半边边框、按钮角标拼接、CSS/SVG 绘制可见装饰、同一语义的多版本长期共存。
## 6. 资产清理登记规则
每项现有资产必须登记以下字段后才可以决定保留或删除:
| 字段 | 含义 |
| --- | --- |
| 当前路径 | 盘点时的真实文件路径 |
| 语义用途 | 它服务的视觉元素,而不是模糊类别 |
| 背景属性 | `opaque``transparent` |
| 代码引用 | 页面、组件、配置或测试中的引用位置 |
| 替代资产 | 若要迁移,写入唯一的新路径;无替代则留空 |
| 决定 | `保留``迁移后删除``无引用删除` |
执行顺序固定为:扫描引用 → 填写登记 → 迁移引用 → 编译与截图验证 → 删除旧资产 → 再次扫描确认零引用。任何一步失败都停止删除。
## 7. 中文注释约定
每个页面和组件文件以中文注释写清页面编号、用途和关键依赖;模板至少区分背景、页头、主体、操作、状态五类区域。资产图层、路由跳转、非直观状态和防回归样式要解释“为什么这样写”。显而易见的单行 CSS 不写噪声注释。
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

@@ -0,0 +1,123 @@
# A-01 / A-02 本地 UI 落地执行计划
> **执行说明:** 当前实现仅覆盖 `A-01 启动登录引导` 与 `A-02 双标签登录页` 的本地 UI 和本地交互;不接后端、不写 token、不跳转家谱页、不改 Android 打包配置。
**目标:** 将已确认的 A-01 / A-02 设计落实为可运行的 uni-app 页面,并把“显式阅读并同意协议”作为进入登录页的唯一前置条件。
**实现合同:** `docs/superpowers/specs/2026-07-13-a01-a02-static-ui-implementation-design.md`
**技术边界:** Vue 3 `<script setup>`、uni-app;仅改路由入口和认证页;仅复用 `static/assets/backgrounds/auth-*``static/assets/icons/auth/*` 中的资源。
---
## 任务 1:先建立 A-01 / A-02 页面合同检查
**文件:**
- 新建:`tests/a01-a02-ui-contract.ps1`
**步骤:**
1. 编写 PowerShell 合同测试,读取 `pages.json``pages/auth/entry.vue``pages/auth/login.vue`
2. 使用 UTF-8 Base64 常量保存中文断言,避免 PowerShell 文件编码影响中文匹配。
3. 断言 `pages/auth/entry` 是第一个页面,A-01 有显式协议勾选、账号登录、微信登录、文字注册入口,A-02 有两个标签、忘记密码和本地待接入反馈。
4. 断言认证页中不再出现 `appApi``calcMD5``loginWithPassword``uni.reLaunch` 和旧的“登录即表示同意”文案。
5. 在页面改造前运行该测试,预期因 A-01 页面尚不存在而失败;不提交红灯状态。
**验证:**
```powershell
& .\tests\a01-a02-ui-contract.ps1
```
预期:失败,指出缺少 `pages/auth/entry.vue`
---
## 任务 2:创建 A-01 启动登录引导页并注册路由
**文件:**
- 修改:`pages.json`
- 新建:`pages/auth/entry.vue`
**步骤:**
1.`pages.json``pages` 数组首位加入 `pages/auth/entry`,保留现有认证页路径和所有其余路由。
2. 建立 `entry.vue`:使用祖祠红色横幅、宣纸和山水背景资源,呈现标题、品牌说明、账号密码登录、微信登录、文字注册入口和协议区域。
3.`agreed` 作为唯一的本地状态。账号登录在勾选后仅执行:
```js
uni.navigateTo({ url: '/pages/auth/login?agreed=1' })
```
4. 所有 A-01 操作在未勾选时统一提示:
```js
uni.showToast({ title: '请先阅读并同意相关协议', icon: 'none' })
```
5. 勾选后,微信登录提示“微信登录功能准备中”,注册入口提示“注册页面准备中”;协议文字本身提示“协议页面准备中”。不创建尚未设计的协议、注册或微信授权页面。
6. 用页面内最小样式匹配已验收的 A-01:窄红色顶部、宣纸主体、红色主按钮、金色描边次按钮、底部山水留白;主内容最大宽度控制为移动端阅读尺寸,并预留安全区。
**验证:**
```powershell
& .\tests\a01-a02-ui-contract.ps1
```
预期:仍失败,仅因 A-02 旧页面含 API / 旧同意逻辑。
---
## 任务 3:将现有 login 页面替换为 A-02 双标签本地 UI
**文件:**
- 修改:`pages/auth/login.vue`
**步骤:**
1. 删除 `appApi``calcMD5`、密码加密、网络请求和登录后 `reLaunch`;不保留兼容分支。
2.`onLoad` 读取 `agreed=1`,并仅以此本地参数决定是否允许提交;缺少该参数时,任何提交均提示“请先返回登录引导确认协议”。
3. 默认显示“账号密码登录”标签,字段为账号/手机号与密码;下方保留“忘记密码”文字入口,点击提示“忘记密码页面准备中”。
4. 增加“手机验证码登录”标签,字段为手机号与验证码,并提供不发请求的“获取验证码”次级按钮,点击提示“获取验证码功能待接入”。
5. 两种登录提交完成本地必填校验后均只提示“登录接口待接入”,不得调用网络、写入本地登录态或跳转业务页。
6. 延续 A-01 视觉语言:同款顶部和纸质背景、红金色标签与主按钮、输入区域清晰分组;不复写协议同意逻辑或出现“登录即表示同意”文案。
**验证:**
```powershell
& .\tests\a01-a02-ui-contract.ps1
```
预期:通过。
---
## 任务 4:做最小可编译验证并更新交接状态
**文件:**
- 修改:`docs/交接记录.md`
- 修改:`docs/规划.md`
**步骤:**
1. 运行认证页合同测试和现有 Vue 编译审计;若项目现有全部 PowerShell 测试可在合理时间内运行,则一并运行。
2. 检查变更只覆盖 A-01/A-02、路由入口、对应合同测试和状态记录;不触碰用户已有的三个脏文件。
3. 在交接记录与规划中明确记录:A-01/A-02 已完成本地 UI,所有认证接口、协议正文、真实微信授权、注册、找回密码仍未接入并等待后续页面设计/接口阶段。
4. 提交仅包含本轮 A-01/A-02 代码、测试和文档的文件。
**验证:**
```powershell
& .\tests\a01-a02-ui-contract.ps1
& .\tests\vue3-compile-audit.ps1
git diff --check
git status --short
```
预期:前两项通过、`git diff --check` 无输出,状态中只出现本轮受管文件及已知用户脏文件。
---
## 自检
- [x] 实现范围只含 A-01、A-02 和它们的入口路由。
- [x] 以实现合同为唯一行为来源,没有引入新认证合同。
- [x] 明确列出红灯、绿灯以及不提交红灯状态的规则。
- [x] 每个任务都给出精确文件和可执行验证命令。
- [x] 无 API、无 token、无伪造完成页、无 Android 配置改动。
@@ -0,0 +1,260 @@
# A-01 启动 / 登录引导设计证据执行计划
> 已失效:本计划基于“四个独立按钮”入口结构。当前方案改为“登录 + 微信登录 + 文字注册入口”,不得继续执行;待设计记录复核后另写新计划。
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to execute this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 完成 A-01 的可归档效果稿、固定栅格、状态设计、本地 PNG 资产和小屏验收;不实现任何 uni-app 页面或接口。
**Architecture:** `docs/design/A01_启动登录引导_设计记录.md` 是唯一需求所有者。整页效果稿只作为美术锚点,真实背景和图标分别存入 `static/assets/backgrounds/``static/assets/icons/`,并由 PowerShell 审计脚本验证归档、文件存在和透明 PNG 条件。
**Tech Stack:** Markdown、PNG/JPG 静态资产、PowerShell 5.1 审计、HBuilderX Web 浏览器预览。
## Global Constraints
- 只做 Android `uni-app` 的页面设计;不改 Vue 页面、接口、真实登录、打包或依赖。
- A-01 的唯一需求所有者是 `docs/design/A01_启动登录引导_设计记录.md`;本计划不可重定义页面规则。
- 账号密码、手机验证码、微信、注册账号四个入口顺序固定;协议默认不勾选,禁止“登录即同意”。
- 登录欢迎页使用独立的红头/宣纸欢迎背景,不复用 G-01 的列表页大山水背景。
- 所有功能图标必须为项目内 `96 × 96px` RGBA 透明 PNG;微信标志必须为本地官方或许可的原色透明 PNG,并记录来源。
- 每个视觉产物先归档到项目并写入设计记录,再展示或进入下一关;全页效果稿不能代替真实图标资产或小屏验收。
---
### Task 1: 建立 A-01 设计归档关口
**Files:**
- Create: `tests/a01-design-record-audit.ps1`
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: `docs/design/A01_启动登录引导_设计记录.md``docs/design/screens/A01-启动登录引导-效果稿-v2-独立注册.png`
- Produces: Exit code `0` only when设计记录包含四个入口、显式协议规则与 v2 效果稿归档路径。
- [ ] **Step 1: 写入会失败的归档审计脚本**
```powershell
function ConvertFrom-Utf8Base64 {
param([string]$Value)
return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Value))
}
$recordPath = ConvertFrom-Utf8Base64 'ZG9jcy9kZXNpZ24vQTAxX+WQr+WKqOeZu+W9leW8leWvvF/orr7orqHorrDlvZUubWQ='
$effect = ConvertFrom-Utf8Base64 'ZG9jcy9kZXNpZ24vc2NyZWVucy9BMDEt5ZCv5Yqo55m75b2V5byV5a+8LeaViOaenOeovy12Mi3ni6znq4vms6jlhowucG5n'
$record = Get-Content -LiteralPath $recordPath -Raw -Encoding utf8
$rules = @(
(ConvertFrom-Utf8Base64 '6LSm5Y+35a+G56CB55m75b2V'),
(ConvertFrom-Utf8Base64 '5omL5py66aqM6K+B56CB55m75b2V'),
(ConvertFrom-Utf8Base64 '5b6u5L+h55m75b2V'),
(ConvertFrom-Utf8Base64 '5rOo5YaM6LSm5Y+3'),
(ConvertFrom-Utf8Base64 '5Yid5aeL5pyq5Yu+6YCJ')
)
foreach ($rule in $rules) {
if ($record -notmatch [regex]::Escape($rule)) {
throw "Missing A01 rule: $rule"
}
}
if (-not (Test-Path -LiteralPath $effect)) {
throw "Missing archived A01 effect: $effect"
}
Write-Output 'A01-DESIGN-RECORD-AUDIT PASS'
```
- [ ] **Step 2: 运行审计并确认因 v2 效果稿不存在而失败**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `Missing archived A01 effect`
- [ ] **Step 3: 将 v2 效果稿路径写入设计记录的已归档美术锚点章节**
```markdown
- 效果稿 v2[`screens/A01-启动登录引导-效果稿-v2-独立注册.png`](screens/A01-启动登录引导-效果稿-v2-独立注册.png),用于确认独立“注册账号”按钮后的完整层级。
```
- [ ] **Step 4: 暂不运行通过态审计**
Run: 不运行;通过态必须在 Task 2 的图稿实际归档后执行。
### Task 2: 生成并归档 A-01 效果稿 v2
**Files:**
- Create: `docs/design/screens/A01-启动登录引导-效果稿-v2-独立注册.png`
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: v1 美术锚点、A-01 四入口结构、D1 的独立登录欢迎背景规则。
- Produces: 仅作为美术锚点的 v2 PNG;不作为 UI 背景或图标来源。
- [ ] **Step 1: 生成竖屏 A-01 效果稿**
生成条件:朱砂祠堂红头与金色“谱”牌居中;暖宣纸主体;标题“家谱”和副句;四个纵向单行按钮,第一项朱砂实心,其余三项纸白古金描边;第四项文字为“注册账号”;底部为未勾选协议;淡墨建筑与竹影只作低对比背景,不遮挡文字。
- [ ] **Step 2: 先将生成文件保存为精确路径,再更新设计记录**
Run: `Get-Item -LiteralPath 'docs\design\screens\A01-启动登录引导-效果稿-v2-独立注册.png' | Select-Object Name,Length`
Expected: 文件存在且 `Length` 大于 `0`
- [ ] **Step 3: 运行归档审计并确认通过**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `A01-DESIGN-RECORD-AUDIT PASS`
- [ ] **Step 4: 人工比对 v2 与 A-01 记录**
检查:四个入口顺序正确;注册按钮独立且不换行;协议未勾选;没有“登录即同意”;没有把 G-01 底部山水直接复制为登录背景。
### Task 3: 固定 Android 栅格与状态说明
**Files:**
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: D1 的 `750rpx` 基准、`32rpx` 安全边距、正文最小 `24rpx`、主按钮最小 `88rpx`
- Produces: A-01 的四种验收尺寸布局表,以及正常态、协议未勾选提示、页面加载三种状态定义。
- [ ] **Step 1: 先在归档审计脚本中加入栅格和状态断言**
```powershell
$requiredDesignText = @('320 × 568', '360 × 640', '360 × 800', '412 × 915', '协议未勾选提示', '页面加载')
foreach ($item in $requiredDesignText) {
if ($record -notmatch [regex]::Escape($item)) {
throw "缺少 A-01 栅格或状态说明:$item"
}
}
```
- [ ] **Step 2: 运行审计并确认因新字段未写入而失败**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `缺少 A-01 栅格或状态说明`
- [ ] **Step 3: 在设计记录新增固定栅格表和状态表**
表格必须为 `320 × 568``360 × 640``360 × 800``412 × 915` 分别写明:红头、标题区、四按钮区、协议区、可滚动区域和底部安全边距。状态表必须说明:正常态、协议未勾选提示、页面加载;微信授权的结果页只链接到 A-03,不在 A-01 重复设计。
- [ ] **Step 4: 运行审计并确认通过**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `A01-DESIGN-RECORD-AUDIT PASS`
### Task 4: 生成并验收本地背景与功能 PNG 资产
**Files:**
- Create: `static/assets/backgrounds/auth-welcome-hall-v1.png`
- Create: `static/assets/backgrounds/auth-welcome-paper-v1.jpg`
- Create: `static/assets/backgrounds/auth-welcome-ink-side-v1.png`
- Create: `static/assets/icons/auth/account-login-v1.png`
- Create: `static/assets/icons/auth/phone-code-login-v1.png`
- Create: `static/assets/icons/auth/register-v1.png`
- Create: `static/assets/icons/brand/wechat-login-v1.png`
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: D1 背景和 PNG 规则。
- Produces: 可由后续 A-01 页面本地引用的独立背景和图标,及其来源/Alpha 验收清单。
- [ ] **Step 1: 将资产存在、尺寸和透明角检查加入审计脚本**
```powershell
$pngAssets = @(
'static/assets/backgrounds/auth-welcome-hall-v1.png',
'static/assets/backgrounds/auth-welcome-ink-side-v1.png',
'static/assets/icons/auth/account-login-v1.png',
'static/assets/icons/auth/phone-code-login-v1.png',
'static/assets/icons/auth/register-v1.png',
'static/assets/icons/brand/wechat-login-v1.png'
)
foreach ($asset in $pngAssets) {
if (-not (Test-Path -LiteralPath $asset)) { throw "缺少 A-01 PNG$asset" }
}
if (-not (Test-Path -LiteralPath 'static/assets/backgrounds/auth-welcome-paper-v1.jpg')) {
throw '缺少 A-01 宣纸背景:auth-welcome-paper-v1.jpg'
}
```
- [ ] **Step 2: 运行审计并确认因资产不存在而失败**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `缺少 A-01 PNG``缺少 A-01 宣纸背景`
- [ ] **Step 3: 创建独立背景与图标资产,并在设计记录登记用途和来源**
规则:自定义背景及账号/手机/注册图标单独生成;微信图标从官方或许可来源获取,不改色、不从截图裁切;账号/手机/注册图标导出 `96 × 96px` RGBA PNG,微信图标保留原色透明背景;设计记录逐项记录路径、用途、来源和透明角检查结果。
- [ ] **Step 4: 扩展审计脚本,检查四个登录入口图标为 `96 × 96px` RGBA 且四角 Alpha 为 0**
```powershell
Add-Type -AssemblyName System.Drawing
$iconPaths = @(
'static/assets/icons/auth/account-login-v1.png',
'static/assets/icons/auth/phone-code-login-v1.png',
'static/assets/icons/auth/register-v1.png',
'static/assets/icons/brand/wechat-login-v1.png'
)
foreach ($iconPath in $iconPaths) {
$bitmap = [System.Drawing.Bitmap]::FromFile((Resolve-Path -LiteralPath $iconPath))
try {
if ($bitmap.Width -ne 96 -or $bitmap.Height -ne 96) { throw "图标尺寸不符:$iconPath" }
foreach ($point in @(@(0,0), @(95,0), @(0,95), @(95,95))) {
if ($bitmap.GetPixel($point[0], $point[1]).A -ne 0) { throw "图标透明角不符:$iconPath" }
}
} finally { $bitmap.Dispose() }
}
```
- [ ] **Step 5: 运行完整审计并确认通过**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `A01-DESIGN-RECORD-AUDIT PASS`
### Task 5: 小屏视觉验收与正式状态更新
**Files:**
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Modify: `docs/规划.md`
- Modify: `docs/交接记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: 已归档 v2、已验收资产、A-01 栅格和状态记录。
- Produces: A-01 的设计完成结论;下一页才可解除冻结并开始设计。
- [ ] **Step 1: 将四个预览尺寸和无底部遮挡检查加入审计脚本**
```powershell
$evidenceLabels = @('320 × 568', '360 × 640', '360 × 800', '412 × 915', '无重叠', '无裁切', '无底部遮挡')
foreach ($label in $evidenceLabels) {
if ($record -notmatch [regex]::Escape($label)) {
throw "缺少 A-01 验收证据:$label"
}
}
```
- [ ] **Step 2: 在 HBuilderX Web 浏览器中逐一预览四种尺寸,并记录截图结论**
验收条件:四个按钮均可见且文字不换行;协议行可到达、默认未选且不被系统导航遮挡;背景不压住文字;点击区不小于 `88rpx`;没有远程图标或 CSS 图形替代。
- [ ] **Step 3: 在设计记录写入截图来源、尺寸、正常/协议未勾选提示状态和结论**
记录必须明确区分 HBuilderX Web 浏览器预览与未来 Android 原生包验证;不得把后者写成已完成。
- [ ] **Step 4: 运行完整审计并确认通过**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1`
Expected: `A01-DESIGN-RECORD-AUDIT PASS`
- [ ] **Step 5: 更新总规划和交接记录,然后提交**
```powershell
git add -- tests/a01-design-record-audit.ps1 docs/design/A01_启动登录引导_设计记录.md docs/design/screens/A01-启动登录引导-效果稿-v2-独立注册.png static/assets/backgrounds/auth-welcome-hall-v1.png static/assets/backgrounds/auth-welcome-paper-v1.jpg static/assets/backgrounds/auth-welcome-ink-side-v1.png static/assets/icons/auth/account-login-v1.png static/assets/icons/auth/phone-code-login-v1.png static/assets/icons/auth/register-v1.png static/assets/icons/brand/wechat-login-v1.png docs/规划.md docs/交接记录.md
git commit -m "docs: complete A01 login guide design"
```
Expected: 仅 A-01 设计证据、资产与状态记录进入提交;不包含 Vue 页面、接口或构建缓存。
@@ -0,0 +1,322 @@
# A-01 启动/登录引导页 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 新增不接接口的 A-01 登录方式选择页,以账号密码为主入口,并要求用户主动勾选协议后才允许进入已设计完成的下一页。
**Architecture:** A-01 使用独立的 `pages/auth/entry.vue`,不改动现有 `pages/auth/login.vue` 的临时密码登录骨架,避免破坏现有核心流程合同。页面状态只包含 `consented` 和点击守卫;视觉由现有本地纸纹、朱砂红头、祠堂线描与山水资产组合,微信图标以本地透明 PNG 引入。A-02/A-03 的最终跳转仅在两页都完成设计并注册路由后接通。
**Tech Stack:** uni-app、Vue 3 `<script setup>`、SCSS、PowerShell 静态合同测试、本地 PNG/JPG 资产。
## Global Constraints
- 仅 Android 手机端视觉和本地交互;不调用 `appApi`、不接真实微信授权、短信服务或后端接口。
- A-01 只能有账号密码、手机号验证码、微信三个入口;不得出现注册、忘记密码、输入框、底部 Tab 或“登录即表示同意”。
- 协议默认未勾选;未勾选点击任一登录入口必须阻止跳转并提示;协议文字分别可点击。
- 账号密码是唯一朱砂实底主按钮;手机号验证码、微信为同层纸白古金描边按钮;微信标识不可改色。
- 只使用项目本地资产;第三方品牌 PNG 必须写入 D1 的来源、用途、路径与透明角检查记录。
- 页面视觉先于接口;在 A-02、A-03 设计与路由完成前,不得把 A-01 接到临时或错误的登录页。
## Execution Gate
本计划的视觉与协议部分可以在 A-01 实施时执行;**实际 `navigateTo` 跳转必须等 A-02、A-03 的页面设计获批、对应路由存在后再执行**。这是《规划.md》“页面全部设计后再处理接口/跨页联动”的硬约束,而不是兼容分支。
---
### Task 1: 先建立 A-01 静态合同测试
**Files:**
- Create: `tests/a01-login-guide-contract.ps1`
- Modify: none
**Interfaces:**
- Consumes: `pages.json`、未来的 `pages/auth/entry.vue``docs/design/D1_安卓视觉规范与页面壳.md`
- Produces: `tests/a01-login-guide-contract.ps1`,输出 `PASS A-01 login guide contract`
- [ ] **Step 1: 写入先失败的合同测试**
```powershell
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json') | ConvertFrom-Json
$paths = @($pages.pages | ForEach-Object { $_.path })
if ($paths[0] -ne 'pages/auth/entry') { throw 'A-01 is not the application entry route.' }
if ('pages/auth/entry' -notin $paths) { throw 'Missing A-01 route.' }
$entry = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/auth/entry.vue')
foreach ($copy in @('账号密码登录', '手机号验证码登录', '微信登录', '我已阅读并同意《用户协议》与《隐私政策》')) {
if (-not $entry.Contains($copy)) { throw "Missing A-01 copy: $copy" }
}
foreach ($forbidden in @('登录即表示同意', '注册账号', '忘记密码', 'appApi', 'calcMD5', '<input')) {
if ($entry.Contains($forbidden)) { throw "Forbidden A-01 content: $forbidden" }
}
if ($entry -notmatch 'const consented = ref\(false\)') { throw 'A-01 consent must default to false.' }
if ($entry -notmatch 'if \(!consented\.value\)') { throw 'A-01 does not block unchecked login.' }
if ($entry -notmatch '请先阅读并同意相关协议') { throw 'A-01 has no unchecked-consent prompt.' }
foreach ($asset in @('header-cinnabar-texture-v2.jpg', 'header-hall-lineart.png', 'paper-rice-texture-v2.jpg', 'footer-mountain-bamboo.png', 'wechat-login.png')) {
if (-not $entry.Contains($asset)) { throw "A-01 does not consume required asset: $asset" }
}
$d1 = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'docs/design/D1_安卓视觉规范与页面壳.md')
foreach ($token in @('wechat-login.png', 'A-01')) {
if (-not $d1.Contains($token)) { throw "D1 brand-asset record is missing: $token" }
}
Write-Output 'PASS A-01 login guide contract'
```
- [ ] **Step 2: 运行测试,确认当前因路由与页面缺失而失败**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-login-guide-contract.ps1`
Expected: FAIL with `A-01 is not the application entry route.` or missing `pages/auth/entry.vue`.
- [ ] **Step 3: 提交测试合同**
```powershell
git add tests/a01-login-guide-contract.ps1
git commit -m "test: define A01 login guide contract"
```
### Task 2: 准备并登记 A-01 本地资产
**Files:**
- Create: `static/assets/icons/brand/wechat-login.png`
- Modify: `docs/design/D1_安卓视觉规范与页面壳.md`
- Test: `tests/a01-login-guide-contract.ps1`
**Interfaces:**
- Consumes: D1 的第三方品牌资产规则与现有 `static/assets/backgrounds/header-cinnabar-texture-v2.jpg``header-hall-lineart.png``paper-rice-texture-v2.jpg``footer-mountain-bamboo.png`
- Produces: `wechat-login.png`RGBA 透明 PNG)及 D1 中唯一的品牌资产记录
- [ ] **Step 1: 取得微信官方或许可渠道的透明 PNG,并保存为 `static/assets/icons/brand/wechat-login.png`**
资产要求:图形为官方绿色微信标识;无文字、无白底、无阴影;四角透明;实际显示 `44rpx` 时清晰。不得从用户截图、整页效果图或生成图裁切。
- [ ] **Step 2: 在 D1 的第三方品牌资产记录中追加唯一一行**
使用 Step 1 实际下载的页面 URL 写入来源栏,并在同一行写明“RGBA,四角透明”。下载页没有可验证的授权或使用说明时,停止本任务,不得改用截图、生成图或未知来源图标。
- [ ] **Step 3: 运行透明角检查**
```powershell
Add-Type -AssemblyName System.Drawing
$image = [System.Drawing.Bitmap]::new('static/assets/icons/brand/wechat-login.png')
try {
if (-not [System.Drawing.Image]::IsAlphaPixelFormat($image.PixelFormat)) { throw 'WeChat icon has no alpha channel.' }
if ($image.GetPixel(0, 0).A -ne 0) { throw 'WeChat icon top-left corner is not transparent.' }
if ($image.Width -lt 48 -or $image.Height -lt 48) { throw 'WeChat icon is too small for mobile display.' }
Write-Output 'PASS WeChat login asset alpha audit'
} finally {
$image.Dispose()
}
```
Expected: `PASS WeChat login asset alpha audit`.
- [ ] **Step 4: 重跑 A-01 合同,确认它仍仅因页面尚未实现失败**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-login-guide-contract.ps1`
Expected: FAIL with missing A-01 route or page; it must not report a missing brand-asset record.
- [ ] **Step 5: 提交资产与 D1 记录**
```powershell
git add static/assets/icons/brand/wechat-login.png docs/design/D1_安卓视觉规范与页面壳.md
git commit -m "assets: add local WeChat login mark"
```
### Task 3: 实现 A-01 独立欢迎页与协议显式同意
**Files:**
- Create: `pages/auth/entry.vue`
- Modify: `pages.json`
- Test: `tests/a01-login-guide-contract.ps1`
**Interfaces:**
- Consumes: Task 2 的 `wechat-login.png`,现有朱砂红头、祠堂线描、纸纹、山水资产
- Produces: `pages/auth/entry.vue`,其状态名固定为 `consented`,点击守卫固定为 `enterLogin`
- [ ] **Step 1: 在 `pages.json` 的第一项新增 A-01 路由**
```json
{
"path": "pages/auth/entry",
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
}
```
保留现有 `pages/auth/login` 路由与其顺序关系,不删除或重命名现有页面。
- [ ] **Step 2: 创建 `pages/auth/entry.vue` 的状态与协议守卫**
```vue
<script setup>
import { ref } from 'vue'
const consented = ref(false)
const toggleConsent = () => {
consented.value = !consented.value
}
const openAgreement = (name) => {
uni.showToast({ title: `${name}将在协议页开放`, icon: 'none' })
}
const enterLogin = (entry) => {
if (!consented.value) {
uni.showToast({ title: '请先阅读并同意相关协议', icon: 'none' })
return
}
uni.showToast({ title: `${entry}入口已确认`, icon: 'none' })
}
</script>
```
本任务阶段不得引入 `appApi``calcMD5`、真实登录、真实微信授权或不存在的 A-02/A-03 路由;跨页跳转在 Task 5 的执行门槛满足后替换。
- [ ] **Step 3: 写入页面主体,保持三种入口与显式复选框**
```vue
<template>
<view class="auth-entry-page">
<view class="auth-hero"><image class="auth-hall" src="/static/assets/backgrounds/header-hall-lineart.png" mode="aspectFit" /><image class="auth-seal" src="/static/assets/icons/brand/jiapu-seal-logo.png" mode="aspectFit" /></view>
<view class="auth-paper">
<image class="auth-side-ink" src="/static/assets/backgrounds/footer-mountain-bamboo.png" mode="aspectFit" />
<view class="auth-title"><text>家谱</text><text>为家族留存可传承的记忆</text></view>
<view class="auth-actions">
<button class="auth-button auth-button--primary" @click="enterLogin('账号密码登录')">账号密码登录</button>
<button class="auth-button" @click="enterLogin('手机号验证码登录')">手机号验证码登录</button>
<button class="auth-button" @click="enterLogin('微信登录')"><image src="/static/assets/icons/brand/wechat-login.png" mode="aspectFit" />微信登录</button>
</view>
<view class="auth-consent" @click="toggleConsent"><view class="auth-checkbox" :class="{ 'auth-checkbox--checked': consented }" /><text>我已阅读并同意</text><text class="auth-link" @click.stop="openAgreement('用户协议')">用户协议</text><text></text><text class="auth-link" @click.stop="openAgreement('隐私政策')">隐私政策</text></view>
</view>
</view>
</template>
```
- [ ] **Step 4: 完成最小 SCSS 外观**
要求:用 `background-image` 组合 `header-cinnabar-texture-v2.jpg``paper-rice-texture-v2.jpg`;红头可见祠堂线描;纸面底部可见但低对比的 `footer-mountain-bamboo.png`;按钮高度至少 `88rpx`;不使用 CSS Grid、`gap``clip-path`、滤镜或外部 URL。协议区域不得使用“登录即表示同意”文案,未选中复选框只显示古金圆圈,选中后显示朱砂对勾。
- [ ] **Step 5: 运行合同测试,确认通过**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-login-guide-contract.ps1`
Expected: `PASS A-01 login guide contract`.
- [ ] **Step 6: 做一次静态构建健康检查**
Run: `powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1`
Expected: `PASS compile audit`.
- [ ] **Step 7: 提交独立 A-01 页面**
```powershell
git add pages/auth/entry.vue pages.json tests/a01-login-guide-contract.ps1
git commit -m "feat: add A01 login guide"
```
### Task 4: 视觉验收与设计状态记录
**Files:**
- Modify: `docs/规划.md`
- Test: `tests/a01-login-guide-contract.ps1`, HBuilderX Android 小屏预览
**Interfaces:**
- Consumes: Task 3 完成的页面与合同
- Produces: D2 阶段下 A-01 的真实设计状态
- [ ] **Step 1: 在 HBuilderX 中预览 `pages/auth/entry` 的 `360 × 800dp` 画面**
检查:红头、标题、三个按钮和未勾选协议同时可见;账号密码为唯一红色主操作;宣纸与山水不压住协议;没有底部 Tab。
- [ ] **Step 2: 手动验证两种协议状态**
1. 初始未勾选,分别点击三个入口,均显示“请先阅读并同意相关协议”,且不跳转。
2. 勾选后复选框显示朱砂对勾;三个入口只显示本地确认提示,不请求网络。
- [ ] **Step 3: 仅在上述视觉和手动检查通过后更新 `docs/规划.md`**
`A-01 启动 / 登录引导页` 改为“视觉完成(已验收)”;D2 保持“进行中”,因为 A-02 至 A-06 尚未完成。
- [ ] **Step 4: 重跑最终验证**
Run:
```powershell
powershell -ExecutionPolicy Bypass -File tests\a01-login-guide-contract.ps1
powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1
powershell -ExecutionPolicy Bypass -File tests\uni-scss-injection.ps1
```
Expected: 三条命令均输出 `PASS`.
- [ ] **Step 5: 提交验收记录**
```powershell
git add docs/规划.md
git commit -m "docs: mark A01 visual review"
```
### Task 5: D2 跨页设计完成后的路由接通(执行门槛任务)
**Precondition:** A-02 与 A-03 的设计说明、页面文件和 `pages.json` 路由均已通过各自视觉验收;否则此任务不得开始。
**Files:**
- Modify: `pages/auth/entry.vue`
- Test: `tests/a01-login-guide-contract.ps1`(扩展路由断言)
**Interfaces:**
- Consumes: A-02 的已验收密码页路由;A-03 的已验收验证码/微信回跳页路由
- Produces: `entryRoutes`,键固定为 `password``phone``wechat`
- [ ] **Step 1: 扩展合同,要求所有目标路由已在 `pages.json` 注册**
```powershell
foreach ($route in @('pages/auth/password-login', 'pages/auth/phone-login')) {
if ($route -notin $paths) { throw "Missing approved auth target route: $route" }
}
```
- [ ] **Step 2: 将 Task 3 的本地确认提示替换为唯一的目标映射**
```js
const entryRoutes = {
password: '/pages/auth/password-login',
phone: '/pages/auth/phone-login',
wechat: '/pages/auth/phone-login?source=wechat'
}
const enterLogin = (entry) => {
if (!consented.value) {
uni.showToast({ title: '请先阅读并同意相关协议', icon: 'none' })
return
}
uni.navigateTo({ url: entryRoutes[entry] })
}
```
模板调用固定为 `enterLogin('password')``enterLogin('phone')``enterLogin('wechat')`。不得在 A-01 增加任何接口调用或注册入口。
- [ ] **Step 3: 运行合同和 A-02/A-03 各自的视觉合同**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-login-guide-contract.ps1`
Expected: `PASS A-01 login guide contract`.
- [ ] **Step 4: 提交跨页接通**
```powershell
git add pages/auth/entry.vue tests/a01-login-guide-contract.ps1
git commit -m "feat: connect A01 auth routes"
```
## Self-Review
- **规格覆盖:** Task 1 和 Task 3 覆盖三入口、主次层级、本地资产、无接口和显式协议;Task 4 覆盖小屏视觉与两种协议状态;Task 5 覆盖 A-02/A-03 的跳转契约且被 D2 设计完成门槛锁住。
- **占位检查:** 没有未落实的实现步骤。Task 2 的来源 URL 是执行时取得的审计事实;没有可验证来源即停止该任务,不得提交资产。
- **命名一致性:** 页面状态固定为 `consented`,守卫固定为 `enterLogin`,跨页唯一映射固定为 `entryRoutes`Task 1 与 Task 5 的测试和实现使用同一命名。
# 已失效历史记录(2026-07-13
> 本计划在未完成设计归档与验收前被错误建立,现禁止执行。唯一有效的 A-01 归档为 `docs/design/A01_启动登录引导_设计记录.md`;当前工作仍以 `docs/交接记录.md` 的 G-01 验收为准。
@@ -0,0 +1,150 @@
# A01 Reference Asset Rebuild Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Rebuild A01's visible red header, paper scene, title lockup, divider knots, and button frames from the approved 412 × 915 reference while preserving real DOM interactions.
**Architecture:** `pages/auth/entry.vue` remains the only interactive owner. Reference-derived raster assets own decorative visual details, while DOM owns button labels, icons, agreement state, and navigation. A small local asset-preparation command reads only the approved source screen, exports composited header/body layers and alpha PNG foreground assets, then leaves no runtime dependency on that command.
**Tech Stack:** Uni-app Vue 3, scoped SCSS, PowerShell/System.Drawing asset preparation, existing PowerShell contract and alpha audits.
## Global Constraints
- Visual truth is `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png` at 412 × 915.
- Do not render the whole reference image as page content; header, body scene, title lockup, divider knot, and button frames are distinct assets.
- Preserve A01 agreement gating and `'/pages/auth/login?agreed=1'` navigation.
- Do not modify `components/GenealogyCard.vue` or `unpackage/dist/cache/.vite/deps/*`.
- New v2 frame/title/knot assets must have transparent outer corners; remove A01's unused v1 generated ornaments.
---
### Task 1: Encode the v2 reference-asset contract
**Files:**
- Modify: `tests/a01-a02-ui-contract.ps1`
- Modify: `tests/a01-asset-alpha-audit.ps1`
**Interfaces:**
- Consumes: current `pages/auth/entry.vue` and existing v1 A01 assets.
- Produces: a failing contract requiring `auth-header-reference-v2.png`, `auth-body-scene-v2.png`, `auth-brand-lockup-v2.png`, `auth-divider-knot-v2.png`, `auth-primary-frame-v2.png`, and `auth-secondary-frame-v2.png`.
- [ ] **Step 1: Write the failing test**
Add these required paths to the A01 entry contract:
```powershell
foreach ($asset in @(
'auth-header-reference-v2.png', 'auth-body-scene-v2.png',
'auth-brand-lockup-v2.png', 'auth-divider-knot-v2.png',
'auth-primary-frame-v2.png', 'auth-secondary-frame-v2.png'
)) {
Assert-Contains -Content $entry -Expected $asset -Message "Missing A01 reference asset: $asset"
}
Assert-NotContains -Content $entry -Unexpected 'auth-button-corner-v1.png' -Message 'A01 must not retain the rejected generated corner asset'
```
Replace the alpha-audit icon list with the four transparent v2 foreground assets and assert their source dimensions: brand lockup `240 × 110`, divider knot `80 × 48`, primary frame `291 × 65`, secondary frame `291 × 65`. Assert header `412 × 139` and body scene `412 × 776` exist as opaque scene layers.
- [ ] **Step 2: Run test to verify it fails**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-a02-ui-contract.ps1; powershell -ExecutionPolicy Bypass -File tests\a01-asset-alpha-audit.ps1`
Expected: FAIL because the six `v2` paths are not referenced or do not exist.
- [ ] **Step 3: Commit the contract only after Task 2 passes**
Do not commit an intentionally failing contract. It is staged with the asset and page changes in Task 3.
### Task 2: Prepare exact reference-derived v2 assets
**Files:**
- Create: `static/assets/backgrounds/auth-header-reference-v2.png`
- Create: `static/assets/backgrounds/auth-body-scene-v2.png`
- Create: `static/assets/icons/auth/auth-brand-lockup-v2.png`
- Create: `static/assets/icons/auth/auth-divider-knot-v2.png`
- Create: `static/assets/icons/auth/auth-primary-frame-v2.png`
- Create: `static/assets/icons/auth/auth-secondary-frame-v2.png`
- Delete: `static/assets/icons/auth/auth-button-corner-v1.png`
- Delete: `static/assets/icons/auth/auth-divider-knot-v1.png`
- Delete: `static/assets/icons/auth/auth-title-cloud-v1.png`
**Interfaces:**
- Consumes: `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png`.
- Produces: fixed-pixel v2 assets used by `entry.vue`; no asset contains a clickable control or agreement copy.
- [ ] **Step 1: Export the visual layers from the approved source**
Use System.Drawing to read the 412 × 915 source. Crop rows `0..138` to `auth-header-reference-v2.png`. Crop rows `139..914` to `auth-body-scene-v2.png`, replacing the title, divider, button, register and agreement foreground rectangles with sampled rice-paper pixels before saving. The retained side architecture, bamboo, and lower mountain must stay in their original reference positions.
- [ ] **Step 2: Export alpha foregrounds**
Use the same source to create: a `240 × 110` brand lockup (dark title plus both gold clouds), an `80 × 48` divider knot, and two `291 × 65` button frame images. For the foregrounds, make the paper/background pixels transparent, preserve the exact colored ornament pixels, and for each frame clear its center rectangle while retaining the double rule and all four reference corner curls.
- [ ] **Step 3: Inspect and audit the assets**
Open the three frame/title/knot PNGs and confirm their outer corners are transparent and their visible artwork has no green matte. Run: `powershell -ExecutionPolicy Bypass -File tests\a01-asset-alpha-audit.ps1`.
Expected: PASS once the Task 1 audit rules are in place.
### Task 3: Replace the A01 decorative layer implementation
**Files:**
- Modify: `pages/auth/entry.vue`
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Modify: `design-qa.md`
- Modify: `tests/a01-a02-ui-contract.ps1`
- Modify: `tests/a01-asset-alpha-audit.ps1`
**Interfaces:**
- Consumes: all six v2 assets from Task 2.
- Produces: reference-derived visuals with unchanged `toggleAgreement`, `openLogin`, `prepareWechatLogin`, `prepareRegister`, and `prepareAgreement` functions.
- [ ] **Step 1: Update page asset ownership**
Replace the generated header plus `header-seal` overlay with `auth-header-reference-v2.png`. Replace the current paper/scenery pair with the prepared body scene layer. Replace the text/cloud title group with `auth-brand-lockup-v2.png`; retain its wrapper only for layout. Keep CSS gold rules but replace each v1 divider ornament with `auth-divider-knot-v2.png`.
For both button types, remove all four `.button-corner-asset` images. Add one absolute `.entry-button__frame` image that uses the matching primary or secondary frame asset. Keep the DOM icon/text group above the frame and preserve existing click handlers.
- [ ] **Step 2: Update visual calibration values and records**
Set the lockup and divider image dimensions to their source ratios, keep the validated vertical anchors, and remove stale v1 asset statements from the A01 design record. Update `design-qa.md` to list the latest screenshot as pending visual confirmation rather than passed.
- [ ] **Step 3: Run the focused tests**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-a02-ui-contract.ps1; powershell -ExecutionPolicy Bypass -File tests\a01-asset-alpha-audit.ps1; powershell -ExecutionPolicy Bypass -File tests\a01-design-record-audit.ps1; powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1`
Expected: all four scripts print `PASS` with exit code 0.
- [ ] **Step 4: Run full regression verification**
Run: `Get-ChildItem -LiteralPath tests -Filter *.ps1 | Sort-Object Name | ForEach-Object { & powershell -ExecutionPolicy Bypass -File $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } }; git diff --check`
Expected: every test script passes and `git diff --check` reports no errors.
- [ ] **Step 5: Commit the reference rebuild**
```powershell
git add -- pages/auth/entry.vue tests/a01-a02-ui-contract.ps1 tests/a01-asset-alpha-audit.ps1 docs/design/A01_启动登录引导_设计记录.md design-qa.md static/assets/backgrounds/auth-header-reference-v2.png static/assets/backgrounds/auth-body-scene-v2.png static/assets/icons/auth/auth-brand-lockup-v2.png static/assets/icons/auth/auth-divider-knot-v2.png static/assets/icons/auth/auth-primary-frame-v2.png static/assets/icons/auth/auth-secondary-frame-v2.png static/assets/icons/auth/auth-button-corner-v1.png static/assets/icons/auth/auth-divider-knot-v1.png static/assets/icons/auth/auth-title-cloud-v1.png
git commit -m "fix: rebuild A01 reference visual assets"
```
### Task 4: Obtain runtime evidence
**Files:**
- Modify: `design-qa.md` after receiving user-provided HBuilderX/Android screenshots.
**Interfaces:**
- Consumes: rendered unchecked A01 screenshots at 412 × 915 and 320 × 568.
- Produces: a visual QA verdict grounded in those screenshots.
- [ ] **Step 1: Compare the two same-state screenshots**
At 412 × 915, verify: header bottom gold band and seal, side architecture/bamboo/lower mountain placements, brand lockup scale, both frame corner curls, divider knots, and agreement baseline.
- [ ] **Step 2: Check narrow-screen safety**
At 320 × 568, scroll to the agreement row and verify that the checkbox and all copy remain visible above the system navigation area.
- [ ] **Step 3: Record evidence status**
Set `design-qa.md` to `final result: passed` only if both screenshots satisfy the checks; otherwise document the remaining pixel difference and keep it pending.
@@ -0,0 +1,273 @@
# A-01 Runtime Visual Calibration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make the A-01 unchecked entry page's real DOM match the approved 412 x 915 visual reference for its header, title, dividers, buttons, registration entry, and agreement row.
**Architecture:** `docs/design/A01_启动登录引导_设计记录.md` owns the calibrated A-01 layout contract. `pages/auth/entry.vue` consumes that contract through local scoped styles and named A-01 elements; it retains the existing local interaction handlers. `tests/a01-a02-ui-contract.ps1` guards the layout anchors and blocks the rejected registration side lines and button-corner construction.
**Tech Stack:** uni-app Vue 3, scoped SCSS, local PNG assets, PowerShell contract scripts.
## Global Constraints
- Use `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png` as the sole visual reference for the unchecked state.
- Keep real DOM, local-only interaction, the existing route to A-02, and explicit agreement gating.
- Do not use the whole reference image as page content, call APIs, modify A-02, or alter the existing authentication backgrounds in this task.
- Preserve vertical scrolling and `env(safe-area-inset-bottom)` for 320 x 568.
- Use local transparent PNG assets for title cloud imagery; do not use text glyphs or emoji as ornaments.
---
### Task 1: Make the A-01 record the sole measured-layout owner and add a red visual contract
**Files:**
- Modify: `docs/design/A01_启动登录引导_设计记录.md:30-33`
- Modify: `docs/superpowers/specs/2026-07-13-a01-runtime-visual-calibration-design.md:7-31`
- Modify: `docs/superpowers/specs/2026-07-13-a01-a02-static-ui-implementation-design.md:19`
- Modify: `tests/a01-a02-ui-contract.ps1:74-87`
**Interfaces:**
- Consumes: approved `A01-启动登录引导-栅格验收-412x915.png` and the current local route/interaction contract.
- Produces: one permanent 750rpx coordinate table and a contract that rejects the old visual construction.
- [ ] **Step 1: Replace the old prose layout values in the A-01 design record with the calibrated table**
Replace the current fixed-layout bullets with this table. It is the only owner of these values:
```markdown
| 元素 | 412 × 915 像素基准 | 750rpx 实现基准 |
| --- | --- | --- |
| 朱砂家祠头图 | 高 139px | 高 253rpx |
| 标题区起点 | 红头后约 82px | `padding-top: 150rpx` |
| 标题分隔饰线 | 宽约 234px | 宽 426rpx;上、下间距独立 |
| 两个按钮 | x=60px,宽约 291px,高约 65px | `margin: 95rpx 110rpx 0`,高 118rpx |
| 两按钮间距 | 约 28px | `margin-top: 51rpx` |
| 注册入口 | 微信按钮后约 56px;无左右短线 | `margin-top: 98rpx` |
| 协议行 | 注册入口后约 70px | `margin-top: 128rpx` 加安全区 |
```
- [ ] **Step 2: Remove the superseded dimensions from the static UI implementation contract**
In `2026-07-13-a01-runtime-visual-calibration-design.md`, replace its numeric coordinate table with this ownership statement so the design record remains the only permanent coordinate source:
```markdown
## Contract ownership
`docs/design/A01_启动登录引导_设计记录.md` owns the calibrated 412 × 915 coordinate table and acceptance thresholds. This implementation specification records scope and component strategy only; it does not define duplicate layout values.
```
Then replace the static UI implementation contract's layout sentence with:
```markdown
- A-01 的运行时布局唯一遵循 `docs/design/A01_启动登录引导_设计记录.md` 中的“412 × 915 运行时校准表”;本文件不再重复或定义按钮与间距数值。
```
- [ ] **Step 3: Extend the PowerShell contract before changing the page**
Add these assertions immediately after the existing A-01 DOM-structure assertions:
```powershell
foreach ($requiredVisualAnchor in @(
'brand-divider--top',
'brand-divider--bottom',
'entry-button--primary',
'entry-button--secondary',
'height: 253rpx',
'padding: 150rpx 56rpx 0',
'width: 426rpx',
'margin: 95rpx 110rpx 0',
'height: 118rpx',
'margin-top: 51rpx',
'margin-top: 98rpx',
'margin-top: 128rpx'
)) {
Assert-Contains -Content $entry -Expected $requiredVisualAnchor -Message "Missing A-01 calibrated visual anchor: $requiredVisualAnchor"
}
foreach ($rejectedVisualConstruction in @('link-line', 'button-corner', 'class="auth-button primary-button"')) {
Assert-NotContains -Content $entry -Unexpected $rejectedVisualConstruction -Message "A-01 retains rejected visual construction: $rejectedVisualConstruction"
}
```
- [ ] **Step 4: Run the focused contract to prove it is red**
Run: `powershell -ExecutionPolicy Bypass -File tests\a01-a02-ui-contract.ps1`
Expected: failure mentioning `Missing A-01 calibrated visual anchor` or `A-01 retains rejected visual construction`.
### Task 2: Rebuild A-01's measured DOM layout without changing its interaction contract
**Files:**
- Modify: `pages/auth/entry.vue:11-65,145-324`
- Test: `tests/a01-a02-ui-contract.ps1`
**Interfaces:**
- Consumes: the measured A-01 table from the design record and existing `openLogin`, `prepareWechatLogin`, `prepareRegister`, and `toggleAgreement` handlers.
- Produces: `brand-divider--top`, `brand-divider--bottom`, `entry-button--primary`, and `entry-button--secondary` rendered from real DOM.
- [ ] **Step 1: Replace the entry markup that creates the rejected decorations**
Keep the existing cloud `image` source, but split the dividers by purpose, remove the registration side lines, remove the four corner views, and use A-01-specific button classes:
```vue
<view class="brand-divider brand-divider--top">
<view class="brand-divider__line" />
<image class="brand-divider__ornament" src="/static/assets/icons/action/create-cloud-v2.png" mode="aspectFit" />
<view class="brand-divider__line" />
</view>
<view class="entry-actions">
<view class="entry-button entry-button--primary" hover-class="entry-button--pressed" @click="openLogin">
<image class="entry-button__icon" src="/static/assets/icons/auth/login-outline-v1.png" mode="aspectFit" />
<text>登录</text>
</view>
<view class="entry-button entry-button--secondary" hover-class="entry-button--pressed" @click="prepareWechatLogin">
<image class="entry-button__icon entry-button__icon--wechat" src="/static/assets/icons/auth/wechat-licensed-v1.png" mode="aspectFit" />
<text>微信登录</text>
</view>
<view class="register-link" hover-class="link-hover" @click="prepareRegister">
<text>还没有账号?<text class="register-emphasis">注册账号</text></text>
</view>
</view>
```
Render the second divider after `brand-subtitle` with the same child structure and class `brand-divider--bottom`.
- [ ] **Step 2: Replace the old generic layout selectors with these calibrated scoped rules**
Keep the existing background and script blocks unchanged. Replace `.auth-header` through `.link-hover` with the following A-01 rules:
```scss
.auth-header { position: relative; height: 253rpx; overflow: hidden; }
.brand-intro {
display: flex;
flex-direction: column;
align-items: center;
padding: 150rpx 56rpx 0;
color: #3f2c1d;
}
.brand-divider {
display: flex;
align-items: center;
justify-content: center;
width: 426rpx;
}
.brand-divider--top { margin: 47rpx 0 21rpx; }
.brand-divider--bottom { margin: 34rpx 0 30rpx; }
.brand-divider__line { flex: 1; height: 1rpx; background: #d5a34f; }
.brand-divider__ornament { width: 44rpx; height: 24rpx; margin: 0 14rpx; }
.entry-actions { margin: 95rpx 110rpx 0; }
.entry-button {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 100%;
height: 118rpx;
border: 2rpx solid #d8ad63;
border-radius: 6rpx;
font-family: STKaiti, KaiTi, serif;
font-size: 38rpx;
letter-spacing: 4rpx;
}
.entry-button::after {
position: absolute;
inset: 10rpx;
border: 1rpx solid rgba(225, 180, 105, 0.92);
border-radius: 2rpx;
content: '';
pointer-events: none;
}
.entry-button--primary {
border-color: #9e170e;
background: #b7170d;
color: #fffaf0;
}
.entry-button--primary::after { border-color: #edc67c; }
.entry-button--secondary {
margin-top: 51rpx;
background: rgba(255, 252, 245, 0.82);
color: #60442f;
}
.entry-button__icon { width: 46rpx; height: 46rpx; margin-right: 18rpx; }
.entry-button__icon--wechat { width: 52rpx; }
.entry-button--primary .entry-button__icon { filter: brightness(0) invert(1); }
.entry-button > text { position: relative; z-index: 1; }
.entry-button--pressed { opacity: 0.84; }
.register-link {
display: flex;
align-items: center;
justify-content: center;
margin-top: 98rpx;
color: #60442f;
font-family: STKaiti, KaiTi, serif;
font-size: 30rpx;
letter-spacing: 2rpx;
white-space: nowrap;
}
.agreement-row { margin-top: 128rpx; }
```
Delete `.gold-divider`, `.divider-line`, `.divider-ornament`, `.auth-button`, `.primary-button`, `.secondary-button`, `.button-icon`, `.wechat-icon`, `.button-hover`, `.button-corner`, all `.corner-*` selectors, and `.link-line`. Do not change any handler or copy.
- [ ] **Step 3: Run the focused contract and relevant compilation audits**
Run:
```powershell
powershell -ExecutionPolicy Bypass -File tests\a01-a02-ui-contract.ps1
powershell -ExecutionPolicy Bypass -File tests\vue3-entry.ps1
powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1
```
Expected: all three scripts print `PASS`; the focused contract prints `A01-A02-UI-CONTRACT PASS`.
### Task 3: Record the completed static change and request runtime evidence
**Files:**
- Modify: `docs/design/A01_启动登录引导_设计记录.md:51-57`
- Modify: `design-qa.md:1-31`
- Test: `tests/a01-a02-ui-contract.ps1`, `tests/vue3-entry.ps1`, `tests/compile-audit.ps1`
**Interfaces:**
- Consumes: passing static contract and a page that still needs HBuilderX/Android visual evidence.
- Produces: an honest handoff that separates static verification from runtime screenshot acceptance.
- [ ] **Step 1: Update the design record's verification state**
Add this sentence under the runtime-verification gap:
```markdown
- 2026-07-13 已按 412 × 915 参考图重新校准真实 DOM 的标题区、分隔饰线、双按钮、注册入口与协议行;静态合同通过,但仍须由同尺寸 HBuilderX 或 Android 截图叠图确认,未确认前不得标记 A-01 完成。
```
- [ ] **Step 2: Replace the stale QA finding with the exact next capture**
Set `design-qa.md` to state that the source remains `A01-启动登录引导-栅格验收-412x915.png`, static calibration has passed, and the sole remaining evidence is an unchecked `412 x 915` HBuilderX/Android capture without browser chrome plus a `320 x 568` safety/scroll capture.
- [ ] **Step 3: Run the final static verification and inspect the scoped diff**
Run:
```powershell
powershell -ExecutionPolicy Bypass -File tests\a01-a02-ui-contract.ps1
powershell -ExecutionPolicy Bypass -File tests\vue3-entry.ps1
powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1
git diff --check
git diff -- pages/auth/entry.vue tests/a01-a02-ui-contract.ps1 docs/design/A01_启动登录引导_设计记录.md docs/superpowers/specs/2026-07-13-a01-runtime-visual-calibration-design.md docs/superpowers/specs/2026-07-13-a01-a02-static-ui-implementation-design.md design-qa.md
```
Expected: all scripts pass, `git diff --check` is silent, and no interaction, route, or API code is changed.
- [ ] **Step 4: Commit only the A-01 calibration work**
```powershell
git add -- pages/auth/entry.vue tests/a01-a02-ui-contract.ps1 docs/design/A01_启动登录引导_设计记录.md docs/superpowers/specs/2026-07-13-a01-runtime-visual-calibration-design.md docs/superpowers/specs/2026-07-13-a01-a02-static-ui-implementation-design.md design-qa.md
git commit -m "fix: calibrate A01 entry visual layout"
```
Expected: the commit includes only the listed A-01 files; it must not include `components/GenealogyCard.vue` or Vite cache files.
@@ -0,0 +1,312 @@
# A-01 Text Registration Entry Design Evidence Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 完成 A-01“登录 + 微信登录 + 文字注册入口”的可归档设计证据与 Android 固定栅格验收口径,不修改 uni-app 页面、接口或打包。
**Architecture:** `docs/design/A01_启动登录引导_设计记录.md` 是 A-01 当前规则的唯一所有者。PowerShell 审计脚本只验证该记录、归档效果稿和项目内静态资产;四个尺寸的效果稿是静态设计验证,不冒充 uni-app 运行时验收。
**Tech Stack:** Markdown、PNG/JPG 静态资产、PowerShell 5.1、内置图像生成工具、HBuilderX Web 浏览器(仅在页面实现阶段用于运行时验收)。
## Global Constraints
- 只做 `D2 / A-01` 的设计证据;不改 `.vue`、路由、接口、真实登录、Android 打包或依赖。
- 当前设计唯一所有者是 `docs/design/A01_启动登录引导_设计记录.md`;本计划和所有审计脚本不得重复定义入口规则。
- A-01 只有朱砂红“登录”主按钮、古金描边“微信登录”按钮和“还没有账号?注册账号”文字入口。
- 登录进入 A-02;A-02 承接“账号密码登录 / 手机验证码登录”双标签;微信授权属于 A-03,注册属于 A-04,忘记密码属于 A-05。
- 协议复选框默认未勾选。任何进入操作都不能默认视为用户同意协议;未勾选仅提示用户先确认协议。
- 使用 Android `rpx + flex` 设计口径,不使用 CSS Grid 或 `gap`;底部安全区规则必须使用 `env(safe-area-inset-bottom)`
- 登录欢迎页的头图、宣纸和山水必须是 A-01 独立资产,不复用 G-01 的 `footer-mountain-bamboo.png`
- 功能图标必须为项目内 `96 × 96px` RGBA 透明 PNG;微信标志只使用官方或许可来源的原色透明 PNG,不重绘或改色。
- 效果稿 v1、v2 是历史证据,保留但不可作为当前入口结构依据;当前整页效果稿 v3 也不能充当真实背景或图标资产。
## File Structure
| 文件 | 职责 |
| --- | --- |
| `docs/design/A01_启动登录引导_设计记录.md` | A-01 当前入口、栅格、状态、资产清单与静态验收结论的唯一设计来源。 |
| `tests/a01-design-record-audit.ps1` | 验证当前入口、路由归属、栅格约束和 v3 效果稿的归档存在。 |
| `static/assets/backgrounds/auth-ancestral-header-v1.png` | A-01 独立朱砂家祠头图,`750 × 196px`。 |
| `static/assets/backgrounds/auth-rice-paper-v1.jpg` | A-01 独立暖宣纸纹理,`750 × 1334px`。 |
| `static/assets/backgrounds/auth-ink-scenery-v1.png` | A-01 独立淡墨建筑、竹影与山水叠层,`750 × 1334px`。 |
| `static/assets/icons/auth/login-outline-v1.png` | 登录按钮使用的 `96 × 96px` 透明 PNG。 |
| `static/assets/icons/auth/wechat-licensed-v1.png` | 微信按钮使用的 `96 × 96px` 许可来源、原色透明 PNG。 |
| `tests/a01-asset-alpha-audit.ps1` | 验证 A-01 图标的尺寸、透明角和可见像素,以及 A-01 独立背景文件。 |
| `docs/design/screens/A01-启动登录引导-栅格验收-320x568.png` | 320 × 568 静态设计效果稿。 |
| `docs/design/screens/A01-启动登录引导-栅格验收-360x640.png` | 360 × 640 静态设计效果稿。 |
| `docs/design/screens/A01-启动登录引导-栅格验收-360x800.png` | 360 × 800 静态设计效果稿。 |
| `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png` | 412 × 915 静态设计效果稿。 |
---
### Task 1: 锁定当前入口与固定栅格的审计关口
**Files:**
- Modify: `tests/a01-design-record-audit.ps1`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: UTF-8 Base64 文本解码函数 `ConvertFrom-Utf8Base64`、A-01 设计记录和效果稿 v3。
- Produces: 退出码 `0``A01-DESIGN-RECORD-AUDIT PASS`;缺少任何规则或效果稿时抛出明确错误。
- [x] **Step 1: 写入会失败的栅格规则断言**
在现有 `$rules` 数组后增加以下代码;这些规则在本任务开始时尚未写入设计记录,因此审计应失败。
```powershell
$layoutRules = @(
(ConvertFrom-Utf8Base64 '5YaF5a655a695bqmIDYzOHJweA=='),
(ConvertFrom-Utf8Base64 '5Li75oyJ6ZKu6auY5bqmIDEwOHJweA=='),
(ConvertFrom-Utf8Base64 'ZW52KHNhZmUtYXJlYS1pbnNldC1ib3R0b20p'),
(ConvertFrom-Utf8Base64 '6LSm5Y+35a+G56CB55m75b2VIC8g5omL5py66aqM6K+B56CB55m75b2V')
)
foreach ($rule in $layoutRules) {
if ($record -notmatch [regex]::Escape($rule)) {
throw "Missing A01 layout rule: $rule"
}
}
$currentStructure = [regex]::Match($record, '(?s)## 2\\..*?(?=## 3\\.)').Value
$currentStructureRule = ConvertFrom-Utf8Base64 '5Lik5Liq5aSn5oyJ6ZKu5ZKM5LiA5Liq5paH5a2X5YWl5Y+j'
if ($currentStructure -notmatch [regex]::Escape($currentStructureRule)) {
throw "Missing current A01 structure rule: $currentStructureRule"
}
```
- [x] **Step 2: 运行审计并确认它先失败**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/a01-design-record-audit.ps1
```
Expected: `Missing A01 layout rule: 内容宽度 638rpx`。
- [x] **Step 3: 保持红灯测试未提交并进入 Task 2**
红灯只用于证明固定栅格规则确实缺失。不要单独提交失败测试;在 Task 2 将设计记录补齐并转绿后,再与设计记录一起提交。
### Task 2: 写入 A-01 固定栅格与状态边界
**Files:**
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: Task 1 的 `$layoutRules` 与当前入口结构断言。
- Produces: 以 750rpx 设计画布表达的固定栅格、四类进入状态和明确的非运行时验收边界。
- [x] **Step 1: 在第 3 节加入固定栅格**
在“固定布局与状态边界”中加入以下内容,保持现有的入口与协议规则不变:
```markdown
- 固定设计画布为 `750rpx` 宽;内容宽度 638rpx,左右各 56rpx。朱砂家祠头图高 196rpx;标题与副标题区域从头图下方开始,操作区首按钮顶边固定为 646rpx。
- 主按钮高度 108rpx,微信按钮高度 108rpx,二者间距 30rpx;注册文字在微信按钮下方 56rpx,字号 28rpx,整行居中且不得换行。两个按钮文字字号 36rpx,均单行居中。
- 协议行使用 26rpx 字号,页面最小底部内边距为 `calc(32rpx + env(safe-area-inset-bottom))`。320 × 568 时页面允许纵向滚动,协议行与任一入口均不得落入系统导航区。
- 仅作静态设计验证,不代表 uni-app 运行时验收;页面实现后仍须在 Android 320 × 568、360 × 640、360 × 800、412 × 915 上重新验收。
```
- [x] **Step 2: 在第 3 节加入状态表**
紧接固定栅格条目,加入以下表格:
```markdown
| 状态 | 触发 | A-01 可见结果 | 去向 |
| --- | --- | --- | --- |
| 初始未勾选 | 首次进入 | 两个按钮、注册文字与空复选框 | 留在 A-01 |
| 未同意协议 | 点击登录、微信登录或注册账号 | 提示“请先阅读并同意相关协议”,不跳转、不改变勾选状态 | 留在 A-01 |
| 登录已确认协议 | 点击“登录” | 结束 A-01 引导 | A-02,默认账号密码登录标签 |
| 微信已确认协议 | 点击“微信登录” | 结束 A-01 引导 | A-03,显示授权中、取消或失败回跳 |
| 注册已确认协议 | 点击“注册账号”文字 | 结束 A-01 引导 | A-04 |
```
- [x] **Step 3: 运行审计并确认通过**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/a01-design-record-audit.ps1
```
Expected: `A01-DESIGN-RECORD-AUDIT PASS`。
- [x] **Step 4: 检查文档差异并提交**
```powershell
git diff --check
git add docs/design/A01_启动登录引导_设计记录.md tests/a01-design-record-audit.ps1
git commit -m "docs: define A01 grid and entry states"
```
### Task 3: 归档 A-01 独立背景和透明图标
**Files:**
- Create: `static/assets/backgrounds/auth-ancestral-header-v1.png`
- Create: `static/assets/backgrounds/auth-rice-paper-v1.jpg`
- Create: `static/assets/backgrounds/auth-ink-scenery-v1.png`
- Create: `static/assets/icons/auth/login-outline-v1.png`
- Create: `static/assets/icons/auth/wechat-licensed-v1.png`
- Create: `tests/a01-asset-alpha-audit.ps1`
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Test: `tests/a01-asset-alpha-audit.ps1`
**Interfaces:**
- Consumes: Task 2 的 A-01 独立美术方向和项目现有 `tests/g01-asset-alpha-audit.ps1` 的透明角审计方式。
- Produces: 可由页面实现消费的独立背景与两个 `96 × 96px` RGBA 透明图标;设计记录中带来源、用途、路径、尺寸和 Alpha 验收结论的资产清单。
- [x] **Step 1: 创建先失败的资产审计脚本**
创建 `tests/a01-asset-alpha-audit.ps1`,内容如下:
```powershell
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
Add-Type -AssemblyName System.Drawing
$backgrounds = @(
'static/assets/backgrounds/auth-ancestral-header-v1.png',
'static/assets/backgrounds/auth-rice-paper-v1.jpg',
'static/assets/backgrounds/auth-ink-scenery-v1.png'
)
$icons = @(
'static/assets/icons/auth/login-outline-v1.png',
'static/assets/icons/auth/wechat-official-v1.png'
)
foreach ($asset in $backgrounds + $icons) {
if (-not (Test-Path -LiteralPath (Join-Path $root $asset))) {
throw "Missing A01 asset: $asset"
}
}
foreach ($asset in $icons) {
$bitmap = [System.Drawing.Bitmap]::FromFile((Join-Path $root $asset))
$width = [int]$bitmap.Width
$height = [int]$bitmap.Height
$corners = @(
$bitmap.GetPixel(0, 0).A,
$bitmap.GetPixel(($width - 1), 0).A,
$bitmap.GetPixel(0, ($height - 1)).A,
$bitmap.GetPixel(($width - 1), ($height - 1)).A
)
$visiblePixels = 0
for ($y = 0; $y -lt $height; $y++) {
for ($x = 0; $x -lt $width; $x++) {
if ($bitmap.GetPixel($x, $y).A -gt 24) { $visiblePixels++ }
}
}
$bitmap.Dispose()
if ($width -ne 96 -or $height -ne 96) { throw "$asset must be 96 x 96px." }
if (($corners | Where-Object { $_ -ne 0 }).Count -ne 0) { throw "$asset has opaque outer corners." }
if ($visiblePixels -lt 80) { throw "$asset has no usable visible artwork." }
Write-Output "PASS $asset transparent corners and visible artwork"
}
Write-Output 'A01-ASSET-ALPHA-AUDIT PASS'
```
- [x] **Step 2: 运行资产审计并确认它先失败**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/a01-asset-alpha-audit.ps1
```
Expected: `Missing A01 asset: static/assets/backgrounds/auth-ancestral-header-v1.png`。
- [x] **Step 3: 生成或下载并记录资产**
生成以下三张独立背景,不能裁切或复用 G-01 背景:朱砂家祠头图 `750 × 196px`、暖宣纸纹理 `750 × 1334px`、淡墨建筑竹影山水叠层 `750 × 1334px`。将它们存到本任务列出的精确路径。
为登录按钮准备古金或白色线性人物图标,保存为 `login-outline-v1.png`;从官方或明确许可来源取得未经改色的微信图标,保存为 `wechat-licensed-v1.png`。两个文件均为 `96 × 96px` RGBA PNG,四个角完全透明。
在 A-01 设计记录新增“资产清单”表格,并写入以下列:资产路径、用途、来源或生成提示、尺寸、透明验收。微信条目必须填入直接来源页面与获取日期;其余四项必须写入生成提示或公开许可证来源。
- [x] **Step 4: 运行资产审计并确认通过**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/a01-asset-alpha-audit.ps1
```
Expected: 每个图标输出一行 `PASS`,最后输出 `A01-ASSET-ALPHA-AUDIT PASS`。
- [x] **Step 5: 提交可消费资产与审计**
```powershell
git add static/assets/backgrounds/auth-ancestral-header-v1.png static/assets/backgrounds/auth-rice-paper-v1.jpg static/assets/backgrounds/auth-ink-scenery-v1.png static/assets/icons/auth/login-outline-v1.png static/assets/icons/auth/wechat-licensed-v1.png tests/a01-asset-alpha-audit.ps1 docs/design/A01_启动登录引导_设计记录.md
git commit -m "assets: add A01 login design resources"
```
### Task 4: 归档四个 Android 尺寸的静态设计验收稿
**Files:**
- Create: `docs/design/screens/A01-启动登录引导-栅格验收-320x568.png`
- Create: `docs/design/screens/A01-启动登录引导-栅格验收-360x640.png`
- Create: `docs/design/screens/A01-启动登录引导-栅格验收-360x800.png`
- Create: `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png`
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Modify: `docs/规划.md`
- Modify: `docs/交接记录.md`
- Test: `tests/a01-design-record-audit.ps1`
**Interfaces:**
- Consumes: Task 2 的 750rpx 栅格、Task 3 的独立资产和当前 v3 的视觉方向。
- Produces: 四张按目标尺寸生成的静态设计验收稿;设计记录明确区分静态验收与页面实现后的运行时验收。
- [x] **Step 1: 生成四张静态验收稿**
使用当前 v3 作为样式参考,为每个精确尺寸单独生成整页效果稿,不裁切同一张长图。每一张都必须显示:朱砂家祠头图、标题与副标题、两个大按钮、单行文字注册入口、未勾选协议和安全区留白。
验收时逐项检查:320 × 568 的“还没有账号?注册账号”不换行;四个尺寸的两个按钮文字均单行;协议行完整可见;底部内容不进入 `env(safe-area-inset-bottom)` 预留区;没有账号密码或手机验证码的独立大按钮。
- [x] **Step 2: 在设计记录写入静态验收结论**
在第 4 节增加四个效果稿链接和以下结论:
```markdown
- 静态设计验收已覆盖 320 × 568、360 × 640、360 × 800、412 × 915;四个尺寸均保持两个大按钮与单行文字注册入口。
- 此结论只验证设计稿与固定栅格,不替代 uni-app 页面实现后的 Android 运行时验收。
```
- [x] **Step 3: 运行设计记录审计并检查差异**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/a01-design-record-audit.ps1
git diff --check
```
Expected: `A01-DESIGN-RECORD-AUDIT PASS`,且 `git diff --check` 没有错误输出。
- [x] **Step 4: 更新阶段状态并提交**
仅当四张静态验收稿和 Task 3 资产审计均通过时,将 `docs/规划.md` 与 `docs/交接记录.md` 更新为“A-01 静态设计证据已完成,等待页面实现阶段的运行时验收”;不得写成页面已实现、接口已接通或 Android 已打包。
```powershell
git add docs/design/screens/A01-启动登录引导-栅格验收-320x568.png docs/design/screens/A01-启动登录引导-栅格验收-360x640.png docs/design/screens/A01-启动登录引导-栅格验收-360x800.png docs/design/screens/A01-启动登录引导-栅格验收-412x915.png docs/design/A01_启动登录引导_设计记录.md docs/规划.md docs/交接记录.md
git commit -m "docs: archive A01 static size evidence"
```
## Plan Self-Review
- 规格覆盖:入口层级由 Task 1 审计、Task 2 固定;协议、路由和状态由 Task 2 记录;独立背景、透明图标与微信来源由 Task 3 归档;四个 Android 尺寸由 Task 4 归档。
- 范围检查:所有任务限于设计记录、静态资源、效果稿和审计;没有 Vue、接口、登录逻辑或打包步骤。
- 一致性检查:A-02 只承接账号密码与手机验证码双标签;A-03 只承接微信授权;A-04 只承接注册;A-05 只承接忘记密码。
- 验收边界:四张效果稿是静态设计验证,不能替代页面实现后的真实 Android 运行时验收。
@@ -0,0 +1,165 @@
# A-01 V3 Visual Fidelity Implementation Plan
> 状态:已失效。用户否决“整张效果图作为页面视觉层”的方案;当前 A-01 必须以真实 DOM 还原 v3,见 `docs/design/A01_启动登录引导_设计记录.md`。
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make the running A-01 page reproduce the selected `A01-启动登录引导-效果稿-v3-文字注册入口.png` at mobile width while preserving its existing local-only interactions.
**Architecture:** The accepted v3 image is a complete 852 × 1846 mobile visual, including typography, ornamental borders, header, ink scenery and the empty agreement circle. `pages/auth/entry.vue` renders it at a logical width of 750rpx and layers only transparent interaction regions above it. The existing `agreed` state and local-only toast/navigation functions remain the sole interaction owner.
**Tech Stack:** uni-app Vue 3 `<script setup>`, local PNG imported with `@/`, PowerShell contract audit.
## Global Constraints
- Visual source of truth: `docs/design/screens/A01-启动登录引导-效果稿-v3-文字注册入口.png`.
- Do not generate artwork, redraw v3 ornaments in CSS, add API calls, write login state, or create routes.
- A-01 stays vertically scrollable below 430 × 932; render the source with width-fixed scaling.
- A-01 is unchecked by default and retains the exact guard message `请先阅读并同意相关协议`.
- Only `pages/auth/entry.vue`, `tests/a01-a02-ui-contract.ps1`, and A-01 design/hand-off records may change.
---
### Task 1: Change the A-01 contract to the selected v3 source
**Files:**
- Modify: `tests/a01-a02-ui-contract.ps1`
**Interfaces:**
- Consumes: the local v3 visual source and existing `openLogin`, `prepareWechatLogin`, `prepareRegister`, `prepareAgreement` functions.
- Produces: a source-level guard that rejects the former hand-built header, title, cloud and button decorations.
- [ ] **Step 1: Write the failing visual-source assertions**
Add these checks while retaining agreement, route, local-toast and forbidden-API checks:
```powershell
Assert-Contains -Content $entry -Expected "import entryGuideVisual from '@/docs/design/screens/A01-启动登录引导-效果稿-v3-文字注册入口.png'" -Message 'A-01 must import the selected v3 visual source'
foreach ($requiredClass in @('guide-art', 'entry-login-hit', 'entry-wechat-hit', 'entry-register-hit', 'entry-agreement-hit')) {
Assert-Contains -Content $entry -Expected $requiredClass -Message "Missing A-01 interaction region: $requiredClass"
}
foreach ($forbiddenClass in @('auth-header', 'brand-intro', 'entry-actions', 'cloud')) {
Assert-NotContains -Content $entry -Unexpected $forbiddenClass -Message "A-01 must not redraw the v3 visual with $forbiddenClass"
}
```
- [ ] **Step 2: Run the contract test before changing the page**
Run: `& .\tests\a01-a02-ui-contract.ps1`
Expected: failure stating that A-01 does not import the selected v3 visual source.
- [ ] **Step 3: Do not commit red state**
Leave the failing contract uncommitted until Task 2 passes.
---
### Task 2: Replace the hand-built A-01 visual with the accepted v3 source
**Files:**
- Modify: `pages/auth/entry.vue`
**Interfaces:**
- Consumes: `entryGuideVisual`, `agreed`, `requireAgreement`, `openLogin`, `prepareWechatLogin`, `prepareRegister`, and `prepareAgreement`.
- Produces: the v3 presentation plus four screen-coordinate interaction regions.
- [ ] **Step 1: Import and render the accepted source width-fixed**
```vue
<script setup>
import { ref } from 'vue'
import entryGuideVisual from '@/docs/design/screens/A01-启动登录引导-效果稿-v3-文字注册入口.png'
</script>
<template>
<view class="entry-page">
<image class="guide-art" :src="entryGuideVisual" mode="widthFix" />
</view>
</template>
```
Set the root to `width: 750rpx; min-height: 1625rpx;` with no `overflow: hidden`, so 320 × 568 remains scrollable.
- [ ] **Step 2: Add only invisible v3 hit regions**
```vue
<view class="entry-hit entry-login-hit" hover-class="entry-hit-active" @click="openLogin" />
<view class="entry-hit entry-wechat-hit" hover-class="entry-hit-active" @click="prepareWechatLogin" />
<view class="entry-hit entry-register-hit" hover-class="entry-hit-active" @click="prepareRegister" />
<view class="entry-hit entry-agreement-hit" @click="toggleAgreement" />
<view v-if="agreed" class="agreement-confirmed"></view>
```
Use v3s 750rpx logical coordinates: login `top: 799rpx; left: 99rpx; width: 552rpx; height: 126rpx`; WeChat `top: 960rpx`; register `top: 1155rpx; left: 200rpx; width: 350rpx; height: 72rpx`; agreement toggle `top: 1362rpx; left: 126rpx; width: 60rpx; height: 60rpx`. Display the check marker only after a user toggle.
- [ ] **Step 3: Preserve the existing local-only behavior**
```js
const requireAgreement = () => {
if (agreed.value) return true
uni.showToast({ title: '请先阅读并同意相关协议', icon: 'none' })
return false
}
const openLogin = () => {
if (!requireAgreement()) return
uni.navigateTo({ url: '/pages/auth/login?agreed=1' })
}
```
Keep prepared-state toasts for WeChat, registration and agreement pages. Do not add `appApi`, `calcMD5`, `loginWithPassword`, `uni.reLaunch`, or an implicit agreement sentence.
- [ ] **Step 4: Run the focused test cycle**
Run: `& .\tests\a01-a02-ui-contract.ps1`
Expected: `A01-A02-UI-CONTRACT PASS`.
---
### Task 3: Record the v3 runtime source and verify
**Files:**
- Modify: `docs/design/A01_启动登录引导_设计记录.md`
- Modify: `docs/交接记录.md`
**Interfaces:**
- Consumes: selected v3 source and current A-01 local interaction contract.
- Produces: one documented visual source for the next Android screenshot comparison.
- [ ] **Step 1: Update the A-01 record**
State that v3 is the runtime visual source, not merely archived evidence. Correct the principal button copy to `登录`; its destination remains A-02s default account/password tab after explicit agreement.
- [ ] **Step 2: Update hand-off status**
Record that the first local preview diverged from v3 in header height, button width and vertical rhythm, and that the correction uses v3 directly. Keep four-size Android runtime comparison pending.
- [ ] **Step 3: Run project verification**
```powershell
$tests = Get-ChildItem -LiteralPath .\tests -Filter *.ps1 | Sort-Object Name
foreach ($test in $tests) { & $test.FullName }
git diff --check
git status --short
```
Expected: all scripts pass, `git diff --check` is silent, and only these four files changed besides user-owned dirty files.
- [ ] **Step 4: Commit scoped work**
```powershell
git add -- pages/auth/entry.vue tests/a01-a02-ui-contract.ps1 docs/design/A01_启动登录引导_设计记录.md docs/交接记录.md
git commit -m "fix: match A01 v3 visual reference"
```
---
## Self-Review
- [x] The target is the exact saved v3 image the user displayed, not a new interpretation.
- [x] The plan replaces hand-drawn visible ornaments with the existing visual asset and keeps interactions local.
- [x] The contract goes red before the page change, then verifies the visual source and interaction regions.
- [x] No API, new route, Android configuration, or non-A-01 screen is included.
- [x] Android runtime screenshot comparison remains follow-up work, not an unverified claim.
@@ -0,0 +1,226 @@
# 家谱 APP 地基重整 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在不改变 A01、G01 已验收视觉结果的前提下,完成页面路由命名、图片资产包、代码中文注释和无引用资源清理的可维护地基。
**Architecture:** `pages.json` 继续作为唯一实际路由源;P00 文档提供页面语义映射。跨模块图片归入 `static/assets/foundation/{opaque,transparent}`,模块专用图片归入 `static/assets/modules/<module>/`。页面只引用语义路径;公共组件仅复用两个及以上已确认页面的稳定结构。
**Tech Stack:** Vue 3、uni-app、SCSS、PowerShell 审计脚本、HBuilderX Android 预览。
## Global Constraints
- Android `uni-app`;不新增依赖、接口、业务功能或未确认页面状态。
- A01、G01 在 412 × 915 的可见结果冻结,迁移后必须回归验证。
- 可见装饰一律为图片:`opaque` 负责完整底色,`transparent` 为 RGBA 叠加;禁止 CSS/SVG 装饰和截图切片。
- 页面路径采用 `<编号>-<语义名>.vue`;旧入口必须在所有引用更新并验证后删除。
- 关键结构、路由、资产图层和复杂逻辑使用中文注释;不得保留无用代码、资产、依赖或兼容入口。
---
### Task 1: 建立可重复的地基审计
**Files:**
- Create: `tests/foundation-structure-audit.ps1`
- Modify: `docs/design/P00_页面结构与资产清单.md`
- Test: `tests/foundation-structure-audit.ps1`
**Interfaces:**
- Consumes: `pages.json``pages[*].path`P00 的目标页面命名规则。
- Produces: 退出码 `0` 表示路由、文件、资产目录和中文文件头满足当前迁移阶段;非 `0` 输出具体缺失路径。
- [ ] **Step 1: 写出会失败的结构审计**
`tests/foundation-structure-audit.ps1` 定义下列检查:
```powershell
$pages = (Get-Content -Raw pages.json | ConvertFrom-Json).pages
foreach ($page in $pages) {
$file = "$($page.path).vue"
if (-not (Test-Path $file)) { throw "缺少路由页面:$file" }
if ($file -match '/(index|list|detail)\.vue$') { throw "最终页面名不允许使用:$file" }
if (-not (Get-Content -Raw $file -Encoding utf8).StartsWith('<!-- 页面编号:')) {
throw "缺少中文页面文件头:$file"
}
}
```
- [ ] **Step 2: 运行并确认当前版本失败**
Run: `powershell -ExecutionPolicy Bypass -File tests/foundation-structure-audit.ps1`
Expected: FAIL,指出当前 `index.vue`、`list.vue` 或缺少中文文件头的路由。
- [ ] **Step 3: 增加迁移阶段允许项**
在同一脚本中把未迁移前的旧路径列为显式 `legacyPaths`,并要求其只在迁移批次开始前存在;迁移完成后删除该数组。检查 `static/assets/foundation/opaque` 和 `static/assets/foundation/transparent` 两个目录存在。
- [ ] **Step 4: 运行审计确认其仅报告真实缺失项**
Run: `powershell -ExecutionPolicy Bypass -File tests/foundation-structure-audit.ps1`
Expected: FAIL,但每项错误都能对应 P00 中的未迁移项目。
### Task 2: 迁移页面文件、路由与中文文件头
**Files:**
- Modify: `pages.json`
- Move: `pages/auth/entry.vue` → `pages/auth/a01-entry.vue`
- Move: `pages/auth/login.vue` → `pages/auth/a02-login.vue`
- Move: `pages/genealogy/index.vue` → `pages/genealogy/g01-my-genealogies.vue`
- Move: `pages/genealogy/create.vue` → `pages/genealogy/g03-create-genealogy.vue`
- Move: `pages/lineage/first-person.vue` → `pages/genealogy/g04-first-ancestor.vue`
- Move: `pages/genealogy/detail.vue` → `pages/genealogy/g05-genealogy-overview.vue`
- Move: `pages/genealogy/search.vue` → `pages/genealogy/g06-search-genealogies.vue`
- Move: `pages/genealogy/applications.vue` → `pages/genealogy/g10-application-review.vue`
- Move: `pages/tree/index.vue` → `pages/tree/t01-tree-overview.vue`
- Move: `pages/member/detail.vue` → `pages/tree/t03-member-profile.vue`
- Move: `pages/family/index.vue` → `pages/family/f01-family-feed.vue`
- Move: `pages/content/editor.vue` → `pages/family/f02-publish-feed.vue`
- Move: `pages/content/list.vue` → `pages/skeleton/content-list-skeleton.vue`
- Move: `pages/notification/index.vue` → `pages/notification/n01-message-center.vue`
- Move: `pages/profile/index.vue` → `pages/profile/m01-profile-home.vue`
- Modify: every file that navigates to one of the 15 paths
- Modify: `docs/design/P00_页面结构与资产清单.md`
- Modify: `tests/a01-a02-ui-contract.ps1`, `tests/g01-visual-contract.ps1`
- Test: `tests/foundation-structure-audit.ps1`, `tests/compile-audit.ps1`, `tests/manifest-json.ps1`
**Interfaces:**
- Consumes: P00 第 2 节的逐项映射。
- Produces: `pages.json` 中每个路径指向一个存在、带中文文件头的语义页面文件。
- [ ] **Step 1: 搜索全部旧路由引用**
Run: `rg -n "pages/(auth/entry|auth/login|genealogy/index|genealogy/create|genealogy/detail|genealogy/search|genealogy/applications|lineage/first-person|content/list|content/editor|tree/index|member/detail|family/index|profile/index|notification/index)" --glob '!unpackage/**'`
Expected: 得到 `pages.json`、Vue 跳转和测试中的完整引用列表;将结果逐项写入 P00 的迁移备注。
- [ ] **Step 2: 移动一个文件并在文件头加入中文身份注释**
每个迁移页面使用固定文件头:
```vue
<!-- 页面编号:A-01;用途:启动/登录引导;视觉基准已由用户验收,重构时不得改变可见效果。 -->
<template>
```
A01、G01 文件头额外注明“视觉冻结”;`content-list-skeleton.vue` 明确注明“功能骨架,不计为设计完成页面”。
- [ ] **Step 3: 在同一批次更新路由和跳转**
`pages.json` 的路径去掉 `.vue` 后必须与新文件完全一致,例如:
```json
{ "path": "pages/auth/a01-entry", "style": { "navigationStyle": "custom" } }
```
所有 `uni.navigateTo`、`uni.reLaunch`、测试断言同步改为新 URL;不留下旧 URL 回退分支。
- [ ] **Step 4: 运行路由审计**
Run: `powershell -ExecutionPolicy Bypass -File tests/foundation-structure-audit.ps1`
Expected: PASS,所有 `pages.json` 路径对应真实 Vue 文件并带中文文件头。
- [ ] **Step 5: 运行现有编译与 manifest 审计**
Run: `powershell -ExecutionPolicy Bypass -File tests/compile-audit.ps1`
Expected: PASS,所有 `pages.json` 路由和本地模块引用均存在。
Run: `powershell -ExecutionPolicy Bypass -File tests/manifest-json.ps1`
Expected: PASS`manifest.json` 仍是有效 JSON。
### Task 3: 建立图片资产清单并迁移 A01/G01 资产
**Files:**
- Create: `static/assets/foundation/opaque/`
- Create: `static/assets/foundation/transparent/`
- Create: `static/assets/modules/genealogy/opaque/`
- Create: `static/assets/modules/genealogy/transparent/`
- Create: `tests/foundation-asset-audit.ps1`
- Modify: `pages/auth/a01-entry.vue`, `pages/auth/a02-login.vue`, `pages/genealogy/g01-my-genealogies.vue`, `components/PageHeader.vue`, `components/AppTabbar.vue`
- Modify: `tests/a01-a02-ui-contract.ps1`, `tests/a01-asset-alpha-audit.ps1`, `tests/g01-asset-alpha-audit.ps1`, `tests/g01-visual-contract.ps1`
- Modify: `docs/design/P00_页面结构与资产清单.md`
- Test: `tests/foundation-asset-audit.ps1`, `tests/a01-asset-alpha-audit.ps1`
**Interfaces:**
- Consumes: 当前已验收 A01G01 图片、D1 的 `opaque``transparent` 规则。
- Produces: 每一项保留图片有唯一语义路径、透明属性与至少一个明确代码引用。
- [ ] **Step 1: 写出会失败的资产审计**
`tests/foundation-asset-audit.ps1` 扫描 `pages/`、`components/`、`pages.json` 和测试内的 `/static/assets/` 路径;对每个路径检查文件存在。脚本还读取 P00 中的资产登记表,拒绝 `-v1`、`-v2`、`-source`、重复语义文件作为最终 `foundation/` 路径。
- [ ] **Step 2: 运行并确认当前版本失败**
Run: `powershell -ExecutionPolicy Bypass -File tests/foundation-asset-audit.ps1`
Expected: FAIL,指出当前资源仍在旧 `backgrounds/`、`icons/` 路径且没有最终登记。
- [ ] **Step 3: 按属性迁移已验收资源**
迁移时不修改像素内容,只改路径和名称:
```text
auth-rice-paper-v1.jpg -> foundation/opaque/auth-page-paper.jpg
auth-ancestral-header-v1.png -> foundation/opaque/auth-header.png
auth-ink-scenery-v1.png -> foundation/transparent/auth-ink-scenery.png
auth-title-cloud-v1.png -> foundation/transparent/auth-title-cloud.png
auth-divider-knot-v1.png -> foundation/transparent/auth-divider-knot.png
auth-button-corner-v1.png -> foundation/transparent/auth-button-frame.png
```
G01 的纸纹、页头、导航底图归 `opaque`;谱印、云纹、功能图标、Tab 图标、分隔纹归 `transparent`。如果某个当前按钮仍依赖 CSS 填色或四角拼接,记录为“待完整按钮图片替代”,不得把它伪装为已完成。
- [ ] **Step 4: 更新所有引用并写中文图层注释**
每个页面的图片层在模板中标明,例如:
```vue
<!-- 不透明纸纹底图:承担页面底色,不能由 CSS 替代。 -->
<image class="paper-background" src="/static/assets/foundation/opaque/auth-page-paper.jpg" mode="aspectFill" />
```
透明 PNG 注释注明叠加的宿主底图。不得新增新图片或改变 A01/G01 的尺寸、层级或 `mode`。
- [ ] **Step 5: 运行资产审计和透明度审计**
Run: `powershell -ExecutionPolicy Bypass -File tests/foundation-asset-audit.ps1`
Expected: PASS,所有代码图片引用存在且最终资产路径没有临时版本名。
Run: `powershell -ExecutionPolicy Bypass -File tests/a01-asset-alpha-audit.ps1`
Expected: PASSA01 中登记为透明的 PNG 保持 RGBA 属性。
### Task 4: 删除无引用旧资产并做视觉回归
**Files:**
- Delete: 资产审计确定为 `无引用删除` 或 `迁移后删除` 的旧文件
- Modify: `docs/design/P00_页面结构与资产清单.md`
- Test: `tests/foundation-asset-audit.ps1`, `tests/a01-a02-ui-contract.ps1`, `tests/compile-audit.ps1`
**Interfaces:**
- Consumes: Task 3 的通过资产审计和 P00 中的去留状态。
- Produces: 不存在无引用旧资产;A01、G01 仍引用唯一的语义资产。
- [ ] **Step 1: 输出删除候选并二次扫描**
Run: `rg -n "/static/assets/" pages components tests pages.json --glob '!unpackage/**'`
Expected: 每一个候选旧文件均不在输出中;若仍有输出,保留文件并回到 Task 3。
- [ ] **Step 2: 删除已确认路径并立即更新 P00**
仅删除 P00 表格中标记为 `无引用删除` 或 `迁移后删除` 的文件;不递归删除目录,不删除用户未确认的模块资产。
- [ ] **Step 3: 运行视觉与编译回归**
Run: `powershell -ExecutionPolicy Bypass -File tests/a01-a02-ui-contract.ps1`
Expected: PASS。
Run: `powershell -ExecutionPolicy Bypass -File tests/compile-audit.ps1`
Expected: PASS。
在 HBuilderX 以 412 × 915 复核 A01、G01:纸纹、页头、按钮、祥云、谱印、导航无断裂、无缺失、无拼接痕迹。
- [ ] **Step 4: 检查差异与文档状态**
Run: `git diff --check`
Expected: PASS。
将 P00 每个页面和资产的处理状态更新为真实结论;仅在上述检查均通过后将总规划的 P-00 标记为完成。
@@ -0,0 +1,159 @@
# G01 v5 Structural Alignment Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Rebuild G01s title slip and list hierarchy, and align its header, hall eave, cloud button, background, and Tabbar to the v5 reference.
**Architecture:** `pages/genealogy/index.vue` owns the current title slip, spacing, lower background, and new cloud asset. `GenealogyCard.vue` owns the two-row list entry. `PageHeader.vue` and `AppTabbar.vue` keep their existing APIs and receive only scoped CSS changes.
**Tech Stack:** uni-app Vue 3, scoped SCSS, local RGBA PNG, PowerShell static contracts.
## Global Constraints
- Android only: use rpx and flexbox; do not add CSS Grid, `gap`, `clip-path`, filters, routes, APIs, or data fields.
- Visual source: `docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`.
- Dynamic values remain Vue text; visible ornaments remain local transparent PNGs.
- At `max-width: 340px`, hide only `更新于`.
- Keep G-01 unchecked until HBuilderX evidence exists at `360 × 800` and `320 × 568`.
---
### Task 1: Add failing structural and asset contracts
**Files:**
- Modify: `tests/g01-visual-contract.ps1`
- Modify: `tests/g01-asset-alpha-audit.ps1`
**Interfaces:** Requires `create-cloud-v2.png`, `.current-summary`, `.card-main`, and `.card-title-row`; rejects the v1 asset and `.card-copy`.
- [ ] **Step 1: Replace the old assertions**
Replace v1 with v2 in both test asset lists. Add this contract block:
```powershell
if ($header -notmatch 'height:\s*164rpx') { throw 'Header height does not match the approved narrow red-head proportion.' }
if ($header -notmatch 'opacity:\s*\.29') { throw 'Header hall line-art is not visible enough for the approved eave layer.' }
if ($tabbar -notmatch 'font-size:\s*30rpx') { throw 'Tabbar label size does not match the approved visual weight.' }
if ($tabbar -notmatch 'width:\s*64rpx') { throw 'Tabbar icon size does not match the approved visual weight.' }
foreach ($token in @('current-summary', 'create-cloud-v2.png')) { if ($page -notmatch $token) { throw "G-01 current panel is missing $token" } }
foreach ($token in @('card-main', 'card-title-row', 'card-detail-row')) { if ($card -notmatch $token) { throw "G-01 list card is missing $token" } }
if ($page -match 'create-cloud-v1\.png' -or $card -match 'card-copy') { throw 'G-01 retains a superseded visual structure.' }
```
- [ ] **Step 2: Verify old code fails**
Run `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`.
Expected: `FAIL` because v2, title-slip/list structure, and new shared sizes do not yet exist.
- [ ] **Step 3: Commit the failing contract**
```powershell
git add tests/g01-visual-contract.ps1 tests/g01-asset-alpha-audit.ps1; git commit -m "test: define G01 v5 structural alignment"
```
### Task 2: Replace the cloud ornament
**Files:**
- Create: `static/assets/icons/action/create-cloud-v2.png`
- Delete: `static/assets/icons/action/create-cloud-v1.png`
- Modify: `pages/genealogy/index.vue`
**Interfaces:** v2 is a 96px RGBA transparent antique-gold horizontal cloud ornament rendered once normally and once with the existing mirror class.
- [ ] **Step 1: Generate and inspect v2**
Create one transparent-background 96px asset: two compact, symmetrical antique-gold Chinese cloud curls joined by a short fine line; no text, shadow, red fill, border, or background. The artwork fills the middle 70% of the canvas and matches the references line weight.
- [ ] **Step 2: Install v2 and remove v1**
Point both button images to `/static/assets/icons/action/create-cloud-v2.png`. Set `.create-action` to `width: 350rpx`, `.create-cloud` to `74rpx × 36rpx`, `.create-action .create-icon` to `48rpx × 48rpx`, and its text to `34rpx`. Run `rg -n 'create-cloud-v1\.png' -g '!unpackage/**'`; expected no output. Remove v1 only after this check.
- [ ] **Step 3: Verify alpha and commit**
Run `powershell -ExecutionPolicy Bypass -File tests\g01-asset-alpha-audit.ps1`; expected `PASS static/assets/icons/action/create-cloud-v2.png transparent corners and visible artwork`.
```powershell
git add static/assets/icons/action/create-cloud-v2.png static/assets/icons/action/create-cloud-v1.png pages/genealogy/index.vue; git commit -m "feat: replace G01 create cloud ornament"
```
### Task 3: Rebuild the current title slip and list rows
**Files:**
- Modify: `pages/genealogy/index.vue`
- Modify: `components/GenealogyCard.vue`
**Interfaces:** Page owns `.current-summary` and full-width `.current-meta`; card owns `.card-main`, `.card-title-row`, and `.card-detail-row`. Props and `select` event stay unchanged.
- [ ] **Step 1: Replace current title-slip hierarchy**
Use this markup around the existing images and text bindings:
```vue
<view class="current-summary"><view class="current-seal"><image class="current-seal-frame" src="/static/assets/icons/genealogy/seal-current-frame-v1.png" mode="scaleToFill" /><text class="current-seal-title">家谱</text></view><text class="current-name">{{ currentGenealogy.name }}</text></view><view class="current-info-divider"></view><view class="current-meta"><view class="current-meta-item"><image class="current-meta-icon" src="/static/assets/icons/common/location-v1.png" mode="aspectFit" /><text class="current-meta-item-text">{{ currentGenealogy.location }}</text></view><view class="current-meta-item"><image class="current-meta-icon" src="/static/assets/icons/common/member-meta-v1.png" mode="aspectFit" /><text class="current-meta-item-text">{{ currentGenealogy.memberCount }} 位成员</text></view><view class="current-meta-item"><image class="current-meta-icon" src="/static/assets/icons/common/admin-v1.png" mode="aspectFit" /><text class="current-meta-item-text">管理员</text></view></view>
```
Delete `.current-copy`. Use `.current-slip { display: block; min-height: 268rpx; padding: 30rpx 34rpx 28rpx; }`, `.current-summary { display: flex; align-items: center; }`, `.current-meta { display: flex; justify-content: space-between; }`, and `.current-meta-item { margin: 0; }`.
- [ ] **Step 2: Replace the lists split columns**
Use this layout after the existing seal:
```vue
<view class="card-main"><view class="card-title-row"><text class="card-name">{{ genealogy.name }}</text><view class="card-side"><text class="card-role">{{ role }}</text><image class="card-chevron" src="/static/assets/icons/common/chevron-right-v2.png" mode="aspectFit" /></view></view><view class="card-detail-row"><view class="card-metas"><view class="card-meta-item"><image class="card-meta-icon" src="/static/assets/icons/common/location-v1.png" mode="aspectFit" /><text class="card-meta">{{ genealogy.location }}</text></view><view class="card-meta-item"><image class="card-meta-icon" src="/static/assets/icons/common/member-meta-v1.png" mode="aspectFit" /><text class="card-meta">{{ genealogy.memberCount }} 位成员</text></view></view><view class="card-updated"><text>更新于 {{ genealogy.updatedAt }}</text></view></view></view>
```
Use `.card-main { display: flex; min-width: 0; flex: 1; flex-direction: column; }`, `.card-title-row { display: flex; align-items: center; justify-content: space-between; }`, and remove the fixed-width `.card-side` column. Preserve `.card-updated { display: none; }` at 340px.
- [ ] **Step 3: Reduce spacing and expand landscape coverage**
Set `.genealogy-content` to `padding: 24rpx 32rpx 176rpx`; set `.page-footer-landscape` to `bottom: 112rpx`, `height: 480rpx`, and `opacity: 0.5`. Reduce only existing shortcut/list margins; do not alter handlers or states.
- [ ] **Step 4: Verify and commit**
Run `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`; expected `PASS G-01 visual contract`.
```powershell
git add pages/genealogy/index.vue components/GenealogyCard.vue; git commit -m "feat: restructure G01 information hierarchy"
```
### Task 4: Calibrate shared shell, document, and verify
**Files:**
- Modify: `components/PageHeader.vue`
- Modify: `components/AppTabbar.vue`
- Modify: `docs/design/G01_我的家谱_设计记录.md`
- Modify: `docs/规划.md`
**Interfaces:** Header events and Tabbar routes remain unchanged; docs record implementation without checking off G-01.
- [ ] **Step 1: Implement narrow header and visible eave**
Set `.page-header--root` to `height: 164rpx`. Set `.header-hall` to `bottom: -2rpx`, `left: -48rpx`, `width: 476rpx`, `height: 166rpx`, and `opacity: .29`. Keep all click targets at least `88rpx` high.
- [ ] **Step 2: Compact the Tabbar while increasing its visual weight**
Set `.app-tabbar` to `height: 112rpx`, `.tab-item` padding to `4rpx 0`, `.tab-icon` to `64rpx × 64rpx`, and `.tab-label` to `30rpx`. Preserve the safe area and active/default assets.
- [ ] **Step 3: Record and run focused checks**
Append a dated record covering the new current slip, list rows, header/eave, compact spacing, landscape, and v2 cloud. Leave G-01 unchecked in `docs/规划.md`. Then run:
```powershell
powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1; powershell -ExecutionPolicy Bypass -File tests\g01-asset-alpha-audit.ps1; powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1; powershell -ExecutionPolicy Bypass -File tests\uni-scss-injection.ps1; powershell -ExecutionPolicy Bypass -File tests\vue3-entry.ps1
```
Expected: five `PASS` results. Capture HBuilderX at `360 × 800` and `320 × 568`; only a direct v5 comparison can complete G-01.
- [ ] **Step 4: Commit**
```powershell
git add components/PageHeader.vue components/AppTabbar.vue docs/design/G01_我的家谱_设计记录.md docs/规划.md; git commit -m "feat: align G01 shared visual shell"
```
## Plan self-review
- Task 1 locks every structural and asset change before runtime edits.
- Task 2 replaces only the mismatched cloud asset and removes its old path.
- Task 3 covers title-slip/list structure, compact spacing, and landscape coverage.
- Task 4 covers header/eave, Tabbar, docs, static checks, and HBuilderX evidence.
@@ -0,0 +1,33 @@
# A-01 / A-02 本地 UI 落地设计
> 状态:用户已选择“完整前端落地,不接接口”。
>
> A-01 的视觉与入口规则唯一归属 [`docs/design/A01_启动登录引导_设计记录.md`](../../design/A01_启动登录引导_设计记录.md)。本文件是 A-01/A-02 本地 UI、路由和无接口交互的唯一实现合同。
## 页面与路由
- 新增 `pages/auth/entry.vue` 作为 A-01,并把它放在 `pages.json` 的第一个页面,成为应用启动入口。
- 保留 `pages/auth/login.vue`,但删除旧的 `appApi``calcMD5`、手机号密码接口和成功重启逻辑;该页改为 A-02。
- A-01 点击“登录”且已主动勾选协议后,使用 `uni.navigateTo` 进入 `/pages/auth/login?agreed=1`
- A-01 未勾选协议时,登录、微信登录、注册账号均只显示“请先阅读并同意相关协议”,不跳转、不改变勾选状态。
- A-01 已勾选协议后,微信登录显示“微信登录功能准备中”,注册账号显示“注册页面准备中”。不创建 A-03/A-04 的伪页面。
## A-01 视觉结构
- 使用现有 `static/assets/backgrounds/auth-ancestral-header-v1.png``auth-rice-paper-v1.jpg``auth-ink-scenery-v1.png` 作为独立认证页背景;使用 `login-outline-v1.png``wechat-licensed-v1.png` 作为本地图标。
- 顺序固定为:朱砂家祠头图、标题“家谱”、副标题“为家族留存可传承的记忆”、朱砂主按钮“登录”、描边按钮“微信登录”、单行文字“还没有账号?注册账号”、未勾选协议行。
- A-01 的运行时布局唯一遵循 `docs/design/A01_启动登录引导_设计记录.md` 中的“412 × 915 运行时校准表”;本文件不再重复或定义按钮与间距数值。
## A-02 双标签登录页
- 页头沿用认证页的朱砂头图与暖宣纸背景,但页面标题为“登录家谱”。
- 默认标签是“账号密码登录”;第二标签是“手机验证码登录”。只有当前标签的表单可见。
- 账号密码表单字段为“账号/手机号”“密码”,并展示“忘记密码”文字入口;手机验证码表单字段为“手机号”“验证码”,展示不可调用接口的“获取验证码”次级按钮。
- 两个标签的主按钮均为“登录”。点击时不发起网络请求:当 A-01 协议状态未通过查询参数传入时提示“请先返回登录引导确认协议”;通过时提示“登录接口待接入”。
- 忘记密码提示“忘记密码页面准备中”。不保留“登录即表示同意”或任何默认同意文案。
## 范围与验证
- 只修改 `pages.json`、新增 `pages/auth/entry.vue`、改造 `pages/auth/login.vue`,并新增针对路由、接口移除与协议文案的 PowerShell 合同审计。
- 不引入依赖、不调用 API、不写入 token、不跳转至家谱首页、不改未设计页面。
- 静态验收覆盖 320 × 568、360 × 640、360 × 800、412 × 915;页面实现后再在 HBuilderX 或 Android 运行环境验收实际布局。
@@ -0,0 +1,21 @@
# A-01 登录入口层级调整设计
> 状态:用户已确认视觉与交互方向;效果稿 v3 已归档,小屏布局验收待完成。
>
> 当前设计事实唯一归属 [`docs/design/A01_启动登录引导_设计记录.md`](../../design/A01_启动登录引导_设计记录.md)。本文件只记录本次确认的调整与其边界,不重复充当设计源。
## 已确认的调整
- A-01 不再并列展示“账号密码登录”“手机验证码登录”“微信登录”“注册账号”四个按钮。
- A-01 保留朱砂红“登录”主按钮与古金描边“微信登录”按钮。
- “还没有账号?注册账号”置于按钮下方,其中“注册账号”是朱砂红文字入口。
- “登录”进入 A-02;A-02 通过“账号密码登录 / 手机验证码登录”双标签承接两种登录方式,默认账号密码登录。
- 微信授权流程仍属 A-03;注册仍属 A-04;忘记密码仍属 A-05。
- 协议复选框默认未勾选。登录、微信、注册三种进入操作均不得默认视为用户同意协议;未勾选时仅提示先确认协议。
## 视觉与范围
- 保留已确认的家祠朱砂红头、暖宣纸、古金云纹、墨褐标题、淡墨建筑与山水背景方向。
- 已归档效果稿 v3`docs/design/screens/A01-启动登录引导-效果稿-v3-文字注册入口.png`
- v1、v2 效果稿保留为历史,不删除、不覆盖,且不能作为当前布局依据。
- 本次只调整设计记录、效果稿与后续验收口径;不改 uni-app 页面、接口、真实登录或 Android 打包。
@@ -0,0 +1,20 @@
# A-01 启动 / 登录引导已确认设计
> 状态:设计方案已于 2026-07-13 获用户确认;尚未完成视觉验收或页面实现。
>
> 唯一需求所有者:[A01 启动登录引导设计记录](../../design/A01_启动登录引导_设计记录.md)。本文件只记录本次审批,不重复定义页面规则。
## 审批结论
- A-01 延续已归档的“家祠卷轴”欢迎页美术锚点。
- 账号密码登录为朱砂主按钮;手机验证码登录、微信登录和独立的注册账号按钮依次位于其下。
- 注册账号入口直达 A-04A-02、A-03、A-05 的职责边界不改变。
- 协议默认不勾选,禁止“登录即同意”。
- 本审批仅启动 A-01 的设计工作;不授权任何 uni-app 页面实现、接口联调、真实登录或 Android 打包。
## 后续关口
1. 生成并归档 A-01 效果稿 v2。
2. 以固定 Android 栅格补齐正常与协议未勾选提示状态。
3. 生成并验收本地透明 PNG 功能资产。
4.`320 × 568``360 × 640``360 × 800``412 × 915` 完成小屏视觉验收后,才可标记 A-01 设计完成。
@@ -0,0 +1,55 @@
# A-01 启动/登录引导页设计
## 目标与边界
A-01 是 Android 用户端首次进入应用时的登录方式选择页。它只提供三个入口:账号密码、手机号验证码、微信登录;不承载输入表单、注册、忘记密码、登录失败或微信授权失败。
本轮只完成视觉与本地交互展示,不接接口、不接真实微信授权、不处理真机状态栏和打包。
## 页面归属与跳转
| A-01 操作 | 本地页面去向 | 说明 |
| --- | --- | --- |
| 账号密码登录 | A-02 | A-02 承载账号、密码输入与忘记密码入口。 |
| 手机号验证码登录 | A-03 | A-03 承载手机号、验证码与未注册承接。 |
| 微信登录 | A-03 | A-03 承载微信授权回跳展示;真实授权留到接口阶段。 |
| A-03 识别为未注册 | A-04 | A-04 承载注册及注册场景的协议确认。 |
因此 A-01 不显示“注册账号”或“忘记密码”,也不展示 A-06 的失败、取消或受限状态。
## 协议同意规则
协议同意必须是用户的主动选择,不能以“登录即表示同意”替代。
1. 页面初始时复选框为空,不预选。
2. 文案固定为“我已阅读并同意《用户协议》与《隐私政策》”;两份协议是独立的朱砂红文字链接。
3. 用户点击任一登录入口而尚未勾选时,停留在 A-01 并给出“请先阅读并同意相关协议”的本地提示;不得跳转、不得视为已同意。
4. 勾选后才允许进入对应的本地下一页。复选框的勾选状态使用朱砂红对勾和古金外圈。
5. A-04 的注册协议确认保留,因为它是注册提交的独立确认,不与 A-01 的登录前同意混为同一个控件。
## 视觉构成
以用户确认的登录效果图为美术锚点,并遵循 D1 的“独立红头/纸纹欢迎背景”规则:
- 顶部:整宽朱砂红祠堂线描,中央小型“谱”印章,下沿为古金海浪纹;不使用 G-01 的顶部导航或底部 Tab。
- 正文:暖白宣纸纹,左侧淡墨亭台、右侧淡竹影,页面尾部才出现低对比山水,不能压住操作和协议文字。
- 标题:中央墨色“家谱”,两侧小云纹;副句为“为家族留存可传承的记忆”,上下用细古金线收束。
- 操作:三个等宽矩形纸签按钮,左右安全边距 `56rpx`;高度不少于 `88rpx`;按钮之间 `24rpx`。主按钮为朱砂实底、暖白账号图标和文字;手机号按钮为纸白底古金双线和手机图标;微信按钮同为纸白金线,仅微信图标保持官方绿色。
- 协议:放在第三按钮之后、底部山水之前。复选框触控区不小于 `88rpx × 88rpx`,文字不低于 `24rpx`,与山水保持足够对比度。
## 资产与数据约束
- 背景继续使用项目内的独立祠堂红头、宣纸、淡山水/竹影资产;不得使用远程背景 URL,也不能把列表页的大山水直接复用为登录背景。
- 微信标识只在该真实入口使用。后续实现时从官方或许可来源取得透明 PNG,保存到 `static/assets/icons/`,记录来源、用途、路径与透明角检查结果;不重绘、不改色。
- A-01 的登录方式、文案、协议勾选和跳转只使用本地静态状态;本轮不读取 `appApi`,不发起请求。
## 验收标准
1. `360 × 800dp` 首屏同时看到红头、标题、三个登录入口和协议勾选。
2. 账号密码为唯一朱砂实底主操作;手机号验证码与微信为同层替代入口。
3. 初始画面没有“登录即同意”、没有预勾选协议、没有注册和忘记密码入口。
4. 未勾选协议点击任一登录入口不跳转;勾选后才按表格进入 A-02 或 A-03。
5. 页面没有底部 Tab、接口加载、真实授权、状态栏兼容处理或 Android 打包改动。
# 已失效历史记录(2026-07-13
> 本文件不再是 A-01 的设计或实现依据。唯一有效的 A-01 归档为 `docs/design/A01_启动登录引导_设计记录.md`;当前工作仍以 `docs/交接记录.md` 的 G-01 验收为准。
@@ -0,0 +1,42 @@
# A01 参考图拆分素材重做设计
## 目标
`docs/design/screens/A01-启动登录引导-栅格验收-412x915.png` 为唯一视觉真值,修正当前 A01 在 412 × 915 运行截图中与参考图不一致的红头、场景、标题、分隔中结和按钮边框。保留既有真实 DOM、协议状态与本地登录跳转,不接接口、不把整张设计图作为页面内容。
## 方案选择
1. 继续使用泛生成装饰素材:已验证会产生粗亮的 L 型角花和错误云纹,拒绝。
2. 从已确认的参考图拆分独立素材并由 DOM 组装:可以保留参考图中的金线、卷角和家祠细节,同时保留按钮、协议和点击逻辑。采用此方案。
3. 直接将 412 × 915 图整页铺底:会破坏响应式与交互,且违背 A01 的既有实现约束,禁止。
## 视觉组件与所有权
| 组件 | 唯一来源 | 页面使用方式 |
| --- | --- | --- |
| 红头与谱印 | 参考图顶部 412 × 139 区域 | `auth-header-reference-v2.png` 作为独立头图;移除额外谱印叠层 |
| 宣纸场景 | 参考图正文的建筑、竹影、山水,清除前景控件后形成独立场景层 | `auth-ink-scenery-v2.png` 覆盖正文,不含标题、按钮或协议文案 |
| 品牌标题及两侧祥云 | 参考图的标题装饰 | 可缩放透明 PNG,保留原有墨褐字形和古金云纹;不依赖运行环境字体 |
| 分隔中结 | 参考图上下分隔线的中心云结 | 单独透明 PNG,保留 CSS 细线 |
| 两类按钮边框 | 参考图中对应按钮的边缘、双描线与四角卷纹 | 主/次按钮各一张透明边框 PNG,按钮底色、图标、文字和点击仍为 DOM |
原有 `auth-button-corner-v1.png``auth-divider-knot-v1.png``auth-title-cloud-v1.png` 是不符合参考图的泛生成资产;本次完成后不得再由 A01 引用。
## 布局与交互
- 保留已接近参考图的固定纵向锚点:红头 139px、标题区起点、两按钮间距、注册入口和协议行位置。
- 标题采用参考字形素材,按钮文字及协议文案保持可读、可点击的 DOM 文本。
- 协议复选框与文案继续为单一水平行,保留现有未同意拦截和 `agreed=1` 跳转。
- 在窄屏时维持纵向滚动和安全区;所有新图层按页面宽度缩放,不写死页面宽度。
## 验收与测试
1. 先更新 A01 合同测试,要求 v2 素材被引用,并明确拒绝 v1 泛生成装饰;运行测试确认失败。
2. 生成或拆分 v2 素材,校验 PNG 尺寸、透明边角和可见像素;更新 `entry.vue` 后使合同与编译审计通过。
3. 在 HBuilderX/Android 获取无浏览器边框的 412 × 915 截图:检查红头底部瓦当、场景位置、标题比例、两个按钮边框与协议行。
4. 复核 320 × 568 可滚动性和协议安全区;未获得两张运行图不得宣称视觉验收通过。
## 范围外
- A02 及后续认证页面、真实登录、接口、Android 打包配置。
- 整页设计图铺底、未授权的字体下载、与本次视觉差异无关的组件重构。
@@ -0,0 +1,24 @@
# A-01 运行时视觉校准设计
## 目标与范围
`docs/design/screens/A01-启动登录引导-栅格验收-412x915.png` 的协议未勾选状态作为唯一视觉基准,把 `pages/auth/entry.vue` 的真实 DOM 校准到同一比例。此次只修正标题区、两条分隔饰线、双按钮、注册入口和协议行;现有认证背景图不在本次视觉校准范围内,接口、路由和 A-02 均不变。
## Contract ownership
`docs/design/A01_启动登录引导_设计记录.md` owns the calibrated 412 × 915 coordinate table and acceptance thresholds. This implementation specification records scope and component strategy only; it does not define duplicate layout values.
## 组件策略
- 页面继续使用真实 DOM 与现有本地认证背景,不使用整图作为页面内容。
- 标题两侧云纹和标题分隔线中心饰物保持独立语义元素;注册链接不得增加设计图中不存在的左右短线。
- 两个按钮共享经过校准的外框尺寸,不使用通用“方括号”角饰;直线边框由 scoped CSS 承担。
- 登录图标保持白色、微信图标保持其许可的原色;按钮文字按参考图缩小并移除全局通用按钮样式造成的额外字重或圆角干扰。
- 视觉常量只定义在 `pages/auth/entry.vue` 的 A-01 样式中;旧的 A-01 实现合同中互相冲突的 `638rpx/108rpx` 说明不再作为运行时布局依据。
## 验收
1. 运行 `tests/a01-a02-ui-contract.ps1`,确认协议与本地路由合同不变。
2. 在 HBuilderX 或 Android 以 `412 x 915` 截取协议未勾选状态,去除浏览器外框后与参考图同尺度叠加比对。
3. 确认红头、标题、两条分隔饰线、按钮四边、注册入口和协议行的误差均不超过约 2px;再复核 `320 x 568` 不会裁切协议或安全区。
4. 没有这两张运行截图,不能声明视觉校准完成。
@@ -0,0 +1,27 @@
# 家谱 APP 地基重整设计
## 目标
在不改变用户已验收 A01、G01 视觉结果的前提下,建立唯一、可维护的页面目录、路由映射、图片资产包、公共组件和中文注释规范,为其余 57 个页面/状态单元提供稳定地基。
## 已确认约束
- Android `uni-app`;不增加依赖、不接新接口、不做额外功能。
- A01、G01 是冻结视觉锚点,必须在 412 × 915 回归验证。
- 可见装饰均使用图片:不透明图片承担完整底色和外观;透明 PNG 仅作为叠加元素;CSS 不绘制装饰。
- 禁止整页截图、局部切片、拼接背景、拼装按钮和保留重复/未引用资产。
- 页面文件采用页面编号和语义名;旧路径只有在所有引用更新并验证后才能删除。
- 所有非直观结构、逻辑、路由和资产图层使用中文注释;实现只覆盖确认需求。
## 架构
`docs/design/P00_页面结构与资产清单.md` 管理页面与资源的映射。`static/assets/foundation/opaque``transparent` 承载跨模块资源,模块专用资源放入 `modules/<module>/``pages.json` 是实际路由唯一来源,页面、跳转和测试与其同轮迁移。
公共组件只沉淀已被两页以上使用且视觉稳定的结构。页面内部无法确认复用的内容不抽象,避免万能组件和预留逻辑。
## 验收
1. 当前 15 个路由在 P00 中均有明确用途和目标文件名。
2. 每个保留图片可追溯用途、透明属性和代码引用;删除前后二次扫描均无引用。
3. A01、G01 在 412 × 915 下没有可见视觉回退。
4. 相关审计、编译检查与 `git diff --check` 通过。
@@ -0,0 +1,60 @@
# G01 v5 视觉对齐设计
> 状态:根据最新预览反馈修订,待确认
> 视觉依据:`docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`
## 目标与边界
将当前 G01「我的家谱」页面的运行预览校正至 v5 参考稿的比例、信息层级和对比度。本轮只处理可见的正常态页面,不改变路由、交互、`data/mock.js` 字段或远程接口;仅为“新建家谱”更换一个参考稿匹配的透明祥云 PNG。
涉及文件仅限:
- `pages/genealogy/index.vue`
- `components/GenealogyCard.vue`
- `components/PageHeader.vue`
- `components/AppTabbar.vue`
- `static/assets/icons/action/create-cloud-v2.png`
- `tests/g01-visual-contract.ps1`
- `docs/design/G01_我的家谱_设计记录.md`
- `docs/规划.md`
## 方案选择
1. 只微调字号和间距:改动最少,但无法让当前题签的元信息跨整行排列,也无法解除列表字段的截断。
2. **结构重排加比例校正(采用)**:将当前题签改为两层信息结构、列表改为主内容两行结构,再对页头、列表、按钮、底栏与背景做 CSS 尺寸校正;保留现有数据与路由。
3. 重新绘制整套 PNG:可进一步调整所有图标墨色,但改动范围大;本轮只重绘当前页面确实不匹配的祥云 PNG。
## 视觉设计
### 页头、屋檐与底栏
- 根页头从当前比例收窄;Logo、标题与通知仍保留各自安全区,标题置于缩窄后红头的视觉中线。
- 放大现有 `header-hall-lineart.png` 的覆盖面积并提高不透明度,使屋檐在页头左下可辨识,同时不遮挡 Logo 和标题;不新增祠堂背景资产。
- Tabbar 收紧容器高度,同时放大 Tab 图标和标签,使其接近参考稿的安全区内视觉重量。
### 当前家谱题签与页面间距
- 将题签分成上方“谱印 + 名称”和下方“分隔线 + 三组元信息”两层;元信息横跨题签底部并均匀分布,而不是挤在名称右侧。
- 保留现有谱印框、地点/成员/管理员 PNG 与动态文本;不嵌入文字到图片中。
- 恢复可见的细金线分隔,提升元信息及快捷入口的墨色对比和可读尺寸。
- 内容区水平边距从当前偏宽的值缩小,题签、列表和分隔线使用同一对齐线;快捷入口与列表之间只保留参考稿需要的留白。
### 列表、新建按钮与背景
- 列表从“左侧内容列 + 独立右侧列”改为单一主内容区:第一行是 `标题 / 角色 / 箭头`,第二行是 `地点 / 成员数 / 更新于`。这让日期可借用角色下方空间,360px 宽度完整显示,320px 时仅隐藏日期。
- 收窄新建按钮,放大“+ 新建家谱”与两侧云纹的可见面积。以独立的 `create-cloud-v2.png` 替换当前与参考稿不一致的 `create-cloud-v1.png`;新资产为紧凑 RGBA 透明 PNG,并在确认没有其他消费者后移除旧资产。
- 扩大底部山水竹影的覆盖范围与可见层次,但保持它在文字和点击区域之下。
## 验收与验证
1. 先为题签两层结构、列表两行结构、新祥云 PNG 与关键尺寸写 G01 静态契约,并确认旧实现失败。
2. 生成、检查并接入 `create-cloud-v2.png`;确认四角透明、有效图形紧凑,且旧祥云 PNG 没有剩余消费者。
3. 修改组件后运行 `g01-visual-contract.ps1``g01-asset-alpha-audit.ps1``compile-audit.ps1``uni-scss-injection.ps1``vue3-entry.ps1`
4. 在 HBuilderX 的 `360 × 800` 重新截图,并同 v5 参考稿逐区比对:页头屋檐、当前题签、快捷入口、两条列表、新建按钮和 Tabbar。
5. 再检查 `320 × 568`:不重叠、不截断、不被底栏遮挡。截图验收完成前,`G-01` 保持未完成状态。
## 自检
- 不含占位项或未定义的视觉来源。
- 组件职责不变:页面负责当前题签和新建按钮,卡片负责完整的两行列表条目,页头和 Tabbar 保持独立。
- 不保留旧的独立 `.card-side` 列或旧祥云 PNG 引用;320px 降级规则只隐藏日期,其他参考稿字段仍保留。
+84 -79
View File
@@ -1,105 +1,110 @@
# 家谱 APP 交接记录 # 家谱 APP 交接记录
> 更新时间:2026-07-13 > 更新时间:2026-07-13
> 用途:更换电脑或更换 GPT 后,先读本文件即可恢复当前工作上下文。 > 用途:更换电脑或更换 GPT 后,本文件恢复项目上下文。
> 保密说明:账号、密码、令牌和其他敏感信息不记录在本文件 > 当前阶段:P-00 地基重整收尾,**不要跳到 A02**
## 1. 新接手者先什么 ## 1. 新接手者先什么
按以下顺序阅读,不要跳过: 1. 根目录 `AGENTS.md`:最小修改、先验证、不得覆盖未知改动。
2. [总规划](规划.md):59 个页面的范围、当前状态和推进顺序。
3. [D1 安卓视觉地基规范](design/D1_安卓视觉规范与页面壳.md):视觉、资产和注释硬规则。
4. [P00 页面结构与资产清单](design/P00_页面结构与资产清单.md):页面重命名映射、资产去留和清理进度。
5. A01、G01 设计记录与下列冻结锚点:
- `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png`
- `docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`
1. 根目录 `AGENTS.md`:项目工作约束,要求先确认依据、最小修改、修改后验证。 ## 2. 当前事实
2. `docs/规划.md`:全项目范围、完成状态与推进顺序。
3. `docs/design/D1_安卓视觉规范与页面壳.md`Android 视觉、兼容性、PNG 资产与背景规则。
4. `docs/design/G01_我的家谱_设计记录.md`G01 已完成的施工与尚未验收项。
5. `docs/superpowers/specs/2026-07-12-g01-reference-typography-design.md``docs/superpowers/plans/2026-07-12-g01-information-hierarchy.md`G01 的已确认规格与执行记录。
视觉唯一基准: - 项目:Android `uni-app`,使用 HBuilderX;不做 iOS、H5、PC、后台或官网。
- 全量规划是 59 个页面/状态单元;`pages.json` 当前只有 15 个已注册路由。
- 用户已通过视觉验收的只有两个页面:A01 启动/登录引导、G01 我的家谱。
- A01、G01 是冻结视觉基准:可以抽取和整理内部实现,但不得改变用户已验收的可见效果。
- 其余页面现有代码只算功能骨架或本地 UI,不能标记为视觉完成。
- 已完成:15 个路由的语义化文件/跳转迁移、35 项在用资产迁入统一目录、24 项运行时零引用旧资产删除、公共页面/组件中文用途注释。
- A01 主/次完整按钮候选已生成并保存到项目,但比例尚不符合现有设计,尚未接入页面;当前第一优先级仍是 P-00 收尾:复核/重做候选,验收后替换旧角标拼接,再做 A01、G01 截图回归;不是继续 A02,也不是接接口。
- `docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png` ## 3. 用户明确确认的规则
不要把“红色中国风”当作完成标准;G01 需要按此图逐区、逐字段、逐比例比对 - 以 A01、G01 已完成样式为项目地基;不能推翻或另起一套画风
- 资产先行:先形成一套可复用、已验收的资产包,再按资产包设计后续页面。
- 所有可见装饰使用图片资产;不使用 CSS/SVG 绘制祥云、边框、分隔纹、按钮边角等可见装饰。
- 不透明图片承担自身底色、纹理、完整按钮/卡片/页头背景;透明 PNG 只承担 Logo、图标、祥云、纹样、边框等叠加元素。
- 禁止把整页设计图或局部截图裁切后拼进页面;禁止按钮由角标、边线和 CSS 底色拼接。
- 页面、路由、资产都必须用语义名和页面编号;旧文件、重复版本和未引用资产确认后删除。
- 关键 JS、Vue 结构、资产图层与关键样式必须有中文注释;注释说明用途和约束,不堆砌无意义逐行注释。
- JS 和页面代码只做已确认的需求:不发散、不预埋猜测功能、不新增无关依赖、不保留无用代码。
## 2. 项目与范围 ## 4. P-00 应按什么顺序执行
- 项目:uni-app,目标为 Android,使用 HBuilderX 打包 1. 运行 `git status --short`,保留未知改动;不得使用 `git reset --hard` 或覆盖用户文件
- 当前阶段:先完成全部页面视觉设计和小屏验收;**暂不恢复接口联调、真实账号联调或 Android 打包** 2. 依据 P00 文档核对 15 个当前路由的页面编号和目标语义名
- 当前正在处理:`G-01 我的家谱` 3. 扫描所有 `/static/assets/` 引用,填写每项资产的用途、透明属性、引用位置与去留状态
- 当前不做:iOS、PC 管理端、官网,以及新增业务接口 4. 建立新的 `foundation/opaque``foundation/transparent``modules/<module>` 资产结构和资产清单
- 本地展示数据的唯一来源:`data/mock.js`G01 当前不调用远程 API 5. 同轮更新 `pages.json`、跳转路径、测试和页面文件名;确认无引用后才删除旧路径和旧资产
- 本目录当前不是 Git 仓库;不要擅自初始化仓库、重置文件或删除既有用户文件 6. 为 A01 生成并单独验收两张完整不透明按钮图片,替换现有 CSS 底色加四角 PNG 的过渡实现;不得裁切设计图,也不得改变当前画风
7. 运行最小相关审计、编译检查、A01/G01 四尺寸截图核对和 `git diff --check`;视觉是否通过以用户换电脑后的设计图复核为准。
## 3. 用户已确认的视觉规则 ## 5. 当前停点(下次从这里继续)
- 风格是“家祠卷轴”:朱砂页头、暖宣纸、古金细线、墨褐文字、淡墨山水/竹影;禁止绿色页面背景。 本次已经完成且通过回归:
- 需要使用的图标、谱印框与云纹必须是项目内的透明 PNG;不能用文字、emoji、CSS 图形或网络图片替代可见图标。
- 参照图中的布局、字号、字段、图标可见面积和留白需要尽量一比一还原;不要只做“差不多”的国风页面。
- 要兼容老款 Android:用 rpx、Flex、边距与基础圆角;避免 CSS Grid、`flex gap``clip-path`、复杂滤镜和大面积动画。
- 每次完成一个可验收子项,更新 `docs/规划.md` 和对应设计记录;没有 HBuilderX 同尺寸截图确认时,不能勾选整个页面完成。
## 4. G01 当前实际状态 - 15 个已注册路由改为“页面编号-语义名”文件和 URL;旧入口已移除。
- 35 项运行时资产进入 `static/assets/foundation``static/assets/modules/genealogy`,并按 `opaque``transparent` 分类。
- 24 项经运行时引用扫描确认无引用的旧副本已删除。
- 页面和 3 个公共组件均补上中文用途/图层注释。
- 已通过 `foundation-structure-audit``foundation-asset-audit`、A01/A02、G01、核心流程、编译、清单、SCSS、Vue 入口以及 `git diff --check` 回归。
### 已施工并通过静态检查 当前没有改动 A01、G01 的可见布局。A01 仍使用旧的“CSS 底色 + 四角 PNG”按钮过渡实现,因此不能称为地基最终验收完成。
- 朱砂顶部、项目 Logo、条件式通知红点、宣纸底纹、淡墨底图、底部 Tabbar。 为避免下次电脑丢失进度,已把两张未接入候选放进项目:
- 当前家谱题签、四个快捷入口、分隔金线、创建/加入列表、新建按钮。
- 透明 PNG 资产已接入:快捷入口、Tab、通知、箭头、元信息图标、谱印框和新建云纹。
- 当前演示字段已统一为参考稿:
- 当前家谱:`汤氏家谱 / 河南·洛阳 / 158 位成员 / 管理员`
- 已加入家谱:`汤氏宗谱 / 山东·济宁 / 286 位成员 / 成员`
- 当前题签有标题/元信息分隔线;列表右侧角色和箭头应为同一水平基线;日期为“更新于”文字,不显示参考稿中没有的日历图标。
### 必须继续做的事 | 文件 | 来源与状态 | 下次处理 |
| --- | --- | --- |
| `static/assets/foundation/opaque/a01-primary-button-candidate.png` | 图像生成工具生成,1983×793,完整朱砂按钮皮肤;未接入 | 比例约 2.5:1,与现有按钮约 3.6:1 不一致;不能裁切设计图或硬拉伸,需按设计图重新生成/确认。 |
| `static/assets/foundation/opaque/a01-secondary-button-candidate.png` | 图像生成工具生成,1983×793,完整宣纸按钮皮肤;未接入 | 与主按钮同样先重做或确认比例、角纹、金边,再进入页面。 |
1. 用 HBuilderX 刷新 G01,并使用与参考稿相同的手机预览尺寸截屏。 下次严格按此顺序继续:
2. 将新截图与 v5 参考稿并排比对,重点检查:
- 顶部家谱题签的字体、字段、分隔线和三组元信息是否同一行;
- 四个快捷图标的实际可见面积与文字大小;
- 两条列表的谱印、标题、地点/成员、日期、右侧角色和箭头基线;
- 新建按钮的云纹、文字、底部山水和 Tabbar 安全区。
3. 有任何差异时,只改对应 G01 组件并重新截图;不要提前勾选 `G-01`
4. 最终还要按 `320 × 568``360 × 800` 做小屏检查,确认无重叠、无裁切、无底部遮挡。
## 5. G01 关键文件 1. 打开 A01 设计图和当前候选,先确认按钮的目标比例、边框粗细、四角纹样和红/宣纸颜色;候选不合格就重新生成,不接入。
2. 两张完整不透明按钮资产分别通过图片尺寸、完整四边框、无截图裁切和设计图人工比对后,才替换 A01 的 CSS 背景、边框及 `auth-button-corner.png` 四角节点。
3. 更新 A01 资产台账和最小测试;运行页面契约、资产审计、编译检查。
4. 在 320 × 568、360 × 640、360 × 800、412 × 915 截图;A01、G01 都须与设计图再次并排验收。用户可撤销此前“通过”,以新的设计图复核为准。
5. 用户确认 P-00 后,才开始 A02;后续每个模块先定完整资产包和母版,再批量实现页面,禁止边做边换画风。
| 作用 | 文件 | ## 6. 现有路由概况
| --- | --- |
| G01 页面与当前题签/快捷入口/新建按钮 | `pages/genealogy/index.vue` |
| G01 列表项 | `components/GenealogyCard.vue` |
| 顶部标题栏/Logo/通知点 | `components/PageHeader.vue` |
| 底部导航 | `components/AppTabbar.vue` |
| 展示数据 | `data/mock.js` |
| 视觉变量 | `styles/tokens.scss``styles/global.scss` |
| G01 图标 | `static/assets/icons/` |
| G01 背景 | `static/assets/backgrounds/` |
| G01 契约检查 | `tests/g01-visual-contract.ps1` |
| 新 PNG 透明检查 | `tests/g01-asset-alpha-audit.ps1` |
## 6. 已知技术注意项 当前路由为:
- `tests/g01-visual-contract.ps1` 用 UTF-8 Base64 形式检查中文展示字段。这是为了避免 Windows PowerShell 5.1 将无 BOM UTF-8 脚本中的中文字符串误读为乱码;不要把该段直接改回裸中文字面量。 ```text
- `calendar-v1.png` 已因为参考稿没有该图标而删除;不要再次接入日期图标。 pages/auth/a01-entry
- G01 的背景资产应继续使用 `footer-mountain-bamboo.png`,不要换回实心灰山的 `footer-mountain-bamboo-v2.png` pages/auth/a02-login
- 题签透明角已经过检查;若再次出现白角,优先检查题签容器是否重新加上不透明的矩形底色。 pages/genealogy/g01-my-genealogies
- 所有新 PNG 在接入前都要检查:RGBA、四角透明、主体没有大量空白、显示尺寸符合 24dp/设计稿。 pages/genealogy/g03-create-genealogy
pages/genealogy/g05-genealogy-overview
## 7. 复核命令 pages/genealogy/g06-search-genealogies
pages/genealogy/g10-application-review
在项目根目录的 PowerShell 中运行: pages/genealogy/g04-first-ancestor
pages/skeleton/content-list-skeleton
```powershell pages/family/f02-publish-feed
powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1 pages/tree/t01-tree-overview
powershell -ExecutionPolicy Bypass -File tests\g01-asset-alpha-audit.ps1 pages/tree/t03-member-profile
powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1 pages/family/f01-family-feed
powershell -ExecutionPolicy Bypass -File tests\uni-scss-injection.ps1 pages/profile/m01-profile-home
powershell -ExecutionPolicy Bypass -File tests\vue3-entry.ps1 pages/notification/n01-message-center
``` ```
最近一次结果:五项均为 `PASS`。这只证明代码和资产契约通过,**不替代 HBuilderX 视觉验收** 这些路径已完成当前 15 个路由的语义化迁移;逐项状态与资产台账见 P00 文档。不要新增无编号的 `index.vue``list.vue``detail.vue` 作为最终页面
## 8. 换电脑操作 ## 7. 运行与验证
1. 完整复制整个 `jiapuapp` 项目文件夹,保留 `docs/``static/``tests/``data/` 和所有 Vue/SCSS 文件 - 现有测试位于 `tests/`;改动前后优先执行受影响页面的审计脚本
2. 新电脑安装 HBuilderX 后,直接“打开目录”选择复制后的项目根目录 - A01 相关:`tests/a01-a02-ui-contract.ps1``tests/a01-asset-alpha-audit.ps1``tests/a01-design-record-audit.ps1`
3. 将本文件和新的 HBuilderX 截图一起发给新的 GPT,并说明:“先读 `docs/交接记录.md``AGENTS.md`,继续 G01 的视觉验收,不要改接口。” - 公共检查:`tests/foundation-structure-audit.ps1``tests/foundation-asset-audit.ps1``tests/compile-audit.ps1``tests/manifest-json.ps1``git diff --check`
4. 新 GPT 修改后,先运行第 7 节命令,再由用户在 HBuilderX 提供同尺寸截图复核 - 截图验收尺寸:320 × 568、360 × 640、360 × 800、412 × 915A01、G01 必须至少复核 412 × 915
## 8. 已知工作区注意事项
- 工作区可能带有用户未提交修改。只处理当前任务涉及文件;提交时只暂存本轮文件。
- 历史 `docs/superpowers/*a01*reference-asset-rebuild*` 与旧 G01 设计记录中存在已废弃的路径和“截图/切片式”尝试;不能作为当前实施依据。
- 当前依据永远以本文件、`docs/规划.md`、D1、P00 和用户最新消息为准;发生冲突时,以用户最新确认优先。
+116 -171
View File
@@ -1,207 +1,152 @@
# 规划 # 家谱 APP 总规划
> 电脑或 GPT 更换时,先阅读:[项目交接记录](交接记录.md)。 > 电脑或 GPT 更换时,先阅读:[项目交接记录](交接记录.md)。
> 状态:执行中 > 当前阶段:P-00 地基重整(结构与现有资产已迁移;A01 完整按钮候选已生成、待设计图复核与截图回归)
> 建立日期:2026-07-11 > 适用端:Android `uni-app`HBuilderX 打包)
> 唯一适用端:Android `uni-app`HBuilderX 打包) > 页面总量:59 个规划页面/状态单元;当前已注册 15 个路由,两者不是同一口径。
> 执行原则:**先完成全部页面设计,再开始逐页实现;每完成一项,立即在本文件勾选。**
## 1. 本轮边界 ## 1. 本轮目标与边界
- 只做手机 Android 用户端;不做 iOS、H5、PC 管理端、后台管理端官网。 - 只做 Android 用户端;不做 iOS、H5、PC 管理端、后台管理端官网。
- 范围覆盖思维导图中的 Android 用户端:登录、家谱、家族、消息、我的,以及必要的空白、加载、异常、权限状态 - 先完成项目地基:页面目录、路由映射、资产包、公共组件、中文注释和验收规则;地基通过前不继续 A02
- 现有 uni-app 页面与接口代码保留为临时功能骨架,但**不计入任何页面设计完成项**,也不再决定最终视觉样式 - 已由用户验收的 A01 启动/登录引导页与 G01 我的家谱页是**冻结视觉基准**。可重整其内部目录和复用方式,但 412 × 915 的可见效果不得改变
- 在“设计验收”完成前,暂停新增业务接口、真实账号联调和打包工作 - 现有的 15 个路由只是临时功能骨架;它们不等于 59 个页面均已设计或完成
- 接口暂未提供的视频、邀请码、多管理员分级等功能:本轮可完成其页面视觉和无数据状态;真实功能接入留到接口阶段 - 不新增业务接口、真实账号联调、Android 打包或设计清单以外的功能
## 2. 视觉方向(已确定) ## 2. 唯一依据与完成口径
### 2.1 风格名称 1. 本文件管理全项目范围、页面状态和推进顺序。
2. [D1 安卓视觉地基规范](design/D1_安卓视觉规范与页面壳.md)管理视觉、资产、组件和代码约束。
3. [P00 页面结构与资产清单](design/P00_页面结构与资产清单.md)管理路由命名、资产去留和清理结果。
4. [项目交接记录](交接记录.md)记录换电脑后应从哪里继续。
**家祠卷轴**:喜庆而庄重。以宣纸的温度承载家族记忆,以朱砂表达礼序和重要操作,以古金表达传承;避免网红化、过度卡片化和大面积高饱和红色。 页面只有同时满足以下条件才可标记完成:
### 2.2 颜色与材质 - 页面编号、功能、正常态和必要异常态明确。
- 使用已验收资产包,没有临时图片、截图切片、CSS 装饰或无引用资源。
- 在 320 × 568、360 × 640、360 × 800、412 × 915 下完成对应检查。
- 用户确认视觉结果;对应设计记录、规划和交接记录同步更新。
| 用途 | 色彩 | 规则 | ## 3. 已确认视觉地基
| --- | --- | --- |
| 页面底色 | 宣纸暖白 `#F6F0E5` | 不使用绿色背景。 |
| 主操作 / 选中 | 朱砂 `#A33B2B` | 仅用于主按钮、当前状态、关键提醒。 |
| 标题 / 主要文字 | 墨褐 `#342A24` | 不使用偏绿的深色。 |
| 细分隔 / 装饰 | 古金 `#B58A4B` | 只做细线、印记、等级,不做大色块。 |
| 容器 | 纸白 `#FFF9EE` | 用轻边线或极浅阴影分层。 |
| 次级文字 | 暖灰褐 `#7A6958` | 保持正文可读性。 |
### 2.3 固定组件规则 - 风格:家祠卷轴——暖宣纸、朱砂、古金、墨褐、淡墨山水;禁止绿色页面背景和“差不多的国风”。
- 冻结锚点:
- A01[412 × 915 栅格验收图](design/screens/A01-启动登录引导-栅格验收-412x915.png)
- G01[紧凑版设计稿](design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png)
- 视觉资产全部使用图片文件:不透明图片负责自身底色/纹理,透明 PNG 负责叠加装饰、Logo 与图标。CSS 只负责布局、文字和状态,不绘制可见装饰。
- 主按钮、次按钮、卡片、页头等有完整边框和底色的元素必须使用完整不透明图片;禁止四角、边线和 CSS 底色拼接。
- 不把整页设计图或其局部裁切后直接塞进页面;复杂背景使用完整背景图片,不能断裂或拼接。
- 顶部为简洁的家祠题签式标题栏;可回退页面保留清晰返回区,不能让装饰挤压标题。 ## 4. 目录、资产与代码硬规则
- 底部导航固定为“家谱 / 家族 / 我的”,通知作为右上角入口,不单独占 Tab。
- 家谱树中的人物使用竖式人物牌位;姓名、字辈、关系与在世状态层次清楚,不能用装饰遮挡信息。
- 所有功能图标均使用透明背景 PNG,统一由 `static/icons/` 管理;不用网络图标字体,不混用不同画风。
- 图片内容优先使用用户上传的真实照片;没有照片时使用统一的纸纹占位图,不使用 AI 人像。
## 3. Android 老机型与小屏兼容(必须验收) ### 4.1 页面与路由
### 3.1 尺寸与排版 - 页面文件使用“`页面编号-语义名.vue`”,例如 `a01-entry.vue``g01-my-genealogies.vue``t01-tree-overview.vue`
- `pages.json`、跳转 URL、测试与页面文件必须同轮更新;旧入口确认无引用后删除,不保留兼容副本。
- 当前路由到目标文件的映射只以 [P00 页面结构与资产清单](design/P00_页面结构与资产清单.md) 为准。
-`320 × 568``360 × 640` 为关键小屏;`360 × 800``412 × 915` 为常规屏进行核对。 ### 4.2 资产
- 布局使用流式纵向滚动;不以单一固定页面高度承载内容,不让底部按钮压住表单。
- 一行中可能出现长姓名、长家谱名、长地址时必须截断或换行,不允许文字重叠。
- 关键正文不小于 `24rpx`,主要按钮高度不小于 `88rpx`,所有可点击区域不小于约 `44dp`
- 双列内容在 `320px` 宽度下自动折为单列;人物树、照片墙等横向内容提供明确的横向滚动区域与边缘提示。
- 顶部、底部均预留系统状态栏和 Android 三键/手势导航空间;无安全区能力的设备也要有保底内边距。
### 3.2 性能与技术限制 - 资产目录按 `foundation|modules``opaque|transparent`、模块和语义名管理;不再使用含义不明的 `v1``v2``source`、重复文件名作为长期资产。
- 每项资产必须记录:路径、用途、所属组件、透明属性、显示尺寸、引用页面和去留状态。
- 删除前必须用代码引用扫描确认未被页面、组件、配置、测试或文档作为运行时资源引用;不能按文件名猜测。
- 所有新增可见资产先单独验收,再允许放入页面;资产未通过不能通过页面验收。
- 不使用全屏大尺寸背景图、视频背景、复杂滤镜、毛玻璃和持续动画。 ### 4.3 JavaScript、Vue 与中文注释
- 不依赖 `flex gap`、复杂 CSS Grid 或只在新 WebView 支持的视觉效果;以 Flex、边距和基础圆角为主。
- 首屏图标为小尺寸透明 PNG;列表图片按缩略图加载,避免一次性渲染大量高清图。
- 家谱树按层级或视区呈现,不在低端机一次渲染无边界的全部成员节点。
- 表单、弹窗、图片预览都要可关闭、可返回,并在键盘弹起时保持提交按钮可见。
### 3.3 兼容验收方式 - 只实现已确认页面、交互和数据状态;不增加推测功能、预留接口、万能工具函数、无用变量或无关依赖。
- 组件只有在至少两个已确认页面共享同一结构和行为时才抽取;否则保留在页面内。
- 每个页面和组件文件开头注明编号/用途;模板按背景、页头、主体、操作和状态分段写中文注释。
- 非直观跳转、状态分支、资产图层和关键样式必须写中文注释,说明用途和约束;不为显而易见的单行属性堆无意义注释。
- 每轮改动后删除本轮产生的无用导入、变量、函数、路径和资产。
- 每个页面设计完成后,先按 `320 × 568` 检查:标题、长文案、表单、底部操作、横向内容五项均不得重叠。 ## 5. 59 个页面/状态单元
- 全部页面实现后,以 Android 真机或模拟器覆盖上述四组尺寸,记录在本文件“最终验收”中。
- 这里的“兼容”指页面尺寸、性能和交互可用性;具体最低 Android 系统版本在最终 HBuilderX 打包配置阶段按实际 SDK 再确认。
## 4. 页面设计的完成标准 | 模块 | 范围 | 数量 | 当前状态 |
| --- | --- | ---: | --- |
| 账户与首次使用 | A-01 至 A-06 | 6 | A01 已验收;其余未完成 |
| 家谱 | G-01 至 G-12 | 12 | G01 已验收;其余未完成 |
| 世系树与成员 | T-01 至 T-08 | 8 | 未完成 |
| 家族内容 | F-01 至 F-10 | 10 | 未完成 |
| 人物、礼仪与档案 | R-01 至 R-11 | 11 | 未完成 |
| 消息 | N-01 至 N-02 | 2 | 未完成 |
| 个人中心 | M-01 至 M-10 | 10 | 未完成 |
| **合计** | | **59** | **已验收 2 个** |
一页只有同时满足以下四项,才能标记为“设计完成”: ### 5.1 账户与首次使用
1. 有明确的正常态布局、字体层级、颜色、图标、间距与操作层级 - [x] A-01 启动/登录引导:登录、微信登录、注册入口、协议勾选
2. 有该页需要的空数据、加载、失败、无权限或审核中状态 - [ ] A-02 账号密码登录:账号密码/手机验证码双标签
3. 标明每个主要点击入口的去向、返回路径和确认操作 - [ ] A-03 手机验证码登录与微信授权回跳状态
4. 已按 `320 × 568` 小屏规则检查,无重叠、无被底部导航遮挡的内容 - [ ] A-04 注册与服务协议确认
- [ ] A-05 忘记密码/重设密码。
- [ ] A-06 账号受限、登录失败、未完成注册、微信授权取消/失败。
## 5. 全量页面清单与完成状态 ### 5.2 家谱
### 5.1 设计基础与通用状态 - [x] G-01 我的家谱列表:已创建、已加入、通知入口。
- [ ] G-02 无家谱空状态。
- [ ] G-03 创建家谱表单。
- [ ] G-04 创建成功后的始祖录入。
- [ ] G-05 家谱总览。
- [ ] G-06 公开家谱搜索与筛选。
- [ ] G-07 搜索结果与家谱简介。
- [ ] G-08 申请加入、提交成功与审核中。
- [ ] G-09 我的申请记录。
- [ ] G-10 管理员申请审核与确认。
- [ ] G-11 家谱设置、公开范围与访问说明。
- [ ] G-12 字辈诗列表、详情与维护。
- [x] P-00 锁定 Android 端范围、家祠卷轴视觉方向与透明 PNG 图标规则。 ### 5.3 世系树与成员
- [x] P-01 锁定老机型 / 小屏适配原则与验收尺寸。
- [x] P-02 设计系统:颜色、字号、间距、圆角、按钮、标签、列表、表单、弹窗、空状态。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
- [x] P-03 公共页面壳:顶部题签、返回栏、三 Tab、右上角通知入口、安全区与底部操作栏。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
- [x] P-04 通用状态:首次加载、列表加载、网络失败、空数据、无权限、审核中、删除确认、操作成功提示。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
- [ ] P-05 生成并验收项目 Logo、导航、通知与公共操作的透明 PNG 图标资产。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
- [ ] P-06 生成并验收祠堂红头、宣纸纸纹、山水 / 竹影等项目内背景资产。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
### 5.2 账户与首次使用 - [ ] T-01 世系树总览。
- [ ] T-02 小屏提示、无成员与加载失败状态。
- [ ] T-03 成员档案详情。
- [ ] T-04 新增直系亲属。
- [ ] T-05 编辑成员资料。
- [ ] T-06 编辑亲属关系与冲突提示。
- [ ] T-07 成员目录与搜索。
- [ ] T-08 去世、隐私隐藏和无权限等状态。
- [ ] A-01 启动 / 登录引导页。 ### 5.4 家族内容
- [ ] A-02 账号密码登录页。
- [ ] A-03 手机验证码登录页。
- [ ] A-04 注册页与服务协议确认。
- [ ] A-05 忘记密码 / 重设密码页。
- [ ] A-06 账号受限、登录失败与未完成注册状态。
### 5.3 家谱:创建、加入与管理入口 - [ ] F-01 家族首页/动态流。
- [ ] F-02 发布动态。
- [ ] F-03 动态详情、评论、点赞与删除确认。
- [ ] F-04 谱文分类与文章列表。
- [ ] F-05 谱文详情。
- [ ] F-06 新建/编辑谱文。
- [ ] F-07 相册列表。
- [ ] F-08 相册详情与照片墙。
- [ ] F-09 图片预览、上传与失败状态。
- [ ] F-10 视频列表、详情与接口未开放状态。
- [x] G-01 信息层级与谱印施工:元信息图标、谱印框、快捷入口、列表双层信息和新建云纹已实现并通过资产/编译审计;待 HBuilderX 同尺寸截图验收,故完整 G-01 仍未勾选。 ### 5.5 人物、礼仪与档案
- [ ] G-01 我的家谱列表(已创建、已加入、搜索入口、通知入口)。
- [ ] G-02 无家谱空状态(创建家谱 / 搜索加入)。
- [ ] G-03 创建家谱表单(名称、地域、简介、公开范围)。
- [ ] G-04 创建成功后的始祖录入页。
- [ ] G-05 家谱总览(名称、编号、地域、成员数、管理员、公开状态、快捷入口)。
- [ ] G-06 公开家谱搜索与筛选页。
- [ ] G-07 家谱搜索结果与家谱简介页。
- [ ] G-08 申请加入表单、提交成功与审核中页。
- [ ] G-09 我的申请记录页。
- [ ] G-10 管理员申请审核列表与审核确认页。
- [ ] G-11 家谱设置、公开范围与成员访问说明页。
- [ ] G-12 字辈诗列表、详情与管理员维护页。
### 5.4 世系树、成员与亲属关系 - [ ] R-01 人物录列表;R-02 人物录详情与编辑。
- [ ] R-03 贺礼列表;R-04 贺礼新增、详情与删除确认。
- [ ] R-05 礼仪活动列表;R-06 礼仪详情;R-07 新建/编辑礼仪。
- [ ] R-08 成长日志;R-09 人生事;R-10 备忘录;R-11 功德记录。
- [ ] T-01 世系树总览(缩放、横向移动、分支切换、当前人物定位)。 ### 5.6 消息与个人中心
- [ ] T-02 世系树小屏提示与无成员 / 数据加载失败状态。
- [ ] T-03 成员档案详情(基本资料、代际、关系、照片、人生记录入口)。
- [ ] T-04 新增直系亲属页(父母、配偶、子女的不同上下文)。
- [ ] T-05 编辑成员资料页。
- [ ] T-06 编辑亲属关系页与关系冲突提示。
- [ ] T-07 成员目录与搜索页。
- [ ] T-08 成员去世、隐私隐藏、无编辑权限等特殊状态。
### 5.5 家族:动态、谱文、相册与影像 - [ ] N-01 消息中心;N-02 消息详情、已读与空状态。
- [ ] M-01 我的首页;M-02 个人资料;M-03 安全设置;M-04 修改密码;M-05 修改手机号。
- [ ] M-06 帮助中心;M-07 意见反馈;M-08 应用推广;M-09 VIP 与订单;M-10 关于、协议、隐私与退出确认。
- [ ] F-01 家族首页 / 家族动态流。 ## 6. 推进顺序
- [ ] F-02 发布动态页(文字、图片、可见范围)。
- [ ] F-03 动态详情、评论、点赞与删除确认页。
- [ ] F-04 谱文分类与文章列表页。
- [ ] F-05 谱文详情页。
- [ ] F-06 新建 / 编辑谱文页。
- [ ] F-07 相册列表页。
- [ ] F-08 相册详情与照片墙页。
- [ ] F-09 图片预览、上传照片与上传失败状态。
- [ ] F-10 视频列表、详情、发布入口与“接口尚未开放”状态。
### 5.6 家族:人物、礼仪与档案 1. **P-00 地基重整(当前)**:已完成页面映射、现有资产迁移、公共组件中文注释和运行时零引用旧资源清理;A01 两张完整按钮候选已保存但尚未接入。下次先按设计图复核候选比例和纹样,通过后才替换旧角标拼接实现,再回归 A01、G01。
2. A-02 至 A-06:在冻结的 A01 认证资产包上完成账户模块。
3. G-02 至 G-12:在冻结的 G01 列表、题签、导航资产包上完成家谱模块。
4. T、F、R、N、M 模块:每个模块先确认母版和完整资产包,再批量实现其页面/状态。
5. 全量四尺寸复核、真实接口接入与 Android 打包。
- [ ] R-01 人物录列表页。 ## 7. 本轮完成前的验收
- [ ] R-02 人物录详情与新增 / 编辑页。
- [ ] R-03 贺礼列表页。
- [ ] R-04 新增贺礼、贺礼详情与删除确认页。
- [ ] R-05 礼仪活动列表(婚礼、升学、生日、其他)。
- [ ] R-06 礼仪详情页。
- [ ] R-07 新建 / 编辑礼仪活动页。
- [ ] R-08 成长日志列表、详情与新增 / 编辑页。
- [ ] R-09 人生事列表、详情与新增 / 编辑页。
- [ ] R-10 备忘录列表、详情与新增 / 编辑页。
- [ ] R-11 功德记录列表、详情与新增 / 编辑页。
### 5.7 消息与个人中心 - 页面路由、文件名与 P00 映射表一致;不存在不明 `index.vue``detail.vue``list.vue` 作为最终页面名。
- 所有保留资产都能说明用途;所有删除资产均通过未引用扫描。
- [ ] N-01 消息中心(生日、贺礼、礼仪、应用通知、点赞评论、推广反馈分类) - A01、G01 在 412 × 915 截图与当前用户验收基准无可见回退
- [ ] N-02 消息详情、已读状态与空消息状态 - 编译检查、页面/资产审计和 `git diff --check` 通过
- [ ] M-01 我的首页(头像、身份、所属家谱、快捷入口) - 规划、交接、设计记录和资产清单同步反映真实状态
- [ ] M-02 个人资料查看 / 编辑页。
- [ ] M-03 安全设置页。
- [ ] M-04 修改密码页。
- [ ] M-05 修改手机号页。
- [ ] M-06 帮助中心与搜索页。
- [ ] M-07 意见反馈页(文字、图片、联系方式)。
- [ ] M-08 应用推广页。
- [ ] M-09 VIP 权益、套餐与订单状态页。
- [ ] M-10 关于我们、服务协议、隐私政策与退出登录确认页。
## 6. 设计推进顺序(开发前必须全部完成)
| 阶段 | 交付内容 | 状态 |
| --- | --- | --- |
| D1 | P-02 至 P-06:统一设计系统、Android 页面壳、公共图标与背景资产 | 进行中(P-02 至 P-04 已完成;P-05、P-06 资产生成中) |
| D2 | A-01 至 A-06:账户与首次使用全套页面 | 未开始 |
| D3 | G-01 至 G-12:家谱创建、加入、总览与管理全套页面 | 进行中(G-01 视觉壳已施工,待 HBuilderX 小屏预览验收;其余页面未开始) |
| D4 | T-01 至 T-08:世系树、成员、关系编辑全套页面 | 未开始 |
| D5 | F-01 至 F-10、R-01 至 R-11:家族内容全套页面 | 未开始 |
| D6 | N-01 至 N-02、M-01 至 M-10:消息与个人中心全套页面 | 未开始 |
| D7 | 全量小屏复核、视觉一致性复核、页面流程复核 | 未开始 |
| D8 | 逐页实现 uni-app、接入接口、Android 打包验证 | 未开始(D7 完成后才可开始) |
## 7. 页面流转主线(设计时必须保持一致)
1. 登录 → 我的家谱为空 → 创建家谱 → 录入始祖 → 家谱总览 → 世系树 → 成员档案 / 新增亲属。
2. 登录 → 搜索公开家谱 → 查看简介 → 申请加入 → 审核中 → 管理员审核 → 进入家谱总览。
3. 家族 Tab → 动态 / 谱文 / 相册 / 人物录 / 礼仪 / 档案 → 详情 → 发布或编辑(取决于权限)。
4. 任一页面的通知入口 → 消息中心 → 相应业务详情。
5. 我的 → 个人资料 / 安全 / 帮助 / 反馈 / VIP / 关于与协议。
## 8. 本轮执行纪律
- 每次只推进一个设计阶段;阶段内的每个页面完成后,更新本文件对应复选框。
- 未有高保真设计和小屏检查的页面,不得开始其业务开发。
- 已确认的视觉规范是代码还原依据;若实现与设计不一致,以设计为准修正实现。
- 参考图是视觉锚点;核心材质、层级、比例、选中效果必须逐项一比一核对,不能只保留“红色中国风”的笼统印象。
- 设计稿不得保留无业务意义的横线、破折号或模拟字符;颜色差异必须有可见且明确的业务含义。
- 后续每一个页面新增的功能图标,必须同步生成、保存并验收透明 PNG 实体资产;未生成资产的页面不得标记为“设计完成”。
- 功能图标必须遵循线上成熟标准的 24dp 语义、留白、触控与选中规则,并导出为透明 PNG;项目可在此基础上保留已确认的古金双线、轻云纹收口等家祠风格。当前从大图裁切的试验资产不得引用,需按单图标标准重新导出后验收。
- 大部分按钮、顶部操作和快捷入口图标采用已确认的古金双线、轻云纹收口风格;底部 Tab 使用简洁同色系版本。每一页必须指定并使用已生成的背景资产组合,未完成背景设计的页面不得标记为“设计完成”。
- 全页 AI 生成图可作为页面的美术效果锚点,但不能单独作为布局验收依据;页面必须同时按固定 Android 栅格、真实数据、真实 PNG 资产制作。已生成的“我的家谱”效果图保留其朱砂、宣纸、题签、山水和庄重层级,留白、Logo、占位文案与图标标准问题按设计栅格修正后才可计入完成项。
- 新增需求先写入本文件,再决定是否纳入本轮,避免在页面开发中临时扩张范围。
## 9. 最终验收(暂未开始)
- [ ] 所有 P / A / G / T / F / R / N / M 页面均有设计完成标记。
- [ ] 全部设计在 `320 × 568` 下无内容重叠、无遮挡、无不可点操作。
- [ ] 所有图标均为透明背景 PNG,且无绿色页面背景。
- [ ] 页面风格与“家祠卷轴”设计一致,代码页面与确认设计逐屏比对通过。
- [ ] 全量页面设计确认后,才解锁 uni-app 实现、接口联调和 Android 打包。
+5
View File
@@ -9,6 +9,11 @@
"usingComponents": true, "usingComponents": true,
"nvueStyleCompiler": "uni-app", "nvueStyleCompiler": "uni-app",
"compilerVersion": 3, "compilerVersion": 3,
"statusbar": {
"immersed": "supportedDevice",
"style": "light",
"background": "#B52E22"
},
"splashscreen": { "splashscreen": {
"alwaysShowBeforeRender": true, "alwaysShowBeforeRender": true,
"waiting": true, "waiting": true,
+18 -14
View File
@@ -1,59 +1,63 @@
{ {
"pages": [ "pages": [
{ {
"path": "pages/auth/login", "path": "pages/auth/a01-entry",
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false } "style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
}, },
{ {
"path": "pages/genealogy/index", "path": "pages/auth/a02-login",
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false } "style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
}, },
{ {
"path": "pages/genealogy/create", "path": "pages/genealogy/g01-my-genealogies",
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
},
{
"path": "pages/genealogy/g03-create-genealogy",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/genealogy/detail", "path": "pages/genealogy/g05-genealogy-overview",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/genealogy/search", "path": "pages/genealogy/g06-search-genealogies",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/genealogy/applications", "path": "pages/genealogy/g10-application-review",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/lineage/first-person", "path": "pages/genealogy/g04-first-ancestor",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/content/list", "path": "pages/skeleton/content-list-skeleton",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/content/editor", "path": "pages/family/f02-publish-feed",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/tree/index", "path": "pages/tree/t01-tree-overview",
"style": { "navigationStyle": "custom", "disableScroll": true } "style": { "navigationStyle": "custom", "disableScroll": true }
}, },
{ {
"path": "pages/member/detail", "path": "pages/tree/t03-member-profile",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/family/index", "path": "pages/family/f01-family-feed",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/profile/index", "path": "pages/profile/m01-profile-home",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
}, },
{ {
"path": "pages/notification/index", "path": "pages/notification/n01-message-center",
"style": { "navigationStyle": "custom" } "style": { "navigationStyle": "custom" }
} }
], ],
+342
View File
@@ -0,0 +1,342 @@
<!-- 页面编号A-01用途启动登录引导视觉基准已由用户验收地基迁移不得改变可见效果 -->
<template>
<view class="auth-page entry-page">
<!-- 不透明纸纹底图承担页面底色透明山水只作为上层装饰 -->
<image class="paper-background" src="/static/assets/foundation/opaque/auth-page-paper.jpg" mode="aspectFill" />
<image class="scenery-background" src="/static/assets/foundation/transparent/auth-ink-scenery.png" mode="aspectFill" />
<view class="auth-content">
<view class="auth-header">
<image class="header-background" src="/static/assets/foundation/opaque/auth-header.png" mode="aspectFill" />
<image class="header-seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
</view>
<view class="brand-intro">
<view class="title-decoration">
<image class="title-cloud title-cloud-left" src="/static/assets/foundation/transparent/auth-title-cloud.png" mode="aspectFit" />
<text class="brand-title">家谱</text>
<image class="title-cloud title-cloud-right" src="/static/assets/foundation/transparent/auth-title-cloud.png" mode="aspectFit" />
</view>
<view class="brand-divider brand-divider--top">
<view class="brand-divider__line" />
<image class="brand-divider__ornament" src="/static/assets/foundation/transparent/auth-divider-knot.png" mode="aspectFit" />
<view class="brand-divider__line" />
</view>
<text class="brand-subtitle">为家族留存可传承的记忆</text>
<view class="brand-divider brand-divider--bottom">
<view class="brand-divider__line" />
<image class="brand-divider__ornament" src="/static/assets/foundation/transparent/auth-divider-knot.png" mode="aspectFit" />
<view class="brand-divider__line" />
</view>
</view>
<view class="entry-actions">
<view class="entry-button entry-button--primary" hover-class="entry-button--pressed" @click="openLogin">
<image class="button-corner-asset button-corner-asset--top-left" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<image class="button-corner-asset button-corner-asset--top-right" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<image class="button-corner-asset button-corner-asset--bottom-left" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<image class="button-corner-asset button-corner-asset--bottom-right" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<view class="entry-button__content">
<image class="entry-button__icon" src="/static/assets/foundation/transparent/auth-login-outline.png" mode="aspectFit" />
<text>登录</text>
</view>
</view>
<view class="entry-button entry-button--secondary" hover-class="entry-button--pressed" @click="prepareWechatLogin">
<image class="button-corner-asset button-corner-asset--top-left" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<image class="button-corner-asset button-corner-asset--top-right" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<image class="button-corner-asset button-corner-asset--bottom-left" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<image class="button-corner-asset button-corner-asset--bottom-right" src="/static/assets/foundation/transparent/auth-button-corner.png" mode="aspectFit" />
<view class="entry-button__content">
<image class="entry-button__icon entry-button__icon--wechat" src="/static/assets/foundation/transparent/auth-wechat.png" mode="aspectFit" />
<text>微信登录</text>
</view>
</view>
<view class="register-link" hover-class="link-hover" @click="prepareRegister">
<text>还没有账号<text class="register-emphasis">注册账号</text></text>
</view>
</view>
<view class="agreement-row" @click="toggleAgreement">
<view class="agreement-check" :class="{ checked: agreed }">
<text v-if="agreed"></text>
</view>
<view class="agreement-copy">
<text>我已阅读并同意</text>
<text class="agreement-link" @click.stop="prepareAgreement">用户协议</text>
<text></text>
<text class="agreement-link" @click.stop="prepareAgreement">隐私政策</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const agreed = ref(false)
const toggleAgreement = () => {
agreed.value = !agreed.value
}
const requireAgreement = () => {
if (agreed.value) return true
uni.showToast({ title: '请先阅读并同意相关协议', icon: 'none' })
return false
}
const openLogin = () => {
if (!requireAgreement()) return
uni.navigateTo({ url: '/pages/auth/a02-login?agreed=1' })
}
const prepareWechatLogin = () => {
if (!requireAgreement()) return
uni.showToast({ title: '微信登录功能准备中', icon: 'none' })
}
const prepareRegister = () => {
if (!requireAgreement()) return
uni.showToast({ title: '注册页面准备中', icon: 'none' })
}
const prepareAgreement = () => {
uni.showToast({ title: '协议页面准备中', icon: 'none' })
}
</script>
<style scoped lang="scss">
.auth-page {
position: relative;
min-height: 100vh;
background: #f7f1e6;
}
.paper-background,
.scenery-background {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.paper-background { opacity: 0.96; }
.scenery-background { opacity: 0.82; }
.auth-content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
box-sizing: border-box;
min-height: 100vh;
padding-bottom: calc(44rpx + env(safe-area-inset-bottom));
}
.auth-header {
position: relative;
height: 253rpx;
overflow: hidden;
}
.header-background {
width: 100%;
height: 100%;
}
.header-seal {
position: absolute;
top: 70rpx;
left: 50%;
width: 136rpx;
height: 136rpx;
transform: translateX(-50%);
}
.brand-intro {
display: flex;
flex-direction: column;
align-items: center;
padding: 150rpx 56rpx 0;
color: #3f2c1d;
}
.title-decoration {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.brand-title {
margin: 0 12rpx;
font-family: STKaiti, KaiTi, serif;
font-size: 112rpx;
font-weight: 700;
letter-spacing: 12rpx;
line-height: 1;
}
.title-cloud { width: 64rpx; height: 40rpx; }
.title-cloud-left { margin-right: 0; }
.title-cloud-right { margin-left: 0; transform: scaleX(-1); }
.brand-divider {
display: flex;
align-items: center;
justify-content: center;
width: 426rpx;
}
.brand-divider--top { margin: 47rpx 0 21rpx; }
.brand-divider--bottom { margin: 34rpx 0 30rpx; }
.brand-divider__line {
flex: 1;
height: 1rpx;
background: #d5a34f;
}
.brand-divider__ornament {
width: 44rpx;
height: 24rpx;
margin: 0 14rpx;
}
.brand-subtitle {
color: #624631;
font-family: STKaiti, KaiTi, serif;
font-size: 36rpx;
letter-spacing: 4rpx;
}
.entry-actions {
margin: 95rpx 110rpx 0;
}
.entry-button {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 100%;
height: 118rpx;
border: 2rpx solid #d8ad63;
border-radius: 6rpx;
font-family: STKaiti, KaiTi, serif;
font-size: 38rpx;
letter-spacing: 4rpx;
}
.entry-button::after {
position: absolute;
inset: 6rpx;
border: 1rpx solid rgba(225, 180, 105, 0.92);
border-radius: 2rpx;
content: '';
pointer-events: none;
z-index: 1;
}
.button-corner-asset {
position: absolute;
z-index: 2;
width: 54rpx;
height: 54rpx;
pointer-events: none;
}
.button-corner-asset--top-left { top: 4rpx; left: 4rpx; }
.button-corner-asset--top-right { top: 4rpx; right: 4rpx; transform: scaleX(-1); }
.button-corner-asset--bottom-left { bottom: 4rpx; left: 4rpx; transform: scaleY(-1); }
.button-corner-asset--bottom-right { right: 4rpx; bottom: 4rpx; transform: scale(-1); }
.entry-button--primary {
border-color: #9e170e;
background: #b7170d;
color: #fffaf0;
}
.entry-button--primary::after { border-color: #edc67c; }
.entry-button--secondary {
margin-top: 51rpx;
background: rgba(255, 252, 245, 0.82);
color: #60442f;
}
.entry-button__icon {
width: 46rpx;
height: 46rpx;
margin-right: 18rpx;
}
.entry-button__icon--wechat { width: 52rpx; }
.entry-button--primary .entry-button__icon { filter: brightness(0) invert(1); }
.entry-button__content {
position: relative;
z-index: 3;
display: flex;
align-items: center;
transform: translateX(-20rpx);
}
.entry-button--pressed { opacity: 0.84; }
.register-link {
display: flex;
align-items: center;
justify-content: center;
margin-top: 98rpx;
color: #60442f;
font-family: STKaiti, KaiTi, serif;
font-size: 30rpx;
letter-spacing: 2rpx;
white-space: nowrap;
}
.register-emphasis { color: #b7170d; }
.link-hover { opacity: 0.65; }
.agreement-row {
display: flex;
align-items: center;
justify-content: center;
margin-top: 141rpx;
padding: 0 44rpx calc(32rpx + env(safe-area-inset-bottom));
color: #6b5a4a;
font-size: 20rpx;
line-height: 30rpx;
}
.agreement-check {
display: flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 30rpx;
height: 30rpx;
margin: 0 12rpx 0 0;
border: 2rpx solid #b88d4b;
border-radius: 50%;
color: #fffaf0;
font-size: 20rpx;
line-height: 1;
}
.agreement-check.checked { background: #b7170d; border-color: #b7170d; }
.agreement-copy {
display: flex;
align-items: center;
line-height: 30rpx;
text-align: center;
white-space: nowrap;
}
.agreement-link { color: #b7170d; }
</style>
+321
View File
@@ -0,0 +1,321 @@
<!-- 页面编号A-02用途账号登录当前仅保留已确认的本地登录界面 -->
<template>
<view class="auth-page login-page">
<!-- A01 共用的登录页底图层先不透明纸纹后透明山水 -->
<image class="paper-background" src="/static/assets/foundation/opaque/auth-page-paper.jpg" mode="aspectFill" />
<image class="scenery-background" src="/static/assets/foundation/transparent/auth-ink-scenery.png" mode="aspectFill" />
<view class="auth-content">
<view class="auth-header">
<image class="header-background" src="/static/assets/foundation/opaque/auth-header.png" mode="aspectFill" />
<image class="header-seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
</view>
<view class="login-panel">
<view class="page-heading">
<view class="back-button" hover-class="back-hover" @click="goBack"></view>
<view>
<text class="page-title">登录家谱</text>
<text class="page-subtitle">欢迎回到家族记忆</text>
</view>
</view>
<view class="login-tabs">
<view class="login-tab" :class="{ active: activeTab === 'password' }" @click="activeTab = 'password'">
<text>账号密码登录</text>
</view>
<view class="login-tab" :class="{ active: activeTab === 'sms' }" @click="activeTab = 'sms'">
<text>手机验证码登录</text>
</view>
</view>
<view v-if="activeTab === 'password'" class="form-content">
<view class="input-row">
<text class="input-label">账号</text>
<input v-model.trim="account" class="auth-input" placeholder="请输入账号或手机号" placeholder-class="placeholder" />
</view>
<view class="input-row">
<text class="input-label">密码</text>
<input v-model="password" class="auth-input" password placeholder="请输入密码" placeholder-class="placeholder" />
</view>
<view class="secondary-action" hover-class="text-hover" @click="prepareForgotPassword">忘记密码</view>
</view>
<view v-else class="form-content">
<view class="input-row">
<text class="input-label">手机</text>
<input v-model.trim="smsPhone" class="auth-input" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="placeholder" />
</view>
<view class="input-row code-row">
<text class="input-label">验证码</text>
<input v-model.trim="verificationCode" class="auth-input" type="number" maxlength="6" placeholder="请输入验证码" placeholder-class="placeholder" />
<view class="get-code" hover-class="text-hover" @click="prepareGetCode">获取验证码</view>
</view>
</view>
<view class="login-submit" hover-class="button-hover" @click="submitLogin">登录</view>
<text class="agreement-reminder">请先在登录引导页阅读并确认协议</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const activeTab = ref('password')
const account = ref('')
const password = ref('')
const smsPhone = ref('')
const verificationCode = ref('')
const agreedFromEntry = ref(false)
onLoad((options) => {
agreedFromEntry.value = options?.agreed === '1'
})
const goBack = () => {
uni.navigateBack()
}
const prepareForgotPassword = () => {
uni.showToast({ title: '忘记密码页面准备中', icon: 'none' })
}
const prepareGetCode = () => {
uni.showToast({ title: '获取验证码功能待接入', icon: 'none' })
}
const canSubmit = () => {
if (agreedFromEntry.value) return true
uni.showToast({ title: '请先返回登录引导确认协议', icon: 'none' })
return false
}
const submitLogin = () => {
if (!canSubmit()) return
if (activeTab.value === 'password') {
if (!account.value) {
uni.showToast({ title: '请输入账号或手机号', icon: 'none' })
return
}
if (!password.value) {
uni.showToast({ title: '请输入密码', icon: 'none' })
return
}
} else {
if (!/^1\d{10}$/.test(smsPhone.value)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!verificationCode.value) {
uni.showToast({ title: '请输入验证码', icon: 'none' })
return
}
}
uni.showToast({ title: '登录接口待接入', icon: 'none' })
}
</script>
<style scoped lang="scss">
.auth-page {
position: relative;
min-height: 100vh;
background: #f7f1e6;
}
.paper-background,
.scenery-background {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.paper-background { opacity: 0.96; }
.scenery-background { opacity: 0.76; }
.auth-content {
position: relative;
z-index: 1;
box-sizing: border-box;
min-height: 100vh;
padding-bottom: calc(44rpx + env(safe-area-inset-bottom));
}
.auth-header {
position: relative;
height: 196rpx;
overflow: hidden;
}
.header-background {
width: 100%;
height: 100%;
}
.header-seal {
position: absolute;
top: 34rpx;
left: 50%;
width: 124rpx;
height: 124rpx;
transform: translateX(-50%);
}
.login-panel {
margin: 68rpx 56rpx 0;
padding: 46rpx 38rpx 54rpx;
border: 2rpx solid #d6ad6c;
border-radius: 10rpx;
background: rgba(255, 252, 246, 0.78);
box-shadow: inset 0 0 0 5rpx rgba(255, 255, 255, 0.55);
}
.page-heading {
display: flex;
align-items: center;
}
.back-button {
display: flex;
align-items: center;
justify-content: center;
width: 48rpx;
height: 48rpx;
margin-right: 20rpx;
border: 1rpx solid #d7ae70;
border-radius: 50%;
color: #9d5b28;
font-size: 52rpx;
font-weight: 300;
line-height: 40rpx;
}
.back-hover { opacity: 0.62; }
.page-title,
.page-subtitle {
display: block;
}
.page-title {
color: #3f2c1d;
font-family: STKaiti, KaiTi, serif;
font-size: 46rpx;
font-weight: 700;
letter-spacing: 4rpx;
}
.page-subtitle {
margin-top: 8rpx;
color: #8c7864;
font-size: 22rpx;
letter-spacing: 2rpx;
}
.login-tabs {
display: flex;
margin-top: 54rpx;
border-bottom: 1rpx solid #dcc29a;
}
.login-tab {
position: relative;
flex: 1;
padding: 0 0 24rpx;
color: #89715a;
font-family: STKaiti, KaiTi, serif;
font-size: 29rpx;
text-align: center;
}
.login-tab.active {
color: #a7160c;
font-weight: 700;
}
.login-tab.active::after {
position: absolute;
right: 28rpx;
bottom: -1rpx;
left: 28rpx;
height: 4rpx;
border-radius: 4rpx;
background: #b7170d;
content: '';
}
.form-content { margin-top: 26rpx; }
.input-row {
display: flex;
align-items: center;
min-height: 96rpx;
border-bottom: 1rpx solid #e5d4b7;
}
.input-label {
width: 92rpx;
color: #674b35;
font-family: STKaiti, KaiTi, serif;
font-size: 30rpx;
}
.auth-input {
flex: 1;
height: 88rpx;
color: #3f2c1d;
font-size: 28rpx;
}
.placeholder { color: #b3a895; }
.code-row .auth-input { min-width: 0; }
.get-code {
flex: 0 0 auto;
padding-left: 20rpx;
border-left: 1rpx solid #ddc7a3;
color: #a7160c;
font-size: 25rpx;
}
.secondary-action {
margin-top: 28rpx;
color: #a7160c;
font-size: 25rpx;
text-align: right;
}
.text-hover { opacity: 0.64; }
.login-submit {
display: flex;
align-items: center;
justify-content: center;
height: 100rpx;
margin-top: 60rpx;
border: 2rpx solid #9e170e;
border-radius: 8rpx;
background: #b7170d;
box-shadow: inset 0 0 0 4rpx #d69b5f;
color: #fffaf0;
font-family: STKaiti, KaiTi, serif;
font-size: 36rpx;
letter-spacing: 6rpx;
}
.button-hover { opacity: 0.84; }
.agreement-reminder {
display: block;
margin-top: 26rpx;
color: #9a8773;
font-size: 22rpx;
text-align: center;
}
</style>
-84
View File
@@ -1,84 +0,0 @@
<template>
<view class="page-shell login-page">
<view class="ceremony-top">
<text class="seal-word"></text>
<text class="eyebrow">家脉绵延 · 生生不息</text>
<text class="brand-title">家谱</text>
<text class="brand-subtitle">为家族留存可传承的记忆</text>
</view>
<view class="login-paper">
<view class="section-title">登录家谱</view>
<text class="form-note">使用已注册手机号和密码登录</text>
<text class="form-label">手机号</text>
<input v-model="phone" class="line-input" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="placeholder" />
<text class="form-label">密码</text>
<input v-model="password" class="line-input" password placeholder="请输入密码" placeholder-class="placeholder" />
<text v-if="errorMessage" class="error-message">{{ errorMessage }}</text>
<button class="primary-button login-button" :loading="isSubmitting" :disabled="isSubmitting" @click="login">进入家谱</button>
<text class="login-tip">短信登录将在验证码服务联调后开放</text>
<text class="terms">登录即表示同意用户协议隐私政策</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { appApi } from '@/utils/api.js'
import { calcMD5 } from '@/utils/md5.js'
const phone = ref('')
const password = ref('')
const isSubmitting = ref(false)
const errorMessage = ref('')
const login = async () => {
if (!/^1\d{10}$/.test(phone.value)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!password.value) {
uni.showToast({ title: '请输入登录密码', icon: 'none' })
return
}
isSubmitting.value = true
errorMessage.value = ''
try {
await appApi.loginWithPassword({ phone: phone.value, passwordHash: calcMD5(password.value) })
uni.reLaunch({ url: '/pages/genealogy/index' })
} catch (error) {
errorMessage.value = error.message || '登录失败,请稍后重试'
} finally {
isSubmitting.value = false
}
}
</script>
<style scoped lang="scss">
.login-page { padding-top: 108rpx; }
.ceremony-top {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 40rpx 76rpx;
background: $brand-red;
color: #fff9ed;
}
.seal-word { display: flex; width: 66rpx; height: 66rpx; align-items: center; justify-content: center; box-sizing: border-box; border: 2rpx solid #f1d494; border-radius: 50%; color: #f5ddaa; font-family: serif; font-size: 37rpx; }
.eyebrow { margin-top: 24rpx; color: #f8dfad; font-size: 22rpx; letter-spacing: 5rpx; }
.brand-title { margin-top: 24rpx; font-family: serif; font-size: 68rpx; font-weight: 700; letter-spacing: 16rpx; }
.brand-subtitle { margin-top: 18rpx; color: #f5deae; font-size: 26rpx; letter-spacing: 3rpx; }
.login-paper { margin: -28rpx 28rpx 0; padding: 48rpx 42rpx; border: 1rpx solid $gold-soft; border-radius: 24rpx; background: $paper-white; }
.form-note { display: block; margin-top: 14rpx; color: $ink-muted; font-size: 23rpx; }
.form-label { display: block; margin-top: 40rpx; color: $ink-muted; font-size: 25rpx; }
.line-input { height: 78rpx; border-bottom: 1rpx solid $gold-soft; color: $ink; font-size: 30rpx; }
.placeholder { color: #b3a895; }
.error-message { display: block; margin-top: 22rpx; color: $brand-red; font-size: 23rpx; line-height: 1.5; }
.login-button { margin-top: 48rpx; }
.login-tip { display: block; margin-top: 24rpx; color: $ink-muted; font-size: 22rpx; text-align: center; }
.terms { display: block; margin-top: 16rpx; color: #a39785; font-size: 21rpx; text-align: center; }
</style>
@@ -1,3 +1,4 @@
<!-- 页面编号F-01用途家族动态首页 -->
<template> <template>
<view class="page-shell family-page"> <view class="page-shell family-page">
<view class="family-top"><view><text class="top-kicker">家族共建</text><text class="page-name">家族记事</text></view><text class="publish" @click="publishFeed">发布动态</text></view> <view class="family-top"><view><text class="top-kicker">家族共建</text><text class="page-name">家族记事</text></view><text class="publish" @click="publishFeed">发布动态</text></view>
@@ -30,10 +31,10 @@ onMounted(async () => {
if (!genealogyId.value) { loadError.value = '请先从“我的家谱”选择一个家谱。'; return } if (!genealogyId.value) { loadError.value = '请先从“我的家谱”选择一个家谱。'; return }
try { feeds.value = await appApi.getFeeds(genealogyId.value) } catch (error) { loadError.value = error.message || '家族动态加载失败。' } try { feeds.value = await appApi.getFeeds(genealogyId.value) } catch (error) { loadError.value = error.message || '家族动态加载失败。' }
}) })
const openSection = (type) => uni.navigateTo({ url: `/pages/content/list?type=${type}&genealogyId=${genealogyId.value}` }) const openSection = (type) => uni.navigateTo({ url: `/pages/skeleton/content-list-skeleton?type=${type}&genealogyId=${genealogyId.value}` })
const publishFeed = () => { const publishFeed = () => {
if (!genealogyId.value) { uni.showToast({ title: '请先选择家谱', icon: 'none' }); return } if (!genealogyId.value) { uni.showToast({ title: '请先选择家谱', icon: 'none' }); return }
uni.navigateTo({ url: `/pages/content/editor?type=feed&genealogyId=${genealogyId.value}` }) uni.navigateTo({ url: `/pages/family/f02-publish-feed?type=feed&genealogyId=${genealogyId.value}` })
} }
</script> </script>
@@ -1,3 +1,4 @@
<!-- 页面编号F-02用途发布家族动态 -->
<template> <template>
<view class="page-shell editor-page"> <view class="page-shell editor-page">
<PageHeader title="发布家族动态" /> <PageHeader title="发布家族动态" />
@@ -1,13 +1,14 @@
<!-- 页面编号G-01用途我的家谱列表视觉基准已由用户验收地基迁移不得改变可见效果 -->
<template> <template>
<view class="page-shell genealogy-index"> <view class="page-shell genealogy-index">
<image <image
class="page-paper-texture" class="page-paper-texture"
src="/static/assets/backgrounds/paper-rice-texture-v2.jpg" src="/static/assets/foundation/opaque/page-paper.jpg"
mode="scaleToFill" mode="scaleToFill"
/> />
<image <image
class="page-footer-landscape" class="page-footer-landscape"
src="/static/assets/backgrounds/footer-mountain-bamboo.png" src="/static/assets/foundation/transparent/footer-mountain-bamboo.png"
mode="scaleToFill" mode="scaleToFill"
/> />
<PageHeader <PageHeader
@@ -28,49 +29,49 @@
<view class="current-slip" @click="openGenealogy(currentGenealogy)"> <view class="current-slip" @click="openGenealogy(currentGenealogy)">
<image <image
class="current-frame" class="current-frame"
src="/static/assets/backgrounds/genealogy-current-slip-frame.png" src="/static/assets/modules/genealogy/transparent/current-slip-frame.png"
mode="scaleToFill" mode="scaleToFill"
/> />
<view class="current-seal"> <view class="current-summary">
<image <view class="current-seal">
class="current-seal-frame" <image
src="/static/assets/icons/genealogy/seal-current-frame-v1.png" class="current-seal-frame"
mode="scaleToFill" src="/static/assets/modules/genealogy/transparent/current-seal-frame.png"
/> mode="scaleToFill"
<text class="current-seal-title">家谱</text> />
</view> <text class="current-seal-title">家谱</text>
<view class="current-copy"> </view>
<text class="current-name">{{ currentGenealogy.name }}</text> <text class="current-name">{{ currentGenealogy.name }}</text>
<view class="current-info-divider"></view> </view>
<view class="current-meta"> <view class="current-info-divider"></view>
<view class="current-meta-item"> <view class="current-meta">
<image <view class="current-meta-item">
class="current-meta-icon" <image
src="/static/assets/icons/common/location-v1.png" class="current-meta-icon"
mode="aspectFit" src="/static/assets/foundation/transparent/meta-location.png"
/> mode="aspectFit"
<text class="current-meta-item-text">{{ />
currentGenealogy.location <text class="current-meta-item-text">{{
}}</text> currentGenealogy.location
</view> }}</text>
<view class="current-meta-item"> </view>
<image <view class="current-meta-item">
class="current-meta-icon" <image
src="/static/assets/icons/common/member-meta-v1.png" class="current-meta-icon"
mode="aspectFit" src="/static/assets/foundation/transparent/meta-member.png"
/> mode="aspectFit"
<text class="current-meta-item-text" />
>{{ currentGenealogy.memberCount }} 位成员</text <text class="current-meta-item-text"
> >{{ currentGenealogy.memberCount }} 位成员</text
</view> >
<view class="current-meta-item"> </view>
<image <view class="current-meta-item">
class="current-meta-icon" <image
src="/static/assets/icons/common/admin-v1.png" class="current-meta-icon"
mode="aspectFit" src="/static/assets/foundation/transparent/meta-admin.png"
/> mode="aspectFit"
<text class="current-meta-item-text">管理员</text> />
</view> <text class="current-meta-item-text">管理员</text>
</view> </view>
</view> </view>
</view> </view>
@@ -89,7 +90,7 @@
<image <image
class="section-divider" class="section-divider"
src="/static/assets/backgrounds/genealogy-section-divider-v2.png" src="/static/assets/modules/genealogy/transparent/section-divider.png"
mode="scaleToFill" mode="scaleToFill"
/> />
@@ -120,18 +121,18 @@
<view class="create-action" @click="createGenealogy"> <view class="create-action" @click="createGenealogy">
<image <image
class="create-cloud" class="create-cloud"
src="/static/assets/icons/action/create-cloud-v1.png" src="/static/assets/modules/genealogy/transparent/create-cloud.png"
mode="aspectFit" mode="aspectFit"
/> />
<image <image
class="create-icon" class="create-icon"
src="/static/assets/icons/action/add-v2.png" src="/static/assets/modules/genealogy/transparent/add.png"
mode="aspectFit" mode="aspectFit"
/> />
<text>新建家谱</text> <text>新建家谱</text>
<image <image
class="create-cloud create-cloud--right" class="create-cloud create-cloud--right"
src="/static/assets/icons/action/create-cloud-v1.png" src="/static/assets/modules/genealogy/transparent/create-cloud.png"
mode="aspectFit" mode="aspectFit"
/> />
</view> </view>
@@ -145,7 +146,7 @@
<view class="empty-primary" @click="createGenealogy"> <view class="empty-primary" @click="createGenealogy">
<image <image
class="create-icon" class="create-icon"
src="/static/assets/icons/action/add-v2.png" src="/static/assets/modules/genealogy/transparent/add.png"
mode="aspectFit" mode="aspectFit"
/> />
<text>新建家谱</text> <text>新建家谱</text>
@@ -185,39 +186,39 @@ const shortcuts = [
{ {
key: "tree", key: "tree",
label: "世系图", label: "世系图",
icon: "/static/assets/icons/genealogy/tree-v2.png", icon: "/static/assets/modules/genealogy/transparent/shortcut-tree.png",
}, },
{ {
key: "members", key: "members",
label: "成员", label: "成员",
icon: "/static/assets/icons/genealogy/members-v2.png", icon: "/static/assets/modules/genealogy/transparent/shortcut-members.png",
}, },
{ {
key: "poem", key: "poem",
label: "字辈诗", label: "字辈诗",
icon: "/static/assets/icons/genealogy/generation-poem-v2.png", icon: "/static/assets/modules/genealogy/transparent/shortcut-generation-poem.png",
}, },
{ {
key: "applications", key: "applications",
label: "申请审核", label: "申请审核",
icon: "/static/assets/icons/genealogy/application-v2.png", icon: "/static/assets/modules/genealogy/transparent/shortcut-application.png",
}, },
]; ];
const openGenealogy = (genealogy) => const openGenealogy = (genealogy) =>
uni.navigateTo({ url: `/pages/genealogy/detail?id=${genealogy.id}` }); uni.navigateTo({ url: `/pages/genealogy/g05-genealogy-overview?id=${genealogy.id}` });
const createGenealogy = () => const createGenealogy = () =>
uni.navigateTo({ url: "/pages/genealogy/create" }); uni.navigateTo({ url: "/pages/genealogy/g03-create-genealogy" });
const applyToJoin = () => uni.navigateTo({ url: "/pages/genealogy/search" }); const applyToJoin = () => uni.navigateTo({ url: "/pages/genealogy/g06-search-genealogies" });
const toNotifications = () => const toNotifications = () =>
uni.navigateTo({ url: "/pages/notification/index" }); uni.navigateTo({ url: "/pages/notification/n01-message-center" });
const openShortcut = (key) => { const openShortcut = (key) => {
const paths = { const paths = {
tree: "/pages/tree/index", tree: "/pages/tree/t01-tree-overview",
members: `/pages/genealogy/detail?id=${currentGenealogy.value.id}`, members: `/pages/genealogy/g05-genealogy-overview?id=${currentGenealogy.value.id}`,
poem: "/pages/content/list?type=poem", poem: "/pages/skeleton/content-list-skeleton?type=poem",
applications: "/pages/genealogy/applications", applications: "/pages/genealogy/g10-application-review",
}; };
uni.navigateTo({ url: paths[key] }); uni.navigateTo({ url: paths[key] });
}; };
@@ -233,7 +234,7 @@ const openShortcut = (key) => {
.page-paper-texture { .page-paper-texture {
position: absolute; position: absolute;
top: 184rpx; top: calc(124rpx + var(--status-bar-height, 0px));
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -247,27 +248,26 @@ const openShortcut = (key) => {
.page-footer-landscape { .page-footer-landscape {
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 128rpx; bottom: 112rpx;
left: 0; left: 0;
z-index: 1; z-index: 1;
width: 100%; width: 100%;
height: 360rpx; height: 480rpx;
opacity: 0.48; opacity: 0.5;
pointer-events: none; pointer-events: none;
} }
.genealogy-content { .genealogy-content {
position: relative; position: relative;
z-index: 2; z-index: 2;
padding: 28rpx 48rpx 194rpx; padding: 24rpx 32rpx 176rpx;
} }
.current-slip { .current-slip {
position: relative; position: relative;
display: flex; display: block;
min-height: 272rpx; min-height: 268rpx;
align-items: center; padding: 30rpx 34rpx 28rpx;
padding: 34rpx 30rpx;
box-sizing: border-box; box-sizing: border-box;
background: transparent; background: transparent;
} }
@@ -289,11 +289,11 @@ const openShortcut = (key) => {
z-index: 1; z-index: 1;
display: flex; display: flex;
width: 80rpx; width: 80rpx;
height: 136rpx; height: 128rpx;
flex: 0 0 auto; flex: 0 0 auto;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: 26rpx; margin-right: 24rpx;
color: #fff7e7; color: #fff7e7;
} }
@@ -317,13 +317,12 @@ const openShortcut = (key) => {
letter-spacing: 2rpx; letter-spacing: 2rpx;
writing-mode: vertical-rl; writing-mode: vertical-rl;
} }
.current-copy { .current-summary {
position: relative; position: relative;
z-index: 1; z-index: 1;
display: flex; display: flex;
min-width: 0; min-width: 0;
flex: 1; align-items: center;
flex-direction: column;
} }
.current-name { .current-name {
overflow: hidden; overflow: hidden;
@@ -335,15 +334,20 @@ const openShortcut = (key) => {
white-space: nowrap; white-space: nowrap;
} }
.current-info-divider { .current-info-divider {
position: relative;
z-index: 1;
width: 100%; width: 100%;
height: 1rpx; height: 1rpx;
margin: 20rpx 0 18rpx; margin: 14rpx 0 16rpx;
background: rgba(181, 138, 75, 0.48); background: rgba(181, 138, 75, 0.48);
} }
.current-meta { .current-meta {
position: relative;
z-index: 1;
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: space-between;
margin-top: 0; margin-top: 0;
color: $ink-muted; color: $ink-muted;
font-size: 25rpx; font-size: 25rpx;
@@ -353,7 +357,7 @@ const openShortcut = (key) => {
.current-meta-item { .current-meta-item {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 0 22rpx 8rpx 0; margin: 0;
white-space: nowrap; white-space: nowrap;
.current-meta-item-text{ .current-meta-item-text{
@@ -368,10 +372,10 @@ const openShortcut = (key) => {
.shortcut-grid { .shortcut-grid {
display: flex; display: flex;
min-height: 208rpx; min-height: 194rpx;
align-items: center; align-items: center;
margin-top: 36rpx; margin-top: 24rpx;
padding: 14rpx 0; padding: 8rpx 0;
} }
.shortcut-item { .shortcut-item {
@@ -402,22 +406,22 @@ const openShortcut = (key) => {
display: block; display: block;
width: 100%; width: 100%;
height: 30rpx; height: 30rpx;
margin: 10rpx 0 2rpx; margin: 4rpx 0 0;
} }
.genealogy-lower { .genealogy-lower {
padding: 18rpx 0 44rpx; padding: 12rpx 0 36rpx;
} }
.list-section { .list-section {
margin-top: 22rpx; margin-top: 16rpx;
} }
.section-heading { .section-heading {
display: flex; display: flex;
min-height: 64rpx; min-height: 56rpx;
align-items: center; align-items: center;
margin-bottom: 12rpx; margin-bottom: 8rpx;
color: $ink; color: $ink;
font-family: "STKaiti", "KaiTi", serif; font-family: "STKaiti", "KaiTi", serif;
font-size: 34rpx; font-size: 34rpx;
@@ -433,7 +437,7 @@ const openShortcut = (key) => {
content: ""; content: "";
} }
.list-section + .list-section { .list-section + .list-section {
margin-top: 26rpx; margin-top: 18rpx;
} }
.create-action, .create-action,
@@ -448,13 +452,14 @@ const openShortcut = (key) => {
} }
.create-action { .create-action {
width: 420rpx; width: 350rpx;
margin: 42rpx auto 0; margin: 32rpx auto 0;
border: 2rpx solid $brand-red; border: 2rpx solid $brand-red;
border-radius: 48rpx; border-radius: 48rpx;
background: rgba(255, 249, 238, 0.66); background: rgba(255, 249, 238, 0.66);
color: $brand-red; color: $brand-red;
font-family: "STKaiti", "KaiTi", serif; font-family: "STKaiti", "KaiTi", serif;
font-size: 32rpx;
font-weight: 700; font-weight: 700;
letter-spacing: 3rpx; letter-spacing: 3rpx;
} }
@@ -477,13 +482,18 @@ const openShortcut = (key) => {
margin-right: 12rpx; margin-right: 12rpx;
} }
.create-action .create-icon { .create-action .create-icon {
width: 44rpx; width: 40rpx;
height: 44rpx; height: 40rpx;
margin: 0 12rpx; margin: 0 8rpx;
} }
.create-cloud { .create-cloud {
width: 64rpx; width: 54rpx;
height: 32rpx; height: 28rpx;
flex: 0 0 auto;
}
.create-action > text {
flex: 0 0 auto;
white-space: nowrap;
} }
.create-cloud--right { .create-cloud--right {
transform: scaleX(-1); transform: scaleX(-1);
@@ -547,8 +557,8 @@ const openShortcut = (key) => {
@media screen and (max-width: 340px) { @media screen and (max-width: 340px) {
.genealogy-content { .genealogy-content {
padding-right: 24rpx; padding-right: 20rpx;
padding-left: 24rpx; padding-left: 20rpx;
} }
.current-name { .current-name {
font-size: 40rpx; font-size: 40rpx;
@@ -556,18 +566,15 @@ const openShortcut = (key) => {
.shortcut-label { .shortcut-label {
font-size: 23rpx; font-size: 23rpx;
} }
.current-meta-item {
margin-right: 12rpx;
}
.current-meta-icon { .current-meta-icon {
width: 28rpx; width: 28rpx;
height: 28rpx; height: 28rpx;
} }
.create-action { .create-action {
width: 360rpx; width: 320rpx;
} }
.create-cloud { .create-cloud {
width: 48rpx; width: 46rpx;
} }
} }
</style> </style>
@@ -1,3 +1,4 @@
<!-- 页面编号G-03用途创建家谱表单 -->
<template> <template>
<view class="page-shell create-page"> <view class="page-shell create-page">
<PageHeader title="创建家谱" /> <PageHeader title="创建家谱" />
@@ -49,7 +50,7 @@ const submit = async () => {
} }
const created = await appApi.createGenealogy({ ...form }) const created = await appApi.createGenealogy({ ...form })
genealogyContext.setCurrentGenealogyId(created.id) genealogyContext.setCurrentGenealogyId(created.id)
uni.redirectTo({ url: `/pages/lineage/first-person?genealogyId=${created.id}` }) uni.redirectTo({ url: `/pages/genealogy/g04-first-ancestor?genealogyId=${created.id}` })
} }
</script> </script>
@@ -1,3 +1,4 @@
<!-- 页面编号G-04用途创建家谱后的始祖录入 -->
<template> <template>
<view class="page-shell first-person-page"> <view class="page-shell first-person-page">
<PageHeader title="录入首代人物" /> <PageHeader title="录入首代人物" />
@@ -35,7 +36,7 @@ const submit = async () => {
isSubmitting.value = true isSubmitting.value = true
try { try {
const person = await appApi.createPerson(genealogyId.value, { ...form, personName: form.personName.trim() }) const person = await appApi.createPerson(genealogyId.value, { ...form, personName: form.personName.trim() })
uni.redirectTo({ url: `/pages/tree/index?genealogyId=${genealogyId.value}&selectedId=${person.id}` }) uni.redirectTo({ url: `/pages/tree/t01-tree-overview?genealogyId=${genealogyId.value}&selectedId=${person.id}` })
} catch (error) { } catch (error) {
uni.showToast({ title: error.message || '保存失败,请稍后重试', icon: 'none' }) uni.showToast({ title: error.message || '保存失败,请稍后重试', icon: 'none' })
} finally { } finally {
@@ -1,3 +1,4 @@
<!-- 页面编号G-05用途家谱总览 -->
<template> <template>
<view class="page-shell detail-page"> <view class="page-shell detail-page">
<PageHeader title="家谱总览" action="管理" @action="showSoon" /> <PageHeader title="家谱总览" action="管理" @action="showSoon" />
@@ -52,10 +53,10 @@ const loadGenealogy = async (id) => {
} }
onLoad((query) => loadGenealogy(query.id)) onLoad((query) => loadGenealogy(query.id))
const toTree = () => uni.navigateTo({ url: `/pages/tree/index?genealogyId=${genealogyId.value}` }) const toTree = () => uni.navigateTo({ url: `/pages/tree/t01-tree-overview?genealogyId=${genealogyId.value}` })
const toFirstPerson = () => uni.navigateTo({ url: `/pages/lineage/first-person?genealogyId=${genealogyId.value}` }) const toFirstPerson = () => uni.navigateTo({ url: `/pages/genealogy/g04-first-ancestor?genealogyId=${genealogyId.value}` })
const toFamily = () => uni.reLaunch({ url: '/pages/family/index' }) const toFamily = () => uni.reLaunch({ url: '/pages/family/f01-family-feed' })
const toApplications = () => uni.navigateTo({ url: `/pages/genealogy/applications?genealogyId=${genealogyId.value}` }) const toApplications = () => uni.navigateTo({ url: `/pages/genealogy/g10-application-review?genealogyId=${genealogyId.value}` })
const showSoon = () => uni.showToast({ title: '该模块将在后续阶段接入', icon: 'none' }) const showSoon = () => uni.showToast({ title: '该模块将在后续阶段接入', icon: 'none' })
</script> </script>
@@ -1,3 +1,4 @@
<!-- 页面编号G-06用途公开家谱搜索 -->
<template> <template>
<view class="page-shell search-page"> <view class="page-shell search-page">
<PageHeader title="查找家谱" /> <PageHeader title="查找家谱" />
@@ -1,3 +1,4 @@
<!-- 页面编号G-10用途管理员入谱申请审核 -->
<template> <template>
<view class="page-shell applications-page"> <view class="page-shell applications-page">
<PageHeader title="入谱审核" action="审核说明" @action="showHelp" /> <PageHeader title="入谱审核" action="审核说明" @action="showHelp" />
@@ -1,3 +1,4 @@
<!-- 页面编号N-01用途消息中心 -->
<template> <template>
<view class="page-shell notification-page"> <view class="page-shell notification-page">
<PageHeader title="消息通知" action="全部已读" @action="readAll" /> <PageHeader title="消息通知" action="全部已读" @action="readAll" />
@@ -1,3 +1,4 @@
<!-- 页面编号M-01用途我的首页 -->
<template> <template>
<view class="page-shell profile-page"> <view class="page-shell profile-page">
<view class="profile-hero"><view class="profile-seal">{{ profile.name ? profile.name.slice(0, 1) : '谱' }}</view><view class="profile-copy"><text class="profile-name">{{ profile.name || '家谱用户' }}</text><text class="profile-meta">{{ profile.phone || '资料加载中' }} · {{ profile.role || '家谱成员' }}</text></view><text class="profile-edit" @click="showSoon">资料</text></view> <view class="profile-hero"><view class="profile-seal">{{ profile.name ? profile.name.slice(0, 1) : '谱' }}</view><view class="profile-copy"><text class="profile-name">{{ profile.name || '家谱用户' }}</text><text class="profile-meta">{{ profile.phone || '资料加载中' }} · {{ profile.role || '家谱成员' }}</text></view><text class="profile-edit" @click="showSoon">资料</text></view>
@@ -24,7 +25,7 @@ const serviceItems = ['帮助中心', '意见反馈', 'VIP 服务']
onMounted(async () => { onMounted(async () => {
try { profile.value = await appApi.getProfile() } catch (error) { loadError.value = error.message || '个人资料加载失败。' } try { profile.value = await appApi.getProfile() } catch (error) { loadError.value = error.message || '个人资料加载失败。' }
}) })
const toNotifications = () => uni.navigateTo({ url: '/pages/notification/index' }) const toNotifications = () => uni.navigateTo({ url: '/pages/notification/n01-message-center' })
const showSoon = () => uni.showToast({ title: '该服务将按计划逐步开放', icon: 'none' }) const showSoon = () => uni.showToast({ title: '该服务将按计划逐步开放', icon: 'none' })
</script> </script>
@@ -1,3 +1,4 @@
<!-- 页面编号S-01用途多类型内容列表功能骨架不计为任何已完成设计页面 -->
<template> <template>
<view class="page-shell content-page"> <view class="page-shell content-page">
<PageHeader :title="config.title" /> <PageHeader :title="config.title" />
@@ -1,3 +1,4 @@
<!-- 页面编号T-01用途世系树总览 -->
<template> <template>
<view class="tree-page"> <view class="tree-page">
<PageHeader title="世系树" action="树形视图" /> <PageHeader title="世系树" action="树形视图" />
@@ -38,7 +39,7 @@ const loadTree = async (query) => {
onLoad(loadTree) onLoad(loadTree)
const nodeStyle = (member) => ({ left: `${member.x}%`, top: `${member.y}%` }) const nodeStyle = (member) => ({ left: `${member.x}%`, top: `${member.y}%` })
const toMember = () => uni.navigateTo({ url: `/pages/member/detail?genealogyId=${genealogyId.value}&personId=${selected.value.id}` }) const toMember = () => uni.navigateTo({ url: `/pages/tree/t03-member-profile?genealogyId=${genealogyId.value}&personId=${selected.value.id}` })
const showSoon = () => uni.showToast({ title: '亲属关系编辑将在下一阶段接入', icon: 'none' }) const showSoon = () => uni.showToast({ title: '亲属关系编辑将在下一阶段接入', icon: 'none' })
</script> </script>
@@ -1,3 +1,4 @@
<!-- 页面编号T-03用途成员档案详情 -->
<template> <template>
<view class="page-shell member-page"> <view class="page-shell member-page">
<PageHeader title="成员档案" action="编辑" @action="showSoon" /> <PageHeader title="成员档案" action="编辑" @action="showSoon" />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More