修改未完成
@@ -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) => {
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
|
<!-- 公共组件: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>
|
||||||
@@ -13,17 +26,29 @@
|
|||||||
<text class="card-name">{{ genealogy.name }}</text>
|
<text class="card-name">{{ genealogy.name }}</text>
|
||||||
<view class="card-side">
|
<view class="card-side">
|
||||||
<text class="card-role">{{ role }}</text>
|
<text class="card-role">{{ role }}</text>
|
||||||
<image class="card-chevron" src="/static/assets/icons/common/chevron-right-v2.png" mode="aspectFit" />
|
<image
|
||||||
|
class="card-chevron"
|
||||||
|
src="/static/assets/foundation/transparent/chevron-right.png"
|
||||||
|
mode="aspectFit"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</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>
|
||||||
@@ -39,9 +64,9 @@
|
|||||||
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">
|
||||||
@@ -55,7 +80,9 @@ defineEmits(['select'])
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genealogy-card--current { background: transparent; }
|
.genealogy-card--current {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.row-frame {
|
.row-frame {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -82,11 +109,39 @@ 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-main {
|
.card-main {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -97,21 +152,64 @@ 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-title-row { display: flex; min-width: 0; align-items: center; justify-content: space-between; }
|
.card-meta {
|
||||||
.card-name { color: $ink; font-family: 'STKaiti', 'KaiTi', serif; font-size: 37rpx; font-weight: 700; }
|
white-space: nowrap;
|
||||||
.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-title-row {
|
||||||
.card-meta-icon { width: 28rpx; height: 28rpx; margin-right: 6rpx; }
|
display: flex;
|
||||||
.card-meta { color: $ink-muted; font-size: 26rpx; }
|
min-width: 0;
|
||||||
.card-updated { display: flex; flex: 0 0 auto; align-items: center; margin-left: 10rpx; color: $ink-muted; font-size: 22rpx; white-space: nowrap; }
|
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;
|
||||||
@@ -125,16 +223,44 @@ defineEmits(['select'])
|
|||||||
margin-left: 20rpx;
|
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: 66rpx; height: 100rpx; margin-right: 16rpx; }
|
min-height: 148rpx;
|
||||||
.card-name { font-size: 32rpx; }
|
padding-right: 18rpx;
|
||||||
.card-meta { font-size: 22rpx; }
|
padding-left: 18rpx;
|
||||||
.card-updated { display: none; }
|
}
|
||||||
.card-side { margin-left: 12rpx; }
|
.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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,272 +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` | 图标视觉大小可为 `40–48rpx`。 |
|
|
||||||
| 列表行高度 | `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.3 第三方平台品牌标识
|
|
||||||
|
|
||||||
- 微信、支付宝、QQ 等已有成熟品牌标识不重新设计、不改色;仅在页面确有对应登录、支付或分享入口时使用。
|
|
||||||
- 资产可从品牌官方渠道或许可的素材来源下载为透明背景 PNG,但必须保存到项目 `static/assets/icons/` 后由页面本地引用;不使用远程 URL,不从截图或大图裁切。
|
|
||||||
- 每个引入的第三方 PNG 必须记录来源、用途、文件路径与透明角检查结果;若品牌方更新使用规则或要求停用,应替换或移除该资产。
|
|
||||||
|
|
||||||
### 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` 状态轴和 20–48dp 光学尺寸;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` 文件;同一语义只保留一个已验收版本。
|
||||||
|
|
||||||
- 顶部采用 `124rpx` 的可视朱砂导航区,外层始终额外预留真实系统状态栏高度;左下放置低对比祠堂屋檐线描,禁止使用满屏建筑照片。
|
## 4. 页面与组件
|
||||||
- 自定义顶部统一使用 `height: calc(124rpx + var(--status-bar-height, 0px))` 与同等 `padding-top`;纸纹或内容背景的起点使用同一计算值,不能让状态栏挤压 Logo、标题或通知入口。
|
|
||||||
- 根页面左上角放项目 Logo(祠堂屋檐 + “谱”字印章,透明 PNG,视觉 `40rpx`、触控区 `88rpx`);详情页该位置优先保留返回按钮,Logo 收入建筑纹样中的小印记。
|
|
||||||
- 搜索、通知、更多均为 `88rpx` 触控框;图标为暖白或金色线稿 PNG。通知铃铛仅在未读消息数大于 0 时显示朱砂小圆点;未读为 0 时不显示圆点和数字。
|
|
||||||
- 家谱名称位于视觉中心,最大两行;名称下可放地域或代系提示,但不能与图标重叠。
|
|
||||||
- 红头结束后立即进入纸面 Tab/分段导航,使用古金底线标记当前项。
|
|
||||||
|
|
||||||
### 4.2 普通列表与详情页面壳
|
- 页面文件:`<页面编号>-<语义名>.vue`;没有确认页面编号的临时骨架要标明 `skeleton`,不能伪装成完成页面。
|
||||||
|
- 公共组件只收纳已被两个或以上确认页面使用的稳定结构:页面纸纹壳、顶部栏、底部导航、完整图片按钮、完整图片卡片、状态容器。
|
||||||
|
- 页面与组件开头写中文用途注释;模板按背景、页头、主体、操作、状态分段;资产图层和复杂交互写中文约束说明。
|
||||||
|
- CSS 只负责布局、尺寸、文字、可见/隐藏与交互状态;不能再用 CSS 绘制边角、金线、云纹、分隔结、纸纹、按钮或卡片外观。
|
||||||
|
|
||||||
适用于:家族动态、谱文、相册、人物录、礼仪、消息、我的。
|
## 5. Android 约束
|
||||||
|
|
||||||
- 顶部使用 `144–176rpx` 紧凑朱砂题签,保留建筑线描的局部纹样。
|
- 核验尺寸: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` | 只预留系统空间,不放页面内容。 |
|
|
||||||
| 家祠红头 | `124rpx` 可视导航区 + 真实状态栏高度 | 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 还原。
|
|
||||||
|
|||||||
@@ -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 不写噪声注释。
|
||||||
@@ -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: 当前已验收 A01/G01 图片、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: PASS,A01 中登记为透明的 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,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` 通过。
|
||||||
@@ -1,110 +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 个当前路由的页面编号和目标语义名。
|
||||||
- 当前正在处理:`D2 / A-01 至 A-06 账户与首次使用`设计;`A-01` 与 `A-02` 已于 2026-07-13 落地本地 UI 并等待运行时验收,`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 位成员 / 成员`
|
|
||||||
- 当前题签有标题/元信息分隔线;列表右侧角色和箭头应为同一水平基线;日期为“更新于”文字,不显示参考稿中没有的日历图标。
|
|
||||||
|
|
||||||
### 2026-07-13 最终验收结论
|
| 文件 | 来源与状态 | 下次处理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `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,完整宣纸按钮皮肤;未接入 | 与主按钮同样先重做或确认比例、角纹、金边,再进入页面。 |
|
||||||
|
|
||||||
- 用户已提供 `360 × 800`、`320 × 568`(含滚动到底部)响应式预览,以及 HBuilderX Web 浏览器 `Pixel 2 XL` 常规屏预览。
|
下次严格按此顺序继续:
|
||||||
- 已确认:顶部、题签、快捷入口、两条列表、新建按钮、山水和 Tabbar 无重叠或裁切;`320 × 568` 下新建按钮可完整露出在 Tabbar 上方。
|
|
||||||
- 用户明确确认可结项,故 G-01 已勾选完成。此结论不解除接口联调、真实账号联调或 Android 打包暂停。
|
|
||||||
|
|
||||||
## 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. 将本文件和后续页面的新截图一起发给新的 GPT,并说明:“先读 `docs/交接记录.md` 与 `AGENTS.md`,按规划继续页面设计,不要改接口。”
|
- 公共检查:`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 × 915;A01、G01 必须至少复核 412 × 915。
|
||||||
|
|
||||||
## 9. 2026-07-13 流程纠正记录
|
## 8. 已知工作区注意事项
|
||||||
|
|
||||||
- A-01 的历史效果稿 v1、v2、v3 与四尺寸栅格稿均已归档;当前协议未勾选状态的运行时视觉依据为 `docs/design/screens/A01-启动登录引导-栅格验收-412x915.png`,对应说明见 `docs/design/A01_启动登录引导_设计记录.md`。
|
- 工作区可能带有用户未提交修改。只处理当前任务涉及文件;提交时只暂存本轮文件。
|
||||||
- A-01 的固定栅格、状态、独立背景/图标及 320 × 568、360 × 640、360 × 800、412 × 915 静态效果稿已归档并经审计;它们仍不替代 uni-app 页面实现后的 Android 运行时验收。
|
- 历史 `docs/superpowers/*a01*reference-asset-rebuild*` 与旧 G01 设计记录中存在已废弃的路径和“截图/切片式”尝试;不能作为当前实施依据。
|
||||||
- 先前误建的 A-01 规格和“四个独立按钮”执行计划已降为失效历史;2026-07-13 已确认文字注册入口方案,唯一需求所有者为 `docs/design/A01_启动登录引导_设计记录.md`。用户随后授权 A-01 欢迎入口与 A-02 双标签登录页的本地 UI 落地;不得接接口、执行真实登录或替其他未设计页面造功能。
|
- 当前依据永远以本文件、`docs/规划.md`、D1、P00 和用户最新消息为准;发生冲突时,以用户最新确认优先。
|
||||||
- 所有后续页面效果图必须先保存到 `docs/design/screens/`,并在对应页面设计记录写明用途、状态和验收缺口;未完成归档不得展示为可推进的设计成果。
|
|
||||||
- G-01 已于 2026-07-13 经用户确认结项;当前进入 D2 / A-01 的设计工作,接口、真实登录和 Android 打包仍暂停。
|
|
||||||
- A-01 入口页与 A-02 双标签登录页已于 2026-07-13 落地本地 UI:路由入口为 `pages/auth/entry`;只有在 A-01 显式勾选协议后才可进入 A-02。页面不调用接口、不写登录态、不跳转家谱;微信、注册、协议、忘记密码和验证码仅提示待相应页面或能力完成。A-01/A-02 运行时四尺寸验收仍待执行。
|
|
||||||
- A-01 首次本地预览与 412 × 915 栅格验收图存在红头高度、按钮宽度和垂直节奏偏差。整图视觉层方案已被用户否决并撤销;现以真实 DOM、分层背景和按钮图标重建页面,下一次 Android 预览必须以该图作同尺寸对比。
|
|
||||||
|
|||||||
@@ -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 启动 / 登录引导页(账号密码、手机号验证码、微信登录入口;本地 UI 已落地,协议页去向与 Android 运行时验收待完成)。
|
### 5.4 家族内容
|
||||||
- [ ] A-02 账号密码登录页(账号密码 / 手机验证码双标签本地 UI 已落地;最终效果稿、运行时验收与接口待完成)。
|
|
||||||
- [ ] 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 信息层级与谱印施工:元信息图标、谱印框、快捷入口、当前题签两层结构、列表两行信息、页头屋檐、紧凑 Tabbar 与新建祥云 v2 已实现并通过资产/编译审计;2026-07-13 已由用户基于小屏与 HBuilderX 预览确认验收。
|
### 5.5 人物、礼仪与档案
|
||||||
- [x] G-01 我的家谱列表(已创建、已加入、通知入口)。公开家谱搜索入口由 G-06 / G-07 统一承接,避免与本页重复。
|
|
||||||
- [ ] 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:账户与首次使用全套页面 | 进行中(A-01 已完成文字注册入口方案、独立资产、固定栅格、四尺寸静态验收与本地 UI;A-02 双标签本地 UI 已落地;协议页去向、最终效果稿、Android 运行时验收、A-03 至 A-06 与认证接口待完成) |
|
|
||||||
| D3 | G-01 至 G-12:家谱创建、加入、总览与管理全套页面 | 进行中(G-01 已于 2026-07-13 验收完成;G-02 至 G-12 未开始) |
|
|
||||||
| 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 打包。
|
|
||||||
|
|||||||
@@ -1,63 +1,63 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"path": "pages/auth/entry",
|
"path": "pages/auth/a01-entry",
|
||||||
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
|
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/auth/login",
|
"path": "pages/auth/a02-login",
|
||||||
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
|
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/genealogy/index",
|
"path": "pages/genealogy/g01-my-genealogies",
|
||||||
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
|
"style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/genealogy/create",
|
"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" }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,52 +1,54 @@
|
|||||||
|
<!-- 页面编号:A-01;用途:启动/登录引导;视觉基准已由用户验收,地基迁移不得改变可见效果。 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="auth-page entry-page">
|
<view class="auth-page entry-page">
|
||||||
<image class="paper-background" src="/static/assets/backgrounds/auth-rice-paper-v1.jpg" mode="aspectFill" />
|
<!-- 不透明纸纹底图承担页面底色;透明山水只作为上层装饰。 -->
|
||||||
<image class="scenery-background" src="/static/assets/backgrounds/auth-ink-scenery-v1.png" mode="aspectFill" />
|
<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-content">
|
||||||
<view class="auth-header">
|
<view class="auth-header">
|
||||||
<image class="header-background" src="/static/assets/backgrounds/auth-ancestral-header-v1.png" mode="aspectFill" />
|
<image class="header-background" src="/static/assets/foundation/opaque/auth-header.png" mode="aspectFill" />
|
||||||
<image class="header-seal" src="/static/assets/icons/brand/jiapu-seal-logo-header.png" mode="aspectFit" />
|
<image class="header-seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="brand-intro">
|
<view class="brand-intro">
|
||||||
<view class="title-decoration">
|
<view class="title-decoration">
|
||||||
<image class="title-cloud title-cloud-left" src="/static/assets/icons/auth/auth-title-cloud-v1.png" mode="aspectFit" />
|
<image class="title-cloud title-cloud-left" src="/static/assets/foundation/transparent/auth-title-cloud.png" mode="aspectFit" />
|
||||||
<text class="brand-title">家谱</text>
|
<text class="brand-title">家谱</text>
|
||||||
<image class="title-cloud title-cloud-right" src="/static/assets/icons/auth/auth-title-cloud-v1.png" mode="aspectFit" />
|
<image class="title-cloud title-cloud-right" src="/static/assets/foundation/transparent/auth-title-cloud.png" mode="aspectFit" />
|
||||||
</view>
|
</view>
|
||||||
<view class="brand-divider brand-divider--top">
|
<view class="brand-divider brand-divider--top">
|
||||||
<view class="brand-divider__line" />
|
<view class="brand-divider__line" />
|
||||||
<image class="brand-divider__ornament" src="/static/assets/icons/auth/auth-divider-knot-v1.png" mode="aspectFit" />
|
<image class="brand-divider__ornament" src="/static/assets/foundation/transparent/auth-divider-knot.png" mode="aspectFit" />
|
||||||
<view class="brand-divider__line" />
|
<view class="brand-divider__line" />
|
||||||
</view>
|
</view>
|
||||||
<text class="brand-subtitle">为家族留存可传承的记忆</text>
|
<text class="brand-subtitle">为家族留存可传承的记忆</text>
|
||||||
<view class="brand-divider brand-divider--bottom">
|
<view class="brand-divider brand-divider--bottom">
|
||||||
<view class="brand-divider__line" />
|
<view class="brand-divider__line" />
|
||||||
<image class="brand-divider__ornament" src="/static/assets/icons/auth/auth-divider-knot-v1.png" mode="aspectFit" />
|
<image class="brand-divider__ornament" src="/static/assets/foundation/transparent/auth-divider-knot.png" mode="aspectFit" />
|
||||||
<view class="brand-divider__line" />
|
<view class="brand-divider__line" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="entry-actions">
|
<view class="entry-actions">
|
||||||
<view class="entry-button entry-button--primary" hover-class="entry-button--pressed" @click="openLogin">
|
<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/icons/auth/auth-button-corner-v1.png" mode="aspectFit" />
|
<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/icons/auth/auth-button-corner-v1.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/icons/auth/auth-button-corner-v1.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/icons/auth/auth-button-corner-v1.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">
|
<view class="entry-button__content">
|
||||||
<image class="entry-button__icon" src="/static/assets/icons/auth/login-outline-v1.png" mode="aspectFit" />
|
<image class="entry-button__icon" src="/static/assets/foundation/transparent/auth-login-outline.png" mode="aspectFit" />
|
||||||
<text>登录</text>
|
<text>登录</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="entry-button entry-button--secondary" hover-class="entry-button--pressed" @click="prepareWechatLogin">
|
<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/icons/auth/auth-button-corner-v1.png" mode="aspectFit" />
|
<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/icons/auth/auth-button-corner-v1.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/icons/auth/auth-button-corner-v1.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/icons/auth/auth-button-corner-v1.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">
|
<view class="entry-button__content">
|
||||||
<image class="entry-button__icon entry-button__icon--wechat" src="/static/assets/icons/auth/wechat-licensed-v1.png" mode="aspectFit" />
|
<image class="entry-button__icon entry-button__icon--wechat" src="/static/assets/foundation/transparent/auth-wechat.png" mode="aspectFit" />
|
||||||
<text>微信登录</text>
|
<text>微信登录</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -88,7 +90,7 @@ const requireAgreement = () => {
|
|||||||
|
|
||||||
const openLogin = () => {
|
const openLogin = () => {
|
||||||
if (!requireAgreement()) return
|
if (!requireAgreement()) return
|
||||||
uni.navigateTo({ url: '/pages/auth/login?agreed=1' })
|
uni.navigateTo({ url: '/pages/auth/a02-login?agreed=1' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const prepareWechatLogin = () => {
|
const prepareWechatLogin = () => {
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
|
<!-- 页面编号:A-02;用途:账号登录;当前仅保留已确认的本地登录界面。 -->
|
||||||
<template>
|
<template>
|
||||||
<view class="auth-page login-page">
|
<view class="auth-page login-page">
|
||||||
<image class="paper-background" src="/static/assets/backgrounds/auth-rice-paper-v1.jpg" mode="aspectFill" />
|
<!-- 与 A01 共用的登录页底图层:先不透明纸纹,后透明山水。 -->
|
||||||
<image class="scenery-background" src="/static/assets/backgrounds/auth-ink-scenery-v1.png" mode="aspectFill" />
|
<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-content">
|
||||||
<view class="auth-header">
|
<view class="auth-header">
|
||||||
<image class="header-background" src="/static/assets/backgrounds/auth-ancestral-header-v1.png" mode="aspectFill" />
|
<image class="header-background" src="/static/assets/foundation/opaque/auth-header.png" mode="aspectFill" />
|
||||||
<image class="header-seal" src="/static/assets/icons/brand/jiapu-seal-logo-header.png" mode="aspectFit" />
|
<image class="header-seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="login-panel">
|
<view class="login-panel">
|
||||||
@@ -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,14 +29,14 @@
|
|||||||
<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-summary">
|
<view class="current-summary">
|
||||||
<view class="current-seal">
|
<view class="current-seal">
|
||||||
<image
|
<image
|
||||||
class="current-seal-frame"
|
class="current-seal-frame"
|
||||||
src="/static/assets/icons/genealogy/seal-current-frame-v1.png"
|
src="/static/assets/modules/genealogy/transparent/current-seal-frame.png"
|
||||||
mode="scaleToFill"
|
mode="scaleToFill"
|
||||||
/>
|
/>
|
||||||
<text class="current-seal-title">家谱</text>
|
<text class="current-seal-title">家谱</text>
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
<view class="current-meta-item">
|
<view class="current-meta-item">
|
||||||
<image
|
<image
|
||||||
class="current-meta-icon"
|
class="current-meta-icon"
|
||||||
src="/static/assets/icons/common/location-v1.png"
|
src="/static/assets/foundation/transparent/meta-location.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
/>
|
/>
|
||||||
<text class="current-meta-item-text">{{
|
<text class="current-meta-item-text">{{
|
||||||
@@ -57,7 +58,7 @@
|
|||||||
<view class="current-meta-item">
|
<view class="current-meta-item">
|
||||||
<image
|
<image
|
||||||
class="current-meta-icon"
|
class="current-meta-icon"
|
||||||
src="/static/assets/icons/common/member-meta-v1.png"
|
src="/static/assets/foundation/transparent/meta-member.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
/>
|
/>
|
||||||
<text class="current-meta-item-text"
|
<text class="current-meta-item-text"
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
<view class="current-meta-item">
|
<view class="current-meta-item">
|
||||||
<image
|
<image
|
||||||
class="current-meta-icon"
|
class="current-meta-icon"
|
||||||
src="/static/assets/icons/common/admin-v1.png"
|
src="/static/assets/foundation/transparent/meta-admin.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
/>
|
/>
|
||||||
<text class="current-meta-item-text">管理员</text>
|
<text class="current-meta-item-text">管理员</text>
|
||||||
@@ -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-v2.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-v2.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] });
|
||||||
};
|
};
|
||||||
@@ -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" />
|
||||||
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 295 KiB |
|
Before Width: | Height: | Size: 59 KiB 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 |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.2 KiB 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 |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 875 B After Width: | Height: | Size: 875 B |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
@@ -30,16 +30,16 @@ function Assert-NotContains {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$root = Split-Path -Parent $PSScriptRoot
|
$root = Split-Path -Parent $PSScriptRoot
|
||||||
$entryPath = Join-Path $root 'pages/auth/entry.vue'
|
$entryPath = Join-Path $root 'pages/auth/a01-entry.vue'
|
||||||
$loginPath = Join-Path $root 'pages/auth/login.vue'
|
$loginPath = Join-Path $root 'pages/auth/a02-login.vue'
|
||||||
$pagesPath = Join-Path $root 'pages.json'
|
$pagesPath = Join-Path $root 'pages.json'
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $entryPath)) {
|
if (-not (Test-Path -LiteralPath $entryPath)) {
|
||||||
throw 'Missing A-01 entry page: pages/auth/entry.vue'
|
throw 'Missing A-01 entry page: pages/auth/a01-entry.vue'
|
||||||
}
|
}
|
||||||
|
|
||||||
$pages = Get-Content -LiteralPath $pagesPath -Raw -Encoding utf8 | ConvertFrom-Json
|
$pages = Get-Content -LiteralPath $pagesPath -Raw -Encoding utf8 | ConvertFrom-Json
|
||||||
if ($pages.pages[0].path -ne 'pages/auth/entry') {
|
if ($pages.pages[0].path -ne 'pages/auth/a01-entry') {
|
||||||
throw 'A-01 entry page must be the first route in pages.json'
|
throw 'A-01 entry page must be the first route in pages.json'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ foreach ($requiredEntryCopy in @($wechatLogin, $registerPrompt, $registerEntry,
|
|||||||
Assert-Contains -Content $entry -Expected $requiredEntryCopy -Message "Missing A-01 copy: $requiredEntryCopy"
|
Assert-Contains -Content $entry -Expected $requiredEntryCopy -Message "Missing A-01 copy: $requiredEntryCopy"
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert-Contains -Content $entry -Expected "url: '/pages/auth/login?agreed=1'" -Message 'A-01 account login must navigate with agreed=1'
|
Assert-Contains -Content $entry -Expected "url: '/pages/auth/a02-login?agreed=1'" -Message 'A-01 account login must navigate with agreed=1'
|
||||||
Assert-Contains -Content $entry -Expected "<text>$loginEntry</text>" -Message 'A-01 primary entry copy must be 登录'
|
Assert-Contains -Content $entry -Expected "<text>$loginEntry</text>" -Message 'A-01 primary entry copy must be 登录'
|
||||||
foreach ($requiredClass in @('auth-header', 'brand-intro', 'entry-actions', 'agreement-row')) {
|
foreach ($requiredClass in @('auth-header', 'brand-intro', 'entry-actions', 'agreement-row')) {
|
||||||
Assert-Contains -Content $entry -Expected $requiredClass -Message "Missing A-01 page structure: $requiredClass"
|
Assert-Contains -Content $entry -Expected $requiredClass -Message "Missing A-01 page structure: $requiredClass"
|
||||||
@@ -92,9 +92,9 @@ foreach ($requiredVisualAnchor in @(
|
|||||||
'inset: 6rpx',
|
'inset: 6rpx',
|
||||||
'entry-button__content',
|
'entry-button__content',
|
||||||
'translateX(-20rpx)',
|
'translateX(-20rpx)',
|
||||||
'auth-button-corner-v1.png',
|
'auth-button-corner.png',
|
||||||
'auth-divider-knot-v1.png',
|
'auth-divider-knot.png',
|
||||||
'auth-title-cloud-v1.png',
|
'auth-title-cloud.png',
|
||||||
'button-corner-asset',
|
'button-corner-asset',
|
||||||
'align-items: center',
|
'align-items: center',
|
||||||
'white-space: nowrap',
|
'white-space: nowrap',
|
||||||
@@ -116,7 +116,7 @@ Assert-NotContains -Content $entry -Unexpected $dividerGlyph -Message 'A-01 divi
|
|||||||
if (([regex]::Matches($entry, 'button-corner-asset button-corner-asset--')).Count -ne 8) {
|
if (([regex]::Matches($entry, 'button-corner-asset button-corner-asset--')).Count -ne 8) {
|
||||||
throw 'A-01 must render the generated ornamental corner asset in all four corners of both entry buttons'
|
throw 'A-01 must render the generated ornamental corner asset in all four corners of both entry buttons'
|
||||||
}
|
}
|
||||||
if (([regex]::Matches($entry, 'auth-divider-knot-v1\.png')).Count -ne 2) {
|
if (([regex]::Matches($entry, 'auth-divider-knot\.png')).Count -ne 2) {
|
||||||
throw 'A-01 must render the dedicated divider-knot asset above and below the subtitle'
|
throw 'A-01 must render the dedicated divider-knot asset above and below the subtitle'
|
||||||
}
|
}
|
||||||
if ($entry -notmatch '(?s)\.agreement-row\s*\{[^}]*align-items:\s*center') {
|
if ($entry -notmatch '(?s)\.agreement-row\s*\{[^}]*align-items:\s*center') {
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ $root = Split-Path -Parent $PSScriptRoot
|
|||||||
Add-Type -AssemblyName System.Drawing
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
|
||||||
$backgrounds = @(
|
$backgrounds = @(
|
||||||
'static/assets/backgrounds/auth-ancestral-header-v1.png',
|
'static/assets/foundation/opaque/auth-header.png',
|
||||||
'static/assets/backgrounds/auth-rice-paper-v1.jpg',
|
'static/assets/foundation/opaque/auth-page-paper.jpg',
|
||||||
'static/assets/backgrounds/auth-ink-scenery-v1.png'
|
'static/assets/foundation/transparent/auth-ink-scenery.png'
|
||||||
)
|
)
|
||||||
$icons = @(
|
$icons = @(
|
||||||
'static/assets/icons/auth/login-outline-v1.png',
|
'static/assets/foundation/transparent/auth-login-outline.png',
|
||||||
'static/assets/icons/auth/wechat-licensed-v1.png',
|
'static/assets/foundation/transparent/auth-wechat.png',
|
||||||
'static/assets/icons/auth/auth-button-corner-v1.png',
|
'static/assets/foundation/transparent/auth-button-corner.png',
|
||||||
'static/assets/icons/auth/auth-divider-knot-v1.png',
|
'static/assets/foundation/transparent/auth-divider-knot.png',
|
||||||
'static/assets/icons/auth/auth-title-cloud-v1.png'
|
'static/assets/foundation/transparent/auth-title-cloud.png'
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($asset in $backgrounds + $icons) {
|
foreach ($asset in $backgrounds + $icons) {
|
||||||
@@ -41,9 +41,9 @@ foreach ($asset in $icons) {
|
|||||||
$bitmap.Dispose()
|
$bitmap.Dispose()
|
||||||
|
|
||||||
$expectedSize = switch ($asset) {
|
$expectedSize = switch ($asset) {
|
||||||
'static/assets/icons/auth/auth-button-corner-v1.png' { @(160, 160) }
|
'static/assets/foundation/transparent/auth-button-corner.png' { @(160, 160) }
|
||||||
'static/assets/icons/auth/auth-divider-knot-v1.png' { @(160, 96) }
|
'static/assets/foundation/transparent/auth-divider-knot.png' { @(160, 96) }
|
||||||
'static/assets/icons/auth/auth-title-cloud-v1.png' { @(200, 120) }
|
'static/assets/foundation/transparent/auth-title-cloud.png' { @(200, 120) }
|
||||||
default { @(96, 96) }
|
default { @(96, 96) }
|
||||||
}
|
}
|
||||||
if ($width -ne $expectedSize[0] -or $height -ne $expectedSize[1]) { throw "$asset must be $($expectedSize[0]) x $($expectedSize[1])px." }
|
if ($width -ne $expectedSize[0] -or $height -ne $expectedSize[1]) { throw "$asset must be $($expectedSize[0]) x $($expectedSize[1])px." }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
$root = Split-Path -Parent $PSScriptRoot
|
$root = Split-Path -Parent $PSScriptRoot
|
||||||
$requiredFiles = @('utils/config.js', 'utils/session.js', 'utils/genealogy-context.js', 'utils/api.js', 'pages/lineage/first-person.vue', 'pages/content/list.vue', 'pages/content/editor.vue')
|
$requiredFiles = @('utils/config.js', 'utils/session.js', 'utils/genealogy-context.js', 'utils/api.js', 'pages/genealogy/g04-first-ancestor.vue', 'pages/skeleton/content-list-skeleton.vue', 'pages/family/f02-publish-feed.vue')
|
||||||
foreach ($file in $requiredFiles) {
|
foreach ($file in $requiredFiles) {
|
||||||
if (-not (Test-Path (Join-Path $root $file))) {
|
if (-not (Test-Path (Join-Path $root $file))) {
|
||||||
throw "Missing core-flow file: $file"
|
throw "Missing core-flow file: $file"
|
||||||
@@ -46,16 +46,16 @@ if ($api -notmatch 'getProfile') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json')
|
$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json')
|
||||||
if ($pages -notmatch 'pages/lineage/first-person') {
|
if ($pages -notmatch 'pages/genealogy/g04-first-ancestor') {
|
||||||
throw 'Missing first-person page route'
|
throw 'Missing first-person page route'
|
||||||
}
|
}
|
||||||
foreach ($route in @('pages/content/list', 'pages/content/editor')) {
|
foreach ($route in @('pages/skeleton/content-list-skeleton', 'pages/family/f02-publish-feed')) {
|
||||||
if ($pages -notmatch [regex]::Escape($route)) {
|
if ($pages -notmatch [regex]::Escape($route)) {
|
||||||
throw "Missing content page route: $route"
|
throw "Missing content page route: $route"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($pageFile in @('pages/genealogy/detail.vue', 'pages/tree/index.vue', 'pages/member/detail.vue')) {
|
foreach ($pageFile in @('pages/genealogy/g05-genealogy-overview.vue', 'pages/tree/t01-tree-overview.vue', 'pages/tree/t03-member-profile.vue')) {
|
||||||
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root $pageFile)
|
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root $pageFile)
|
||||||
if ($page -match "id=1001|id=3") {
|
if ($page -match "id=1001|id=3") {
|
||||||
throw "Hard-coded route ID remains in $pageFile"
|
throw "Hard-coded route ID remains in $pageFile"
|
||||||
@@ -71,33 +71,33 @@ foreach ($method in @('markNotificationRead', 'markAllNotificationsRead')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($pageFile in @('pages/genealogy/applications.vue', 'pages/notification/index.vue')) {
|
foreach ($pageFile in @('pages/genealogy/g10-application-review.vue', 'pages/notification/n01-message-center.vue')) {
|
||||||
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root $pageFile)
|
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root $pageFile)
|
||||||
if ($page -match '1001') {
|
if ($page -match '1001') {
|
||||||
throw "Hard-coded genealogy ID remains in $pageFile"
|
throw "Hard-coded genealogy ID remains in $pageFile"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$applications = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/applications.vue')
|
$applications = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/g10-application-review.vue')
|
||||||
if ($applications -notmatch 'genealogyContext') {
|
if ($applications -notmatch 'genealogyContext') {
|
||||||
throw 'Missing genealogy context in applications page'
|
throw 'Missing genealogy context in applications page'
|
||||||
}
|
}
|
||||||
|
|
||||||
$notifications = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/notification/index.vue')
|
$notifications = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/notification/n01-message-center.vue')
|
||||||
foreach ($method in @('markNotificationRead', 'markAllNotificationsRead')) {
|
foreach ($method in @('markNotificationRead', 'markAllNotificationsRead')) {
|
||||||
if ($notifications -notmatch [regex]::Escape($method)) {
|
if ($notifications -notmatch [regex]::Escape($method)) {
|
||||||
throw "Notification page does not use $method"
|
throw "Notification page does not use $method"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$family = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/family/index.vue')
|
$family = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/family/f01-family-feed.vue')
|
||||||
foreach ($token in @('genealogyContext', 'openSection', 'pages/content/list', 'pages/content/editor')) {
|
foreach ($token in @('genealogyContext', 'openSection', 'pages/skeleton/content-list-skeleton', 'pages/family/f02-publish-feed')) {
|
||||||
if ($family -notmatch [regex]::Escape($token)) {
|
if ($family -notmatch [regex]::Escape($token)) {
|
||||||
throw "Missing family content flow: $token"
|
throw "Missing family content flow: $token"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$profile = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/profile/index.vue')
|
$profile = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/profile/m01-profile-home.vue')
|
||||||
foreach ($token in @('appApi', 'getProfile', 'profile.name')) {
|
foreach ($token in @('appApi', 'getProfile', 'profile.name')) {
|
||||||
if ($profile -notmatch [regex]::Escape($token)) {
|
if ($profile -notmatch [regex]::Escape($token)) {
|
||||||
throw "Missing dynamic profile behavior: $token"
|
throw "Missing dynamic profile behavior: $token"
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$root = Split-Path -Parent $PSScriptRoot
|
||||||
|
$errors = [System.Collections.Generic.List[string]]::new()
|
||||||
|
$sourceFiles = Get-ChildItem -LiteralPath (Join-Path $root 'pages'), (Join-Path $root 'components') -Recurse -File |
|
||||||
|
Where-Object { $_.Extension -in '.vue', '.js', '.scss' }
|
||||||
|
|
||||||
|
foreach ($sourceFile in $sourceFiles) {
|
||||||
|
$content = Get-Content -Raw -Encoding UTF8 $sourceFile.FullName
|
||||||
|
$matches = [regex]::Matches($content, '/static/assets/[^"''\s<]+')
|
||||||
|
|
||||||
|
foreach ($match in $matches) {
|
||||||
|
$assetUrl = $match.Value
|
||||||
|
$assetPath = Join-Path $root $assetUrl.TrimStart('/')
|
||||||
|
$relativeSource = $sourceFile.FullName.Substring($root.Length + 1)
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $assetPath)) {
|
||||||
|
$errors.Add("Missing asset $assetUrl referenced by $relativeSource")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($assetUrl -notmatch '^/static/assets/(foundation|modules)/') {
|
||||||
|
$errors.Add("Legacy asset path $assetUrl referenced by $relativeSource")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($assetUrl -match '(-v[0-9]+|-source)\.') {
|
||||||
|
$errors.Add("Temporary asset name $assetUrl referenced by $relativeSource")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors.Count -gt 0) {
|
||||||
|
throw "Foundation asset audit failed:`n$($errors -join "`n")"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output 'PASS foundation asset audit'
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$root = Split-Path -Parent $PSScriptRoot
|
||||||
|
$errors = [System.Collections.Generic.List[string]]::new()
|
||||||
|
$pageCommentPrefix = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('PCEtLSDpobXpnaLnvJblj7fvvJo='))
|
||||||
|
|
||||||
|
$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json') | ConvertFrom-Json
|
||||||
|
$registeredPaths = @($pages.pages | ForEach-Object { $_.path })
|
||||||
|
|
||||||
|
function Test-ExpectedPage {
|
||||||
|
param(
|
||||||
|
[string]$ExpectedPath,
|
||||||
|
[string]$PageId
|
||||||
|
)
|
||||||
|
|
||||||
|
$pageFile = Join-Path $root ($expectedPath + '.vue')
|
||||||
|
if ($registeredPaths -notcontains $expectedPath) {
|
||||||
|
$errors.Add("Missing final route: $expectedPath")
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $pageFile)) {
|
||||||
|
$errors.Add("Missing final page file: $($expectedPath).vue")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = Get-Content -Raw -Encoding UTF8 $pageFile
|
||||||
|
if (-not $content.StartsWith($pageCommentPrefix + $pageId)) {
|
||||||
|
$errors.Add("Missing Chinese page header: $($expectedPath).vue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-AssetDirectory {
|
||||||
|
param([string]$AssetDirectory)
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath (Join-Path $root $assetDirectory))) {
|
||||||
|
$errors.Add("Missing foundation asset directory: $assetDirectory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# P-00 final routes: each file name identifies the page and its design ID.
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/auth/a01-entry' -PageId 'A-01'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/auth/a02-login' -PageId 'A-02'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/genealogy/g01-my-genealogies' -PageId 'G-01'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/genealogy/g03-create-genealogy' -PageId 'G-03'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/genealogy/g04-first-ancestor' -PageId 'G-04'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/genealogy/g05-genealogy-overview' -PageId 'G-05'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/genealogy/g06-search-genealogies' -PageId 'G-06'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/genealogy/g10-application-review' -PageId 'G-10'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/tree/t01-tree-overview' -PageId 'T-01'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/tree/t03-member-profile' -PageId 'T-03'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/family/f01-family-feed' -PageId 'F-01'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/family/f02-publish-feed' -PageId 'F-02'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/skeleton/content-list-skeleton' -PageId 'S-01'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/notification/n01-message-center' -PageId 'N-01'
|
||||||
|
Test-ExpectedPage -ExpectedPath 'pages/profile/m01-profile-home' -PageId 'M-01'
|
||||||
|
|
||||||
|
# Both asset directories are required so opaque bases and transparent overlays stay separate.
|
||||||
|
Test-AssetDirectory -AssetDirectory 'static/assets/foundation/opaque'
|
||||||
|
Test-AssetDirectory -AssetDirectory 'static/assets/foundation/transparent'
|
||||||
|
|
||||||
|
if ($errors.Count -gt 0) {
|
||||||
|
throw "P-00 foundation structure audit failed:`n$($errors -join "`n")"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output 'PASS foundation structure audit'
|
||||||
@@ -4,12 +4,12 @@ $root = Split-Path -Parent $PSScriptRoot
|
|||||||
Add-Type -AssemblyName System.Drawing
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
|
||||||
$assets = @(
|
$assets = @(
|
||||||
'static/assets/icons/common/location-v1.png',
|
'static/assets/foundation/transparent/meta-location.png',
|
||||||
'static/assets/icons/common/member-meta-v1.png',
|
'static/assets/foundation/transparent/meta-member.png',
|
||||||
'static/assets/icons/common/admin-v1.png',
|
'static/assets/foundation/transparent/meta-admin.png',
|
||||||
'static/assets/icons/genealogy/seal-current-frame-v1.png',
|
'static/assets/modules/genealogy/transparent/current-seal-frame.png',
|
||||||
'static/assets/icons/genealogy/seal-row-frame-v1.png',
|
'static/assets/modules/genealogy/transparent/row-seal-frame.png',
|
||||||
'static/assets/icons/action/create-cloud-v2.png'
|
'static/assets/modules/genealogy/transparent/create-cloud.png'
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($asset in $assets) {
|
foreach ($asset in $assets) {
|
||||||
|
|||||||
@@ -2,33 +2,32 @@ $ErrorActionPreference = 'Stop'
|
|||||||
|
|
||||||
$root = Split-Path -Parent $PSScriptRoot
|
$root = Split-Path -Parent $PSScriptRoot
|
||||||
$requiredAssets = @(
|
$requiredAssets = @(
|
||||||
'static/assets/icons/brand/jiapu-seal-logo.png',
|
'static/assets/foundation/transparent/brand-seal.png',
|
||||||
'static/assets/icons/brand/jiapu-seal-logo-header.png',
|
'static/assets/foundation/transparent/notice.png',
|
||||||
'static/assets/icons/common/notice-v3.png',
|
'static/assets/foundation/transparent/chevron-right.png',
|
||||||
'static/assets/icons/common/chevron-right-v2.png',
|
'static/assets/foundation/transparent/meta-location.png',
|
||||||
'static/assets/icons/common/location-v1.png',
|
'static/assets/foundation/transparent/meta-member.png',
|
||||||
'static/assets/icons/common/member-meta-v1.png',
|
'static/assets/foundation/transparent/meta-admin.png',
|
||||||
'static/assets/icons/common/admin-v1.png',
|
'static/assets/modules/genealogy/transparent/shortcut-tree.png',
|
||||||
'static/assets/icons/genealogy/tree-v2.png',
|
'static/assets/modules/genealogy/transparent/shortcut-members.png',
|
||||||
'static/assets/icons/genealogy/members-v2.png',
|
'static/assets/modules/genealogy/transparent/shortcut-generation-poem.png',
|
||||||
'static/assets/icons/genealogy/generation-poem-v2.png',
|
'static/assets/modules/genealogy/transparent/shortcut-application.png',
|
||||||
'static/assets/icons/genealogy/application-v2.png',
|
'static/assets/modules/genealogy/transparent/current-seal-frame.png',
|
||||||
'static/assets/icons/genealogy/seal-current-frame-v1.png',
|
'static/assets/modules/genealogy/transparent/row-seal-frame.png',
|
||||||
'static/assets/icons/genealogy/seal-row-frame-v1.png',
|
'static/assets/modules/genealogy/transparent/add.png',
|
||||||
'static/assets/icons/action/add-v2.png',
|
'static/assets/modules/genealogy/transparent/create-cloud.png',
|
||||||
'static/assets/icons/action/create-cloud-v2.png',
|
'static/assets/foundation/transparent/tab-genealogy.png',
|
||||||
'static/assets/icons/tab/genealogy-v4.png',
|
'static/assets/foundation/transparent/tab-genealogy-active.png',
|
||||||
'static/assets/icons/tab/genealogy-active-v4.png',
|
'static/assets/foundation/transparent/tab-family.png',
|
||||||
'static/assets/icons/tab/family-v4.png',
|
'static/assets/foundation/transparent/tab-family-active.png',
|
||||||
'static/assets/icons/tab/family-active-v4.png',
|
'static/assets/foundation/transparent/tab-profile.png',
|
||||||
'static/assets/icons/tab/profile-v4.png',
|
'static/assets/foundation/transparent/tab-profile-active.png',
|
||||||
'static/assets/icons/tab/profile-active-v4.png',
|
'static/assets/modules/genealogy/transparent/current-slip-frame.png',
|
||||||
'static/assets/backgrounds/genealogy-current-slip-frame.png',
|
'static/assets/modules/genealogy/transparent/list-slip-frame.png',
|
||||||
'static/assets/backgrounds/genealogy-list-slip-frame.png',
|
'static/assets/foundation/opaque/root-header-cinnabar.jpg',
|
||||||
'static/assets/backgrounds/header-cinnabar-texture-v2.jpg',
|
'static/assets/foundation/opaque/page-paper.jpg',
|
||||||
'static/assets/backgrounds/paper-rice-texture-v2.jpg',
|
'static/assets/foundation/transparent/footer-mountain-bamboo.png',
|
||||||
'static/assets/backgrounds/footer-mountain-bamboo.png',
|
'static/assets/modules/genealogy/transparent/section-divider.png'
|
||||||
'static/assets/backgrounds/genealogy-section-divider-v2.png'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($asset in $requiredAssets) {
|
foreach ($asset in $requiredAssets) {
|
||||||
@@ -40,14 +39,14 @@ foreach ($asset in $requiredAssets) {
|
|||||||
$header = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/PageHeader.vue')
|
$header = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/PageHeader.vue')
|
||||||
$tabbar = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/AppTabbar.vue')
|
$tabbar = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/AppTabbar.vue')
|
||||||
|
|
||||||
if ($header -notmatch 'jiapu-seal-logo-header\.png') { throw 'Header does not use the approved tight brand PNG.' }
|
if ($header -notmatch 'brand-seal\.png') { throw 'Header does not use the approved tight brand PNG.' }
|
||||||
if ($header -notmatch 'jiapu-seal-logo-header\.png') { throw 'Header does not use the tight header Logo asset.' }
|
if ($header -notmatch 'brand-seal\.png') { throw 'Header does not use the tight header Logo asset.' }
|
||||||
if ($header -notmatch 'notice-v3\.png') { throw 'Header does not use the approved notice PNG.' }
|
if ($header -notmatch 'notice\.png') { throw 'Header does not use the approved notice PNG.' }
|
||||||
if ($header -notmatch 'unreadCount\s*>\s*0') { throw 'Header badge is not conditional on unread count.' }
|
if ($header -notmatch 'unreadCount\s*>\s*0') { throw 'Header badge is not conditional on unread count.' }
|
||||||
if ($header -notmatch 'class="header-hall"') { throw 'Header does not isolate the hall line-art as a low-contrast background layer.' }
|
if ($header -notmatch 'class="header-hall"') { throw 'Header does not isolate the hall line-art as a low-contrast background layer.' }
|
||||||
if ($header -match 'background-image: url') { throw 'Header still applies hall line-art directly to the red header surface.' }
|
if ($header -match 'background-image: url') { throw 'Header still applies hall line-art directly to the red header surface.' }
|
||||||
if ($header -notmatch 'opacity:\s*\.29') { throw 'Header hall line-art is not visible enough for the approved eave layer.' }
|
if ($header -notmatch 'opacity:\s*\.29') { throw 'Header hall line-art is not visible enough for the approved eave layer.' }
|
||||||
if ($header -notmatch 'header-cinnabar-texture-v2\.jpg') { throw 'Header does not use the approved cinnabar texture asset.' }
|
if ($header -notmatch 'root-header-cinnabar\.jpg') { throw 'Header does not use the approved cinnabar texture asset.' }
|
||||||
if ($header -notmatch 'height:\s*calc\(124rpx\s*\+\s*var\(--status-bar-height,\s*0px\)\)') { throw 'Header does not preserve the approved visual height above the system status bar.' }
|
if ($header -notmatch 'height:\s*calc\(124rpx\s*\+\s*var\(--status-bar-height,\s*0px\)\)') { throw 'Header does not preserve the approved visual height above the system status bar.' }
|
||||||
if ($header -notmatch 'padding-top:\s*var\(--status-bar-height,\s*0px\)') { throw 'Header does not reserve the real system status-bar height.' }
|
if ($header -notmatch 'padding-top:\s*var\(--status-bar-height,\s*0px\)') { throw 'Header does not reserve the real system status-bar height.' }
|
||||||
if ($tabbar -match '/static/icons/tab-') { throw 'Tabbar still references rejected legacy tab assets.' }
|
if ($tabbar -match '/static/icons/tab-') { throw 'Tabbar still references rejected legacy tab assets.' }
|
||||||
@@ -55,20 +54,20 @@ if ($tabbar -notmatch 'safe-area-inset-bottom') { throw 'Tabbar has no Android s
|
|||||||
if ($tabbar -notmatch 'font-size:\s*30rpx') { throw 'Tabbar label size does not match the approved visual weight.' }
|
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.' }
|
if ($tabbar -notmatch 'width:\s*64rpx') { throw 'Tabbar icon size does not match the approved visual weight.' }
|
||||||
|
|
||||||
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/index.vue')
|
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/g01-my-genealogies.vue')
|
||||||
if ($page -match "from '@/utils/api\.js'") { throw 'G-01 visual phase must not call the remote API.' }
|
if ($page -match "from '@/utils/api\.js'") { throw 'G-01 visual phase must not call the remote API.' }
|
||||||
foreach ($state in @('isLoading', 'hasGenealogies', 'createdGenealogies', 'joinedGenealogies')) {
|
foreach ($state in @('isLoading', 'hasGenealogies', 'createdGenealogies', 'joinedGenealogies')) {
|
||||||
if ($page -notmatch $state) { throw "G-01 is missing presentation state: $state" }
|
if ($page -notmatch $state) { throw "G-01 is missing presentation state: $state" }
|
||||||
}
|
}
|
||||||
foreach ($asset in @('tree-v2.png', 'members-v2.png', 'generation-poem-v2.png', 'application-v2.png', 'add-v2.png')) {
|
foreach ($asset in @('shortcut-tree.png', 'shortcut-members.png', 'shortcut-generation-poem.png', 'shortcut-application.png', 'add.png')) {
|
||||||
if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume $asset" }
|
if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume $asset" }
|
||||||
}
|
}
|
||||||
if ($page -notmatch 'genealogy-current-slip-frame\.png') { throw 'G-01 does not use the approved current genealogy frame asset.' }
|
if ($page -notmatch 'current-slip-frame\.png') { throw 'G-01 does not use the approved current genealogy frame asset.' }
|
||||||
foreach ($token in @('current-summary', 'current-meta-item', 'current-seal-frame', 'location-v1.png', 'member-meta-v1.png', 'admin-v1.png', 'create-cloud-v2.png')) {
|
foreach ($token in @('current-summary', 'current-meta-item', 'current-seal-frame', 'meta-location.png', 'meta-member.png', 'meta-admin.png', 'create-cloud.png')) {
|
||||||
if ($page -notmatch $token) { throw "G-01 current panel is missing $token" }
|
if ($page -notmatch $token) { throw "G-01 current panel is missing $token" }
|
||||||
}
|
}
|
||||||
if ($page -notmatch 'width:\s*82rpx') { throw 'G-01 shortcut icons remain below the reference visual size.' }
|
if ($page -notmatch 'width:\s*82rpx') { throw 'G-01 shortcut icons remain below the reference visual size.' }
|
||||||
foreach ($asset in @('paper-rice-texture-v2.jpg', 'footer-mountain-bamboo.png', 'genealogy-section-divider-v2.png')) {
|
foreach ($asset in @('page-paper.jpg', 'footer-mountain-bamboo.png', 'section-divider.png')) {
|
||||||
if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume the approved background asset: $asset" }
|
if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume the approved background asset: $asset" }
|
||||||
}
|
}
|
||||||
if ($page -match 'footer-mountain-bamboo-v2\.png') { throw 'G-01 still uses the solid mountain footer asset instead of the translucent watercolor landscape.' }
|
if ($page -match 'footer-mountain-bamboo-v2\.png') { throw 'G-01 still uses the solid mountain footer asset instead of the translucent watercolor landscape.' }
|
||||||
@@ -77,13 +76,13 @@ if ($page -notmatch '(?s)\.page-footer-landscape\s*\{.*?opacity:\s*0\.5') { thro
|
|||||||
if ($page -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.88\)') { throw 'G-01 current genealogy card retains a rectangular pale backing behind its transparent corners.' }
|
if ($page -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.88\)') { throw 'G-01 current genealogy card retains a rectangular pale backing behind its transparent corners.' }
|
||||||
|
|
||||||
$card = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/GenealogyCard.vue')
|
$card = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/GenealogyCard.vue')
|
||||||
if ($card -notmatch 'genealogy-list-slip-frame\.png') { throw 'G-01 list rows do not use the approved slip frame asset.' }
|
if ($card -notmatch 'list-slip-frame\.png') { throw 'G-01 list rows do not use the approved slip frame asset.' }
|
||||||
if ($card -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.(9|96)\)') { throw 'G-01 list cards retain a rectangular pale backing behind their transparent corners.' }
|
if ($card -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.(9|96)\)') { throw 'G-01 list cards retain a rectangular pale backing behind their transparent corners.' }
|
||||||
foreach ($token in @('seal-row-frame-v1.png', 'card-main', 'card-title-row', 'card-detail-row', 'card-updated')) {
|
foreach ($token in @('row-seal-frame.png', 'card-main', 'card-title-row', 'card-detail-row', 'card-updated')) {
|
||||||
if ($card -notmatch $token) { throw "G-01 list card is missing $token" }
|
if ($card -notmatch $token) { throw "G-01 list card is missing $token" }
|
||||||
}
|
}
|
||||||
if ($card -match 'background:\s*#b93b2e') { throw 'G-01 list seal is still a plain CSS red block.' }
|
if ($card -match 'background:\s*#b93b2e') { throw 'G-01 list seal is still a plain CSS red block.' }
|
||||||
foreach ($asset in @('location-v1.png', 'member-meta-v1.png', 'admin-v1.png', 'seal-current-frame-v1.png', 'seal-row-frame-v1.png', 'create-cloud-v2.png')) {
|
foreach ($asset in @('meta-location.png', 'meta-member.png', 'meta-admin.png', 'current-seal-frame.png', 'row-seal-frame.png', 'create-cloud.png')) {
|
||||||
if ($page -notmatch [regex]::Escape($asset) -and $card -notmatch [regex]::Escape($asset)) {
|
if ($page -notmatch [regex]::Escape($asset) -and $card -notmatch [regex]::Escape($asset)) {
|
||||||
throw "G-01 does not consume $asset"
|
throw "G-01 does not consume $asset"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,21 @@ $requiredFiles = @(
|
|||||||
'utils/api.js',
|
'utils/api.js',
|
||||||
'components/AppTabbar.vue',
|
'components/AppTabbar.vue',
|
||||||
'components/PageHeader.vue',
|
'components/PageHeader.vue',
|
||||||
'pages/auth/login.vue',
|
'pages/auth/a01-entry.vue',
|
||||||
'pages/genealogy/index.vue',
|
'pages/auth/a02-login.vue',
|
||||||
'pages/genealogy/create.vue',
|
'pages/genealogy/g01-my-genealogies.vue',
|
||||||
'pages/genealogy/detail.vue',
|
'pages/genealogy/g03-create-genealogy.vue',
|
||||||
'pages/genealogy/search.vue',
|
'pages/genealogy/g04-first-ancestor.vue',
|
||||||
'pages/genealogy/applications.vue',
|
'pages/genealogy/g05-genealogy-overview.vue',
|
||||||
'pages/tree/index.vue',
|
'pages/genealogy/g06-search-genealogies.vue',
|
||||||
'pages/member/detail.vue',
|
'pages/genealogy/g10-application-review.vue',
|
||||||
'pages/family/index.vue',
|
'pages/tree/t01-tree-overview.vue',
|
||||||
'pages/profile/index.vue',
|
'pages/tree/t03-member-profile.vue',
|
||||||
'pages/notification/index.vue',
|
'pages/family/f01-family-feed.vue',
|
||||||
|
'pages/family/f02-publish-feed.vue',
|
||||||
|
'pages/skeleton/content-list-skeleton.vue',
|
||||||
|
'pages/profile/m01-profile-home.vue',
|
||||||
|
'pages/notification/n01-message-center.vue',
|
||||||
'static/icons/tab-genealogy.png',
|
'static/icons/tab-genealogy.png',
|
||||||
'static/icons/tab-family.png',
|
'static/icons/tab-family.png',
|
||||||
'static/icons/tab-profile.png'
|
'static/icons/tab-profile.png'
|
||||||
@@ -37,17 +41,21 @@ if ($missing) {
|
|||||||
$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json') | ConvertFrom-Json
|
$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json') | ConvertFrom-Json
|
||||||
$paths = @($pages.pages | ForEach-Object { $_.path })
|
$paths = @($pages.pages | ForEach-Object { $_.path })
|
||||||
$requiredRoutes = @(
|
$requiredRoutes = @(
|
||||||
'pages/auth/login',
|
'pages/auth/a01-entry',
|
||||||
'pages/genealogy/index',
|
'pages/auth/a02-login',
|
||||||
'pages/genealogy/create',
|
'pages/genealogy/g01-my-genealogies',
|
||||||
'pages/genealogy/detail',
|
'pages/genealogy/g03-create-genealogy',
|
||||||
'pages/genealogy/search',
|
'pages/genealogy/g04-first-ancestor',
|
||||||
'pages/genealogy/applications',
|
'pages/genealogy/g05-genealogy-overview',
|
||||||
'pages/tree/index',
|
'pages/genealogy/g06-search-genealogies',
|
||||||
'pages/member/detail',
|
'pages/genealogy/g10-application-review',
|
||||||
'pages/family/index',
|
'pages/tree/t01-tree-overview',
|
||||||
'pages/profile/index',
|
'pages/tree/t03-member-profile',
|
||||||
'pages/notification/index'
|
'pages/family/f01-family-feed',
|
||||||
|
'pages/family/f02-publish-feed',
|
||||||
|
'pages/skeleton/content-list-skeleton',
|
||||||
|
'pages/profile/m01-profile-home',
|
||||||
|
'pages/notification/n01-message-center'
|
||||||
)
|
)
|
||||||
|
|
||||||
$missingRoutes = $requiredRoutes | Where-Object { $_ -notin $paths }
|
$missingRoutes = $requiredRoutes | Where-Object { $_ -notin $paths }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"hash": "569c6bd0",
|
"hash": "7ee1354b",
|
||||||
"configHash": "dc147105",
|
"configHash": "ce3416a0",
|
||||||
"lockfileHash": "e3b0c442",
|
"lockfileHash": "e3b0c442",
|
||||||
"browserHash": "e2dbcede",
|
"browserHash": "29a8e5f0",
|
||||||
"optimized": {},
|
"optimized": {},
|
||||||
"chunks": {}
|
"chunks": {}
|
||||||
}
|
}
|
||||||