v0.1.0 界面框架:抽屉骨架 + 13 个模块页面
对应服务端 FamilyBook(家书)的鸿蒙客户端首版。本轮只做**界面框架**, 数据全部是演示数据 —— 服务端尚无客户端接口,所有增删改只做入口与样式, 不落任何数据。 ## 骨架 左侧抽屉 + 右侧内容。抽屉两套形态由窗口短边决定(≥600vp 为宽屏): 宽屏常驻侧栏、窄屏滑出层。判据取「短边」而非「宽度」,因为短边在旋转时 不变,比例接近 1:1 的设备横竖屏布局天然一致。 ## 模块(与 Web 端 Module::LIST 一一对应,key 即路由名) 首页、家谱、名册、观影、旅程、日程、资源、任务、纪念日、财务、读书、 导航、笔记;另有通知消息、个人设置两个附加页。 ## 与 Web 端保持一致的几处 · 模块清单唯一来源在 model/Modules.ets,加模块只改那一处; · 模块主题色三档由主色按 HSL 压明度算出,预设色板与 Web 端逐值一致; · 术语沿用 Web 端固定下来的口径(旅程 ⊃ 行程;观影的「影院」不单独占 抽屉位,从模块内设置进); · 页面结构照 Web 端的信息层次(首页信息面板 / 财务概览在账目之前 / 纪念日「即将到来」在顶 / 任务按状态而非按看板)。 ## 编译验证 hvigorw assembleHap --mode module -p product=default → BUILD SUCCESSFUL,0 error / 0 warning(含一次删 build 后的干净重建) 过程中修掉的编译期问题: · Circle().fill() 是 API 26 才有的重载(compatibleSdk 是 24) → 换「方块 + 圆角」画圆,零版本风险; · setWindowBackgroundColor 对 phone/tablet 不可用 → 去掉(页面已铺满同色); · window.getLastWindow() 只有异步版 → 由 Ability 侧同步取窗存 AppStorage; · 颜色类 @Param 声明成 string 导致传 Resource 报错 → 统一改 ResourceColor; · 页面在 pages/<模块>/ 下,公共件 import 相对路径要多退一级。 (首轮编译 95 error / 18 warn,逐条定位到 5 类根因后归零。)
This commit is contained in:
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 演示数据(鸿蒙端)
|
||||
*
|
||||
* ⚠️ 本文件是**临时占位**:接口还没做,页面要能看出长什么样,所以这里造了一批
|
||||
* 结构完整的假数据。接接口时删掉本文件即可 —— 页面只依赖 `model/Types.ets`
|
||||
* 里的类型,不依赖这些常量本身。
|
||||
*
|
||||
* ⚠️ 数据刻意照 Web 端的真实形态造(首页 12 个模块卡、观影有海报位、
|
||||
* 旅程分「计划/实际」两阶段的行程段、任务按看板分组带逾期标记…),
|
||||
* 这样「页面该长什么样」的判断才站得住,而不是凭空拍。
|
||||
*
|
||||
* ⚠️ 全文件统一 `Fb` 前缀命名,避免与其他文件顶层名冲突(arkts-no-decl-merging)。
|
||||
*/
|
||||
|
||||
import { FbAccount, FbAnniversaryItem, FbBookItem, FbCalBook, FbCalEvent, FbGroup,
|
||||
FbKinChip, FbKinRelation, FbLedgerBook, FbLedgerEntry, FbModuleCard, FbMovieItem,
|
||||
FbNavLink, FbNoteItem, FbNotificationItem, FbResourceItem, FbRosterItem, FbSession,
|
||||
FbTaskItem, FbTripItem, FbTripNode, FbWeather, FbWeekStat } from './Types';
|
||||
|
||||
/** 假数据里的「我」 */
|
||||
export const FB_DEMO_ME: FbAccount = {
|
||||
id: 1,
|
||||
name: '杨永全',
|
||||
kinship: '我',
|
||||
avatarColor: '#A84A28',
|
||||
role: 'admin',
|
||||
isGuest: false,
|
||||
canWrite: true
|
||||
};
|
||||
|
||||
/** 假会话 */
|
||||
export const FB_DEMO_SESSION: FbSession = {
|
||||
loggedIn: true,
|
||||
familyId: 1,
|
||||
familyTitle: '杨家',
|
||||
slogan: '记录家庭的全部生活,一本用岁月写成的家谱',
|
||||
me: FB_DEMO_ME
|
||||
};
|
||||
|
||||
/** 造一个头像 chip 的快捷函数(少写重复字面量) */
|
||||
function fbChip(id: number, shown: string, kinship: string, color: string): FbKinChip {
|
||||
return { id: id, shown: shown, kinship: kinship, avatarColor: color, isAdmin: false };
|
||||
}
|
||||
|
||||
/** 首页「我的家人」 */
|
||||
export const FB_DEMO_KIN: FbKinChip[] = [
|
||||
fbChip(2, '爱人', '配偶', '#9A5C7A'),
|
||||
fbChip(3, '儿子', '儿子', '#4A7C82'),
|
||||
fbChip(4, '女儿', '女儿', '#6B5B95'),
|
||||
fbChip(5, '爸爸', '父亲', '#8A6B4B'),
|
||||
fbChip(6, '妈妈', '母亲', '#B0763A'),
|
||||
fbChip(7, '弟弟', '弟弟', '#3F7D5A')
|
||||
];
|
||||
|
||||
/** 首页天气 */
|
||||
export const FB_DEMO_WEATHER: FbWeather = {
|
||||
place: '青岛市 · 崂山区',
|
||||
temp: 21,
|
||||
text: '多云',
|
||||
wind: '东南风 3 级'
|
||||
};
|
||||
|
||||
/** 首页近七天统计 */
|
||||
export const FB_DEMO_WEEK_STAT: FbWeekStat = {
|
||||
events: 11,
|
||||
todos: 4,
|
||||
overdue: 1
|
||||
};
|
||||
|
||||
/** 首页日子标记 */
|
||||
export const FB_DEMO_MARKS: string[] = ['寒露', '重阳节', '儿子生日'];
|
||||
|
||||
/** 首页模块卡片(顺序与 Web 端 Module::DEFAULT_NAV 一致) */
|
||||
export const FB_DEMO_MODULE_CARDS: FbModuleCard[] = [
|
||||
{ key: 'people', name: '名册', big: '12 人', lines: ['1089 人在这个家里'], color: '#4A7C82' },
|
||||
{ key: 'movies', name: '观影', big: '86 部', lines: ['最近看了《漫长的季节》'], color: '#7A4E8C' },
|
||||
{ key: 'trips', name: '旅程', big: '9 次', lines: ['最近去了青岛·崂山'], color: '#3F7D5A' },
|
||||
{ key: 'calendar', name: '日程', big: '11 条', lines: ['未来七天'], color: '#B0763A' },
|
||||
{ key: 'resources', name: '资源', big: '37 项', lines: ['证件 / 资料 / 附件'], color: '#5566A8' },
|
||||
{ key: 'tasks', name: '任务', big: '3 件在做', lines: ['4 件待办 · 1 件逾期'], color: '#A8574F' },
|
||||
{ key: 'days', name: '纪念日', big: '6 天后', lines: ['结婚周年'], color: '#B4633C' },
|
||||
{ key: 'ledger', name: '财务', big: '¥86.4 万', lines: ['3 本账(净资产)'], color: '#5E7A3C' },
|
||||
{ key: 'books', name: '读书', big: '2 本在读', lines: ['31 本读完 · 48 条笔记'], color: '#6B5B95' },
|
||||
{ key: 'nav', name: '导航', big: '24 个', lines: ['我的常用站点'], color: '#3E7A9E' },
|
||||
{ key: 'notes', name: '笔记', big: '63 条', lines: ['最近:阳台改造清单'], color: '#7A6A4F' }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 名册
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_ROSTER: FbRosterItem[] = [
|
||||
{ id: 1, name: '杨永全', kinship: '我', group: '本家', tags: ['管理员'], avatarColor: '#A84A28', roster: 'member', birthday: '03-12', isAdmin: true },
|
||||
{ id: 2, name: '李慧', kinship: '配偶', group: '本家', tags: [], avatarColor: '#9A5C7A', roster: 'member', birthday: '07-08', isAdmin: false },
|
||||
{ id: 3, name: '杨小满', kinship: '儿子', group: '本家', tags: ['学生'], avatarColor: '#4A7C82', roster: 'member', birthday: '10-18', isAdmin: false },
|
||||
{ id: 4, name: '杨小禾', kinship: '女儿', group: '本家', tags: ['学生'], avatarColor: '#6B5B95', roster: 'member', birthday: '01-04', isAdmin: false },
|
||||
{ id: 5, name: '杨建国', kinship: '父亲', group: '长辈', tags: ['退休'], avatarColor: '#8A6B4B', roster: 'member', birthday: '05-21', isAdmin: false },
|
||||
{ id: 6, name: '王秀兰', kinship: '母亲', group: '长辈', tags: [], avatarColor: '#B0763A', roster: 'member', birthday: '11-30', isAdmin: false },
|
||||
{ id: 7, name: '杨永强', kinship: '弟弟', group: '本家', tags: [], avatarColor: '#3F7D5A', roster: 'member', birthday: '08-15', isAdmin: false },
|
||||
{ id: 8, name: '李国栋', kinship: '岳父', group: '姻亲', tags: [], avatarColor: '#5566A8', roster: 'member', birthday: '02-26', isAdmin: false },
|
||||
{ id: 9, name: '陈静', kinship: '岳母', group: '姻亲', tags: [], avatarColor: '#A8574F', roster: 'member', birthday: '09-09', isAdmin: false },
|
||||
{ id: 10, name: '张伟', kinship: '朋友', group: '亲友', tags: ['同事'], avatarColor: '#5E7A3C', roster: 'friend', birthday: '06-14', isAdmin: false },
|
||||
{ id: 11, name: '刘敏', kinship: '朋友', group: '亲友', tags: ['同学'], avatarColor: '#7A6A4F', roster: 'friend', birthday: '12-02', isAdmin: false },
|
||||
{ id: 12, name: '赵鹏', kinship: '朋友', group: '亲友', tags: ['邻居'], avatarColor: '#3D6B8E', roster: 'friend', birthday: '04-17', isAdmin: false }
|
||||
];
|
||||
|
||||
/** 家谱连线(父母 / 配偶 / 子女) */
|
||||
export const FB_DEMO_KIN_RELATIONS: FbKinRelation[] = [
|
||||
{ id: 1, fromId: 1, fromName: '杨永全', toId: 5, toName: '杨建国', kind: 'parent', label: '父亲' },
|
||||
{ id: 2, fromId: 1, fromName: '杨永全', toId: 6, toName: '王秀兰', kind: 'parent', label: '母亲' },
|
||||
{ id: 3, fromId: 1, fromName: '杨永全', toId: 2, toName: '李慧', kind: 'spouse', label: '配偶' },
|
||||
{ id: 4, fromId: 1, fromName: '杨永全', toId: 3, toName: '杨小满', kind: 'child', label: '儿子' },
|
||||
{ id: 5, fromId: 1, fromName: '杨永全', toId: 4, toName: '杨小禾', kind: 'child', label: '女儿' },
|
||||
{ id: 6, fromId: 1, fromName: '杨永全', toId: 7, toName: '杨永强', kind: 'child', label: '弟弟' },
|
||||
{ id: 7, fromId: 2, fromName: '李慧', toId: 8, toName: '李国栋', kind: 'parent', label: '岳父' },
|
||||
{ id: 8, fromId: 2, fromName: '李慧', toId: 9, toName: '陈静', kind: 'parent', label: '岳母' }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 观影
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_MOVIES: FbMovieItem[] = [
|
||||
{ id: 1, title: '漫长的季节', watchedAt: '2026-10-05 19:30', cinemaName: '家里', cinemaColor: '#8A6B4B', rating: 9.2, reviewCount: 3, viewers: [fbChip(1, '我', '我', '#A84A28'), fbChip(2, '爱人', '配偶', '#9A5C7A')], hasPoster: true },
|
||||
{ id: 2, title: '流浪地球 3', watchedAt: '2026-09-28 14:00', cinemaName: '万达影城·李沧店', cinemaColor: '#5566A8', rating: 8.6, reviewCount: 2, viewers: [fbChip(1, '我', '我', '#A84A28'), fbChip(3, '儿子', '儿子', '#4A7C82'), fbChip(4, '女儿', '女儿', '#6B5B95')], hasPoster: true },
|
||||
{ id: 3, title: '飞屋环游记', watchedAt: '2026-09-20 20:00', cinemaName: '家里', cinemaColor: '#8A6B4B', rating: 9.0, reviewCount: 1, viewers: [fbChip(4, '女儿', '女儿', '#6B5B95')], hasPoster: false },
|
||||
{ id: 4, title: '奥本海默', watchedAt: '2026-09-12 19:10', cinemaName: 'CGV·市南店', cinemaColor: '#A8574F', rating: 8.9, reviewCount: 4, viewers: [fbChip(1, '我', '我', '#A84A28'), fbChip(2, '爱人', '配偶', '#9A5C7A')], hasPoster: true },
|
||||
{ id: 5, title: '海蒂和爷爷', watchedAt: '2026-08-30 15:30', cinemaName: '家里', cinemaColor: '#8A6B4B', rating: 8.4, reviewCount: 2, viewers: [fbChip(4, '女儿', '女儿', '#6B5B95'), fbChip(3, '儿子', '儿子', '#4A7C82')], hasPoster: false },
|
||||
{ id: 6, title: '宇宙探索编辑部', watchedAt: '2026-08-16 21:00', cinemaName: '家里', cinemaColor: '#8A6B4B', rating: 8.1, reviewCount: 1, viewers: [fbChip(1, '我', '我', '#A84A28')], hasPoster: true }
|
||||
];
|
||||
|
||||
/** 观影影院(movies/cinemas) */
|
||||
export const FB_DEMO_CINEMAS: string[] = ['家里', '万达影城·李沧店', 'CGV·市南店', '横店影城', '猫眼线上'];
|
||||
|
||||
// ============================================================
|
||||
// 旅程
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_TRIPS: FbTripItem[] = [
|
||||
{ id: 1, title: '青岛·崂山 两日', sub: '青岛 · 2 天', dateRange: '2026-10-02 ~ 10-03', nodeCount: 8, doneCount: 8 },
|
||||
{ id: 2, title: '烟台海边周末', sub: '烟台 · 2 天', dateRange: '2026-08-15 ~ 08-16', nodeCount: 6, doneCount: 6 },
|
||||
{ id: 3, title: '国庆西安行', sub: '西安 · 4 天', dateRange: '2025-10-01 ~ 10-04', nodeCount: 12, doneCount: 12 },
|
||||
{ id: 4, title: '寒假哈尔滨', sub: '哈尔滨 · 5 天', dateRange: '2026-01-20 ~ 01-24', nodeCount: 0, doneCount: 0 }
|
||||
];
|
||||
|
||||
/** 一段旅程的行程(trips/show 时间轴;stage 分「计划 / 实际」) */
|
||||
export const FB_DEMO_TRIP_NODES: FbTripNode[] = [
|
||||
{ id: 1, title: '出发', when: '10-02 07:30', place: '家 → 青岛北站', stage: 'actual', status: 'done', mode: '自驾', expenseCent: 0 },
|
||||
{ id: 2, title: '抵达民宿', when: '10-02 10:20', place: '崂山区·沙子口民宿', stage: 'actual', status: 'done', mode: '自驾', expenseCent: 68000 },
|
||||
{ id: 3, title: '海边散步 + 午餐', when: '10-02 12:00', place: '仰口海水浴场', stage: 'actual', status: 'done', mode: '步行', expenseCent: 21800 },
|
||||
{ id: 4, title: '太清宫', when: '10-02 15:00', place: '崂山太清景区', stage: 'actual', status: 'done', mode: '自驾', expenseCent: 27000 },
|
||||
{ id: 5, title: '登山(巨峰)', when: '10-03 08:00', place: '崂山巨峰景区', stage: 'plan', status: 'done', mode: '自驾', expenseCent: 34000 },
|
||||
{ id: 6, title: '返程', when: '10-03 17:00', place: '青岛 → 家', stage: 'plan', status: 'done', mode: '自驾', expenseCent: 0 }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 日程
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_CAL_BOOKS: FbCalBook[] = [
|
||||
{ id: 1, name: '我的日程', color: '#A84A28', canWrite: true, visible: true, shared: false, derived: false },
|
||||
{ id: 2, name: '家庭日程', color: '#B0763A', canWrite: true, visible: true, shared: true, derived: false },
|
||||
{ id: 3, name: '观影', color: '#7A4E8C', canWrite: true, visible: true, shared: false, derived: true },
|
||||
{ id: 4, name: '旅行', color: '#3F7D5A', canWrite: true, visible: true, shared: false, derived: true },
|
||||
{ id: 5, name: '孩子的课表', color: '#4A7C82', canWrite: false, visible: false, shared: true, derived: false },
|
||||
{ id: 6, name: '节假日', color: '#C0533F', canWrite: false, visible: true, shared: false, derived: true }
|
||||
];
|
||||
|
||||
export const FB_DEMO_EVENTS_TODAY: FbCalEvent[] = [
|
||||
{ id: 1, title: '儿子数学兴趣班', date: '2026-10-11', time: '09:00', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '小区活动中心' },
|
||||
{ id: 2, title: '家庭大扫除', date: '2026-10-11', time: '10:30', calName: '家庭日程', color: '#B0763A', allDay: false, place: '家' },
|
||||
{ id: 3, title: '晚上看电影', date: '2026-10-11', time: '19:30', calName: '观影', color: '#7A4E8C', allDay: false, place: '家里' },
|
||||
{ id: 4, title: '妈妈体检预约', date: '2026-10-11', time: '', calName: '家庭日程', color: '#B0763A', allDay: true, place: '市立医院' }
|
||||
];
|
||||
|
||||
export const FB_DEMO_EVENTS_SOON: FbCalEvent[] = [
|
||||
{ id: 11, title: '女儿钢琴课', date: '10-12', time: '16:00', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '琴行' },
|
||||
{ id: 12, title: '结婚周年', date: '10-17', time: '', calName: '家庭日程', color: '#B0763A', allDay: true, place: '' },
|
||||
{ id: 13, title: '周末露营', date: '10-18', time: '08:00', calName: '家庭日程', color: '#B0763A', allDay: false, place: '仰口' },
|
||||
{ id: 14, title: '家长会', date: '10-20', time: '18:30', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '学校' }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 任务
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_TASKS: FbTaskItem[] = [
|
||||
{ id: 1, title: '交物业费', status: 'doing', board: '家务', boardColor: '#A8574F', dueText: '10-13 前', overdue: false, assignee: '我', childDone: 0, childTotal: 0 },
|
||||
{ id: 2, title: '预约明年体检', status: 'doing', board: '家人健康', boardColor: '#5E7A3C', dueText: '10-20 前', overdue: false, assignee: '爱人', childDone: 1, childTotal: 3 },
|
||||
{ id: 3, title: '整理去年旅行照片', status: 'doing', board: '家务', boardColor: '#A8574F', dueText: '', overdue: false, assignee: '我', childDone: 0, childTotal: 0 },
|
||||
{ id: 4, title: '换季衣物收纳', status: 'todo', board: '家务', boardColor: '#A8574F', dueText: '10-09 前', overdue: true, assignee: '爱人', childDone: 0, childTotal: 0 },
|
||||
{ id: 5, title: '给儿子买秋季校服', status: 'todo', board: '孩子', boardColor: '#4A7C82', dueText: '10-15 前', overdue: false, assignee: '我', childDone: 0, childTotal: 0 },
|
||||
{ id: 6, title: '车保养', status: 'todo', board: '车与房', boardColor: '#5566A8', dueText: '', overdue: false, assignee: '', childDone: 0, childTotal: 0 },
|
||||
{ id: 7, title: '读完《置身事内》', status: 'done', board: '家务', boardColor: '#A8574F', dueText: '', overdue: false, assignee: '我', childDone: 0, childTotal: 0 }
|
||||
];
|
||||
|
||||
/** 任务看板(tasks/settings) */
|
||||
export const FB_DEMO_TASK_BOARDS: string[] = ['家务', '家人健康', '孩子', '车与房'];
|
||||
|
||||
// ============================================================
|
||||
// 纪念日
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_ANNIVERSARIES: FbAnniversaryItem[] = [
|
||||
{ id: 1, title: '结婚周年', date: '10-17', daysLeft: 6, kind: 'anniversary', people: [fbChip(1, '我', '我', '#A84A28'), fbChip(2, '爱人', '配偶', '#9A5C7A')], yearly: true },
|
||||
{ id: 2, title: '儿子生日', date: '10-18', daysLeft: 7, kind: 'birthday', people: [fbChip(3, '儿子', '儿子', '#4A7C82')], yearly: true },
|
||||
{ id: 3, title: '重阳节', date: '10-29', daysLeft: 18, kind: 'holiday', people: [], yearly: true },
|
||||
{ id: 4, title: '爸爸生日', date: '05-21', daysLeft: 222, kind: 'birthday', people: [fbChip(5, '爸爸', '父亲', '#8A6B4B')], yearly: true },
|
||||
{ id: 5, title: '搬家纪念日', date: '2019-11-08', daysLeft: 28, kind: 'anniversary', people: [], yearly: true }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 财务
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_LEDGER_BOOKS: FbLedgerBook[] = [
|
||||
{ id: 1, name: '家庭总账', netCent: 86420000, myPerm: 'owner', entryCount: 9 },
|
||||
{ id: 2, name: '我的私房账', netCent: 1260000, myPerm: 'owner', entryCount: 3 },
|
||||
{ id: 3, name: '爸妈的养老账', netCent: 4580000, myPerm: 'write', entryCount: 5 }
|
||||
];
|
||||
|
||||
export const FB_DEMO_LEDGER_ENTRIES: FbLedgerEntry[] = [
|
||||
{ id: 1, name: '工资卡', kind: 'deposit', amountCent: 18600000, ownerName: '我', updatedAt: '2026-10-01' },
|
||||
{ id: 2, name: '公积金', kind: 'deposit', amountCent: 24300000, ownerName: '我', updatedAt: '2026-10-01' },
|
||||
{ id: 3, name: '指数基金', kind: 'fund', amountCent: 34800000, ownerName: '我', updatedAt: '2026-09-30' },
|
||||
{ id: 4, name: '重疾险', kind: 'insurance', amountCent: 500000, ownerName: '爱人', updatedAt: '2026-08-12' },
|
||||
{ id: 5, name: '自住房', kind: 'house', amountCent: 180000000, ownerName: '我们', updatedAt: '2026-06-20' },
|
||||
{ id: 6, name: '代步车', kind: 'car', amountCent: 8200000, ownerName: '我', updatedAt: '2026-06-20' },
|
||||
{ id: 7, name: '房贷余额', kind: 'debt', amountCent: -142600000, ownerName: '我们', updatedAt: '2026-10-01' },
|
||||
{ id: 8, name: '信用卡待还', kind: 'debt', amountCent: -3860000, ownerName: '爱人', updatedAt: '2026-10-08' },
|
||||
{ id: 9, name: '车位', kind: 'house', amountCent: 12000000, ownerName: '我们', updatedAt: '2026-06-20' }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 资源
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_RESOURCES: FbResourceItem[] = [
|
||||
{ id: 1, name: '户口本', kind: 'document', ownerName: '全家', fileCount: 2, shared: true, updatedAt: '2026-09-14' },
|
||||
{ id: 2, name: '房产证', kind: 'document', ownerName: '我们', fileCount: 3, shared: false, updatedAt: '2026-07-02' },
|
||||
{ id: 3, name: '儿子学籍资料', kind: 'material', ownerName: '儿子', fileCount: 6, shared: true, updatedAt: '2026-09-01' },
|
||||
{ id: 4, name: '全家保险合同', kind: 'document', ownerName: '全家', fileCount: 4, shared: true, updatedAt: '2026-08-12' },
|
||||
{ id: 5, name: '装修图纸', kind: 'material', ownerName: '我', fileCount: 12, shared: false, updatedAt: '2026-05-19' },
|
||||
{ id: 6, name: '车险单', kind: 'document', ownerName: '我', fileCount: 1, shared: false, updatedAt: '2026-04-08' }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 读书
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_BOOKS: FbBookItem[] = [
|
||||
{ id: 1, title: '置身事内', author: '兰小欢', status: 'reading', rate: 5, noteCount: 12, progress: 62 },
|
||||
{ id: 2, title: '地球的红飘带', author: '魏巍', status: 'reading', rate: 4, noteCount: 5, progress: 28 },
|
||||
{ id: 3, title: '蛤蟆先生去看心理医生', author: '罗伯特·戴博德', status: 'done', rate: 5, noteCount: 9, progress: 100 },
|
||||
{ id: 4, title: '一日三秋', author: '刘震云', status: 'done', rate: 4, noteCount: 7, progress: 100 },
|
||||
{ id: 5, title: '显微镜下的大明', author: '马伯庸', status: 'done', rate: 5, noteCount: 15, progress: 100 },
|
||||
{ id: 6, title: '也许你该找个人聊聊', author: '洛莉·戈特利布', status: 'want', rate: 0, noteCount: 0, progress: 0 }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 导航
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_NAV: FbNavLink[] = [
|
||||
{ id: 1, title: 'Gitea 代码', url: 'https://code.yangyq.net', group: '开发', icon: '' },
|
||||
{ id: 2, title: '阿里云控制台', url: 'https://ecs.console.aliyun.com', group: '开发', icon: '' },
|
||||
{ id: 3, title: '腾讯云文档', url: 'https://docs.qq.com', group: '办公', icon: '' },
|
||||
{ id: 4, title: '学校 OA', url: 'https://oa.example.edu.cn', group: '办公', icon: '' },
|
||||
{ id: 5, title: '豆瓣', url: 'https://www.douban.com', group: '常用', icon: '' },
|
||||
{ id: 6, title: '高德地图', url: 'https://www.amap.com', group: '常用', icon: '' },
|
||||
{ id: 7, title: '知乎', url: 'https://www.zhihu.com', group: '常用', icon: '' },
|
||||
{ id: 8, title: '国家气象中心', url: 'http://www.nmc.cn', group: '科研', icon: '' }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 笔记
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_NOTES: FbNoteItem[] = [
|
||||
{ id: 1, title: '阳台改造清单', excerpt: '1. 换推拉门(断桥铝,考虑隔音)2. 加一组置物架 3. 补两盆绿萝…', tags: ['家务', '装修'], updatedAt: '10-11 09:12', shared: false, attachCount: 2 },
|
||||
{ id: 2, title: '读书摘抄 · 置身事内', excerpt: '「地方政府的行为,要从它面对的制度约束去理解。」……', tags: ['读书'], updatedAt: '10-10 22:40', shared: true, attachCount: 0 },
|
||||
{ id: 3, title: '儿子的兴趣班时间表', excerpt: '周一 16:30 数学;周三 17:00 游泳;周六 09:00 钢琴…', tags: ['孩子'], updatedAt: '10-09 18:05', shared: true, attachCount: 1 },
|
||||
{ id: 4, title: '崂山两日流水', excerpt: '第一天仰口 → 太清宫;第二天巨峰。民宿 680,两餐 218…', tags: ['旅行', '记账'], updatedAt: '10-04 20:30', shared: false, attachCount: 6 },
|
||||
{ id: 5, title: '车里该备的东西', excerpt: '拖车绳、搭电线、应急灯、保温毯、常用药…', tags: ['车'], updatedAt: '09-25 07:48', shared: false, attachCount: 0 }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 通知
|
||||
// ============================================================
|
||||
|
||||
export const FB_DEMO_NOTIFICATIONS: FbNotificationItem[] = [
|
||||
{ id: 1, title: '爱人共享了「家庭日程」', body: '你现在可以看到这本日历的全部内容', timeText: '12 分钟前', unread: true, from: fbChip(2, '爱人', '配偶', '#9A5C7A') },
|
||||
{ id: 2, title: '任务「换季衣物收纳」已逾期', body: '截止时间 10-09,现在还没完成', timeText: '2 小时前', unread: true, from: fbChip(2, '爱人', '配偶', '#9A5C7A') },
|
||||
{ id: 3, title: '弟弟 在「观影」里加了一条', body: '《宇宙探索编辑部》', timeText: '昨天 21:04', unread: false, from: fbChip(7, '弟弟', '弟弟', '#3F7D5A') },
|
||||
{ id: 4, title: '爸爸生日还有 222 天', body: '05-21,要不要提前记一下礼物', timeText: '3 天前', unread: false, from: fbChip(5, '爸爸', '父亲', '#8A6B4B') }
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// 分组工具(把平铺数组按某个字段切成 FbGroup)
|
||||
// ============================================================
|
||||
|
||||
/** 按 roster 分组(名册页:家里的人 / 亲友) */
|
||||
export function fbGroupRoster(list: FbRosterItem[]): FbGroup<FbRosterItem>[] {
|
||||
return fbGroupBy(list, (it: FbRosterItem): string => it.group);
|
||||
}
|
||||
|
||||
/** 按 status 分组建任务(待办 / 进行中 / 已完成) */
|
||||
export function fbGroupTask(list: FbTaskItem[]): FbGroup<FbTaskItem>[] {
|
||||
const order: string[] = ['doing', 'todo', 'done'];
|
||||
const titles: string[] = ['进行中', '待办', '已完成'];
|
||||
const groups: FbGroup<FbTaskItem>[] = [];
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
const key: string = order[i];
|
||||
const items: FbTaskItem[] = list.filter((t: FbTaskItem): boolean => t.status === key);
|
||||
if (items.length > 0) {
|
||||
groups.push({ key: key, title: titles[i], items: items, count: items.length.toString() + ' 项' });
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/** 按 kind 分组建资源(证件 / 资料 / 其他) */
|
||||
export function fbGroupResource(list: FbResourceItem[]): FbGroup<FbResourceItem>[] {
|
||||
return fbGroupBy(list, (it: FbResourceItem): string => fbResourceKindName(it.kind));
|
||||
}
|
||||
|
||||
/** 按 status 分组建书架(在读 / 想读 / 读完) */
|
||||
export function fbGroupBook(list: FbBookItem[]): FbGroup<FbBookItem>[] {
|
||||
const order: string[] = ['reading', 'want', 'done'];
|
||||
const titles: string[] = ['在读', '想读', '读完'];
|
||||
const groups: FbGroup<FbBookItem>[] = [];
|
||||
for (let i = 0; i < order.length; i++) {
|
||||
const key: string = order[i];
|
||||
const items: FbBookItem[] = list.filter((b: FbBookItem): boolean => b.status === key);
|
||||
if (items.length > 0) {
|
||||
groups.push({ key: key, title: titles[i], items: items, count: items.length.toString() + ' 本' });
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/** 按 group 分组建导航 */
|
||||
export function fbGroupNav(list: FbNavLink[]): FbGroup<FbNavLink>[] {
|
||||
return fbGroupBy(list, (it: FbNavLink): string => it.group);
|
||||
}
|
||||
|
||||
/** 通用分组:保持首次出现的顺序(不做字典序,免得打乱业务排序) */
|
||||
function fbGroupBy<T>(list: T[], keyOf: (it: T) => string): FbGroup<T>[] {
|
||||
const keys: string[] = [];
|
||||
const buckets: T[][] = [];
|
||||
for (const it of list) {
|
||||
const k: string = keyOf(it);
|
||||
let idx: number = keys.indexOf(k);
|
||||
if (idx < 0) {
|
||||
keys.push(k);
|
||||
buckets.push([]);
|
||||
idx = keys.length - 1;
|
||||
}
|
||||
buckets[idx].push(it);
|
||||
}
|
||||
const out: FbGroup<T>[] = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
out.push({
|
||||
key: keys[i],
|
||||
title: keys[i],
|
||||
items: buckets[i],
|
||||
count: buckets[i].length.toString()
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 资源类别的中文名 */
|
||||
export function fbResourceKindName(kind: string): string {
|
||||
if (kind === 'document') {
|
||||
return '证件';
|
||||
}
|
||||
if (kind === 'material') {
|
||||
return '资料';
|
||||
}
|
||||
return '其他';
|
||||
}
|
||||
|
||||
/** 账目类别的中文名 */
|
||||
export function fbLedgerKindName(kind: string): string {
|
||||
if (kind === 'deposit') {
|
||||
return '存款';
|
||||
}
|
||||
if (kind === 'fund') {
|
||||
return '基金';
|
||||
}
|
||||
if (kind === 'insurance') {
|
||||
return '保险';
|
||||
}
|
||||
if (kind === 'house') {
|
||||
return '房产';
|
||||
}
|
||||
if (kind === 'car') {
|
||||
return '车辆';
|
||||
}
|
||||
if (kind === 'debt') {
|
||||
return '负债';
|
||||
}
|
||||
return '其他';
|
||||
}
|
||||
|
||||
/** 分 → 「¥12.3 万」这样的中文金额(Web 端 Ledger::money 同口径) */
|
||||
export function fbMoney(cent: number): string {
|
||||
const yuan: number = cent / 100;
|
||||
const neg: boolean = yuan < 0;
|
||||
const abs: number = Math.abs(yuan);
|
||||
let text: string;
|
||||
if (abs >= 100000000) {
|
||||
text = (abs / 100000000).toFixed(2) + ' 亿';
|
||||
} else if (abs >= 10000) {
|
||||
text = (abs / 10000).toFixed(2) + ' 万';
|
||||
} else {
|
||||
text = abs.toFixed(2);
|
||||
}
|
||||
return (neg ? '-¥' : '¥') + text;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 模块注册中心(鸿蒙端)
|
||||
*
|
||||
* 与 Web 端 `app/Module.php` 一一对应 —— 那边是「有哪些模块 / 谁在导航条上 /
|
||||
* 各自什么主题色」的**唯一来源**,客户端这份是它在 ArkTS 侧的镜像。
|
||||
*
|
||||
* 三条分工(与 Web 端一致,别混):
|
||||
* · **模块本身**(有哪些、叫什么、默认什么色)→ 这里的 FB_MODULES,写死在代码里;
|
||||
* · **谁在抽屉里 / 什么顺序** → 后续接到接口后由 user_settings('nav_modules') 决定,
|
||||
* 没配过就用 DEFAULT_NAV;
|
||||
* · **主题色** → 后续由 settings('module_colors')(家庭级)下发。
|
||||
*
|
||||
* ⚠️ key 必须等于 Web 端路由名(r=xxx):接口对接、页面高亮、「本页属于哪个模块」
|
||||
* 全都按 key 走,两边各写一份迟早对不上。
|
||||
*
|
||||
* ⚠️ 命名前缀:ArkTS 不支持声明合并(arkts-no-decl-merging),不同文件的顶层
|
||||
* interface/class/struct 名不能重复。本文件所有导出名统一加 `Fb` 前缀。
|
||||
*/
|
||||
|
||||
/** 一个功能模块的静态定义 */
|
||||
export interface FbModuleDef {
|
||||
/** 模块 key,等于 Web 端路由名(home / kin / people / …) */
|
||||
key: string;
|
||||
/** 抽屉里显示的名字 */
|
||||
name: string;
|
||||
/** 默认主题色(家庭级设置可覆盖) */
|
||||
color: string;
|
||||
/** 一句话说明(抽屉里那行小灰字) */
|
||||
desc: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部模块(顺序与 Web 端 `Module::LIST` 一致)。
|
||||
*
|
||||
* ⚠️ 主题色一律挑中低饱和、中低明度的:按钮上是白字,太浅会看不清,
|
||||
* 太艳会盖过内容的纸墨底色(家书整体是宣纸米 + 砖红的暖色调)。
|
||||
*/
|
||||
export const FB_MODULES: FbModuleDef[] = [
|
||||
{ key: 'home', name: '首页', color: '#A84A28', desc: '今天要看的东西' },
|
||||
{ key: 'kin', name: '家谱', color: '#8A6B4B', desc: '以我为中心的一张网' },
|
||||
{ key: 'people', name: '名册', color: '#4A7C82', desc: '家庭名册与亲友通讯录' },
|
||||
{ key: 'movies', name: '观影', color: '#7A4E8C', desc: '一家人看过的电影' },
|
||||
{ key: 'trips', name: '旅程', color: '#3F7D5A', desc: '去过的地方与行程' },
|
||||
{ key: 'calendar', name: '日程', color: '#B0763A', desc: '日历 / 重复日程 / 提醒' },
|
||||
{ key: 'resources', name: '资源', color: '#5566A8', desc: '证件 / 资料 / 附件' },
|
||||
{ key: 'tasks', name: '任务', color: '#A8574F', desc: '看板与待办' },
|
||||
{ key: 'days', name: '纪念日', color: '#B4633C', desc: '生日与重要日子' },
|
||||
{ key: 'ledger', name: '财务', color: '#5E7A3C', desc: '账本与账目' },
|
||||
{ key: 'books', name: '读书', color: '#6B5B95', desc: '书架与读书笔记' },
|
||||
{ key: 'nav', name: '导航', color: '#3E7A9E', desc: '我的个人导航' },
|
||||
{ key: 'notes', name: '笔记', color: '#7A6A4F', desc: '随手记的分块笔记' }
|
||||
];
|
||||
|
||||
/**
|
||||
* 默认出现在抽屉里的模块(没自定义过就用它)。
|
||||
*
|
||||
* ⚠️ 与 Web 端 `Module::DEFAULT_NAV` 保持一致:**家谱默认不上导航条**
|
||||
* (用户原话「毕竟,不需要每天都查看家谱」),但它仍在抽屉的「更多」里能勾出来。
|
||||
*/
|
||||
export const FB_DEFAULT_NAV: string[] = [
|
||||
'home', 'people', 'movies', 'trips', 'calendar',
|
||||
'resources', 'tasks', 'days', 'ledger', 'books', 'nav', 'notes'
|
||||
];
|
||||
|
||||
/**
|
||||
* 主题色的预设色板(设置页点一下就换)。
|
||||
*
|
||||
* ⚠️ 与 Web 端 `Module::PRESET_COLORS` 是**同一套值** —— 两端配色预设必须一致,
|
||||
* 否则同一个人在手机和电脑上选出的「观影紫」会是两种紫。
|
||||
* ⚠️ 一律中低饱和、中低明度:fbThemeVars 会把主色压到 L≈0.42 当按钮底色、
|
||||
* L≈0.28 当字色 —— 太浅则白字在按钮上看不清,太艳则盖过纸墨底的本色。
|
||||
*/
|
||||
export const FB_PRESET_COLORS: string[] = [
|
||||
'#A84A28', '#8A6B4B', '#4A7C82', '#7A4E8C', '#3F7D5A',
|
||||
'#B0763A', '#5566A8', '#A8574F', '#5C8A48', '#6B5B95',
|
||||
'#9A5C7A', '#3D6B8E'
|
||||
];
|
||||
|
||||
/** 按 key 取模块定义(认不出返回 undefined,调用方自己兜底) */
|
||||
export function fbModuleOf(key: string): FbModuleDef | undefined {
|
||||
return FB_MODULES.find((m: FbModuleDef): boolean => m.key === key);
|
||||
}
|
||||
|
||||
/** 模块名(认不出返回 key 本身,避免抽屉里出现空白条目) */
|
||||
export function fbModuleName(key: string): string {
|
||||
const m = fbModuleOf(key);
|
||||
return m !== undefined ? m.name : key;
|
||||
}
|
||||
|
||||
/** 模块主题色(认不出回落到首页砖红) */
|
||||
export function fbModuleColor(key: string): string {
|
||||
const m = fbModuleOf(key);
|
||||
return m !== undefined ? m.color : '#A84A28';
|
||||
}
|
||||
|
||||
/** 默认抽屉列表对应的模块定义数组(顺序照 FB_DEFAULT_NAV) */
|
||||
export function fbDefaultNavModules(): FbModuleDef[] {
|
||||
const out: FbModuleDef[] = [];
|
||||
for (const k of FB_DEFAULT_NAV) {
|
||||
const m = fbModuleOf(k);
|
||||
if (m !== undefined) {
|
||||
out.push(m);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把「#RRGGBB」压成三档同色系(与 Web 端 `Module::cssVars()` 同一算法)。
|
||||
*
|
||||
* 为什么要三档而不是只给一个主色:按钮 hover 色与文字色都是从主色推出来的
|
||||
* 深浅两档,只换主色会让 hover 变成别的颜色、或者白字压在浅底上看不清。
|
||||
* 这里统一按 HSL 压明度,保证三档永远同色系。
|
||||
*/
|
||||
export interface FbThemeVars {
|
||||
/** 按钮底色(L≈0.42) */
|
||||
brand: string;
|
||||
/** hover / 强调(L≈0.58) */
|
||||
brand2: string;
|
||||
/** 文字色(L≈0.28) */
|
||||
ink: string;
|
||||
}
|
||||
|
||||
/** hex → [h(0..1), s, l] */
|
||||
function fbHexToHsl(hex: string): number[] {
|
||||
const s: string = hex.startsWith('#') ? hex.substring(1) : hex;
|
||||
const r: number = parseInt(s.substring(0, 2), 16) / 255;
|
||||
const g: number = parseInt(s.substring(2, 4), 16) / 255;
|
||||
const b: number = parseInt(s.substring(4, 6), 16) / 255;
|
||||
const mx: number = Math.max(r, g, b);
|
||||
const mn: number = Math.min(r, g, b);
|
||||
const l: number = (mx + mn) / 2;
|
||||
const d: number = mx - mn;
|
||||
if (d === 0) {
|
||||
return [0, 0, l];
|
||||
}
|
||||
const den: number = 1 - Math.abs(2 * l - 1);
|
||||
const sat: number = den > 1e-9 ? d / den : 0;
|
||||
let h: number;
|
||||
if (mx === r) {
|
||||
h = ((g - b) / d) % 6;
|
||||
} else if (mx === g) {
|
||||
h = ((b - r) / d) + 2;
|
||||
} else {
|
||||
h = ((r - g) / d) + 4;
|
||||
}
|
||||
h = h * 60;
|
||||
if (h < 0) {
|
||||
h = h + 360;
|
||||
}
|
||||
return [h / 360, sat, l];
|
||||
}
|
||||
|
||||
/** [h,s,l] → '#rrggbb' */
|
||||
function fbHslToHex(h: number, sat: number, l: number): string {
|
||||
const hh: number = h - Math.floor(h);
|
||||
if (sat <= 0) {
|
||||
const v: number = Math.round(l * 255);
|
||||
return fbHex2(v) + fbHex2(v) + fbHex2(v);
|
||||
}
|
||||
const q: number = l < 0.5 ? l * (1 + sat) : l + sat - l * sat;
|
||||
const p: number = 2 * l - q;
|
||||
const conv = (t0: number): number => {
|
||||
let t: number = t0 - Math.floor(t0);
|
||||
if (t < 1 / 6) {
|
||||
return p + (q - p) * 6 * t;
|
||||
}
|
||||
if (t < 1 / 2) {
|
||||
return q;
|
||||
}
|
||||
if (t < 2 / 3) {
|
||||
return p + (q - p) * (2 / 3 - t) * 6;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
const r: number = Math.max(0, Math.min(255, Math.round(conv(hh + 1 / 3) * 255)));
|
||||
const g: number = Math.max(0, Math.min(255, Math.round(conv(hh) * 255)));
|
||||
const b: number = Math.max(0, Math.min(255, Math.round(conv(hh - 1 / 3) * 255)));
|
||||
return fbHex2(r) + fbHex2(g) + fbHex2(b);
|
||||
}
|
||||
|
||||
/** 0..255 → 两位大写十六进制 */
|
||||
function fbHex2(v: number): string {
|
||||
const t: string = v.toString(16).toUpperCase();
|
||||
return t.length < 2 ? '0' + t : t;
|
||||
}
|
||||
|
||||
/** 把一个主题色铺成三档 CSS 变量(客户端直接当 ResourceColor 用) */
|
||||
export function fbThemeVars(hex: string): FbThemeVars {
|
||||
const hsl: number[] = fbHexToHsl(hex.startsWith('#') ? hex : '#A84A28');
|
||||
const h: number = hsl[0];
|
||||
const sat: number = Math.max(0.28, Math.min(0.72, hsl[1]));
|
||||
return {
|
||||
brand: '#' + fbHslToHex(h, sat, 0.42),
|
||||
brand2: '#' + fbHslToHex(h, sat, 0.58),
|
||||
ink: '#' + fbHslToHex(h, sat, 0.28)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 数据类型定义(鸿蒙端)
|
||||
*
|
||||
* ⚠️ 命名前缀:ArkTS 把同一 hap 内所有文件编译进**共享全局作用域**,
|
||||
* 顶层 interface/class 重名会被判定为「声明合并」直接报错
|
||||
* (arkts-no-decl-merging)。所以本文件所有类型统一加 `Fb` 前缀。
|
||||
*
|
||||
* ⚠️ 这份类型表是**照 Web 端页面实际渲染的字段**归纳出来的(读 templates/ 与
|
||||
* app/ 各自的取数逻辑得到),不是凭空设计的 —— 后续接接口时,服务端返回的
|
||||
* JSON 应当能直接映射到这些字段上。字段名尽量与 Web 端保持一致(如
|
||||
* `starts_at` 改成 `startsAt` 只是风格统一,语义不变)。
|
||||
*/
|
||||
|
||||
// ============================================================
|
||||
// 账号 / 家庭(阶段 0:名册、账号、登录、权限模型)
|
||||
// ============================================================
|
||||
|
||||
/** 一个家庭成员账号(对应 Web 端 `people` + `accounts`) */
|
||||
export interface FbAccount {
|
||||
/** persons.id */
|
||||
id: number;
|
||||
/** 姓名(Web 端首页显示的是"我叫他什么",这里先用本名) */
|
||||
name: string;
|
||||
/** 「我」对他的称呼(relations 派生,如「爸爸」「儿子」) */
|
||||
kinship: string;
|
||||
/** 头像色(Web 端 AvatarColor 算法得出) */
|
||||
avatarColor: string;
|
||||
/** 角色:admin / member / guest(Web 端 accounts.role) */
|
||||
role: string;
|
||||
/** 是否为只读访客(Web 端 Auth::isGuest) */
|
||||
isGuest: boolean;
|
||||
/** 是否有写权限(只读账号在页面上要隐藏所有新建/编辑入口) */
|
||||
canWrite: boolean;
|
||||
}
|
||||
|
||||
/** 当前登录会话 */
|
||||
export interface FbSession {
|
||||
/** 是否已登录 */
|
||||
loggedIn: boolean;
|
||||
/** 家庭 id */
|
||||
familyId: number;
|
||||
/** 家庭名(首页大标题 Auth::homeTitle()) */
|
||||
familyTitle: string;
|
||||
/** 家庭格言 / slogan */
|
||||
slogan: string;
|
||||
/** 当前登录者 */
|
||||
me: FbAccount;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 首页(对应 templates/home.php 的几个区块)
|
||||
// ============================================================
|
||||
|
||||
/** 首页顶部信息面板里的「日子标记」(节气 / 节日 / 纪念日 / 生日) */
|
||||
export interface FbTodayMark {
|
||||
/** 标签文案,如「处暑」「中秋」「结婚纪念」 */
|
||||
word: string;
|
||||
/** 类别:solar_term / festival / anniversary / birthday */
|
||||
kind: string;
|
||||
}
|
||||
|
||||
/** 天气(Web 端 Home::weather 三级回落:自定义经纬度 → 内置城市表 → 不显示) */
|
||||
export interface FbWeather {
|
||||
/** 地点名 */
|
||||
place: string;
|
||||
/** 温度(摄氏度,整数) */
|
||||
temp: number;
|
||||
/** 天气描述,如「晴」「多云」 */
|
||||
text: string;
|
||||
/** 风向风力,如「东南风 3 级」 */
|
||||
wind: string;
|
||||
}
|
||||
|
||||
/** 首页统计块(近七天) */
|
||||
export interface FbWeekStat {
|
||||
/** 未来七天日程条数 */
|
||||
events: number;
|
||||
/** 待办件数 */
|
||||
todos: number;
|
||||
/** 逾期件数 */
|
||||
overdue: number;
|
||||
}
|
||||
|
||||
/** 首页「我的家人」里的一个人(带称谓与称呼) */
|
||||
export interface FbKinChip {
|
||||
id: number;
|
||||
/** 显示名(优先用「我叫他什么」) */
|
||||
shown: string;
|
||||
/** 称谓 */
|
||||
kinship: string;
|
||||
/** 头像色 */
|
||||
avatarColor: string;
|
||||
/** 是否管理员(首页徽标) */
|
||||
isAdmin: boolean;
|
||||
}
|
||||
|
||||
/** 首页「模块卡片」—— 一块一块的入口,带一句关键数字 */
|
||||
export interface FbModuleCard {
|
||||
/** 模块 key(点击后切到哪个模块) */
|
||||
key: string;
|
||||
/** 模块名 */
|
||||
name: string;
|
||||
/** 大字:关键数字,如「12 部」「3 件在做」 */
|
||||
big: string;
|
||||
/** 小字说明(可多行) */
|
||||
lines: string[];
|
||||
/** 模块主题色 */
|
||||
color: string;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 名册 / 家谱
|
||||
// ============================================================
|
||||
|
||||
/** 名册里的一个人(people/index.php 的行) */
|
||||
export interface FbRosterItem {
|
||||
id: number;
|
||||
name: string;
|
||||
/** 称谓 */
|
||||
kinship: string;
|
||||
/** 分组名(people_groups) */
|
||||
group: string;
|
||||
/** 标签(person_tags) */
|
||||
tags: string[];
|
||||
/** 头像色 */
|
||||
avatarColor: string;
|
||||
/** 归属:member(家里的人)/ friend(亲友通讯录) */
|
||||
roster: string;
|
||||
/** 生日('MM-DD' 或 '') */
|
||||
birthday: string;
|
||||
/** 是否管理员徽标 */
|
||||
isAdmin: boolean;
|
||||
}
|
||||
|
||||
/** 家谱连线(kin/relate:父母 / 配偶 / 子女) */
|
||||
export interface FbKinRelation {
|
||||
id: number;
|
||||
/** 从谁出发 */
|
||||
fromId: number;
|
||||
fromName: string;
|
||||
/** 到谁 */
|
||||
toId: number;
|
||||
toName: string;
|
||||
/** 关系类型:parent / spouse / child */
|
||||
kind: string;
|
||||
/** 称谓(Kinship::label 算出,如「爷爷」「外甥」) */
|
||||
label: string;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 观影(movies)
|
||||
// ============================================================
|
||||
|
||||
/** 观影记录(movies/index.php 的海报卡片) */
|
||||
export interface FbMovieItem {
|
||||
id: number;
|
||||
/** 片名 */
|
||||
title: string;
|
||||
/** 观影时间显示文案(Movie::formatWatch),如「2026-10-05 19:30」 */
|
||||
watchedAt: string;
|
||||
/** 影院名(可为空) */
|
||||
cinemaName: string;
|
||||
/** 影院色标(movies/cinemas) */
|
||||
cinemaColor: string;
|
||||
/** 平均评分(0–10,0 表示没有评价) */
|
||||
rating: number;
|
||||
/** 评价条数 */
|
||||
reviewCount: number;
|
||||
/** 同看的人 */
|
||||
viewers: FbKinChip[];
|
||||
/** 海报是否已有(没有则画占位) */
|
||||
hasPoster: boolean;
|
||||
}
|
||||
|
||||
/** 观影统计(movies/stats) */
|
||||
export interface FbMovieStat {
|
||||
/** 每月一部柱状图的月份标签 */
|
||||
months: string[];
|
||||
/** 对应月份的数量 */
|
||||
counts: number[];
|
||||
/** 年度总览的关键数字 */
|
||||
total: number;
|
||||
/** 平均分 */
|
||||
avgRating: number;
|
||||
/** 最常去的影院 */
|
||||
topCinema: string;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 旅程(trips)
|
||||
// ============================================================
|
||||
|
||||
/** 旅程列表项(trips/index.php) */
|
||||
export interface FbTripItem {
|
||||
id: number;
|
||||
/** 旅程名 */
|
||||
title: string;
|
||||
/** 副标题:城市 / 天数 */
|
||||
sub: string;
|
||||
/** 时间范围显示文案(从各段行程派生) */
|
||||
dateRange: string;
|
||||
/** 行程段数 */
|
||||
nodeCount: number;
|
||||
/** 已完成段数 */
|
||||
doneCount: number;
|
||||
}
|
||||
|
||||
/** 旅程里的一段行程(trips/show 的时间轴节点) */
|
||||
export interface FbTripNode {
|
||||
id: number;
|
||||
/** 段标题,如「出发」「抵达民宿」 */
|
||||
title: string;
|
||||
/** 时间文案 */
|
||||
when: string;
|
||||
/** 地点 */
|
||||
place: string;
|
||||
/** 阶段:plan(计划)/ actual(实际) */
|
||||
stage: string;
|
||||
/** 状态:todo / done / skip */
|
||||
status: string;
|
||||
/** 交通方式(trips/modes) */
|
||||
mode: string;
|
||||
/** 花费(分) */
|
||||
expenseCent: number;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 日程(calendar)
|
||||
// ============================================================
|
||||
|
||||
/** 一条日程(calendar/index.php 的格子/时间轴条目) */
|
||||
export interface FbCalEvent {
|
||||
id: number;
|
||||
/** 标题 */
|
||||
title: string;
|
||||
/** 日期 'YYYY-MM-DD' */
|
||||
date: string;
|
||||
/** 时间 'HH:mm'(全天事件为空串) */
|
||||
time: string;
|
||||
/** 所属日历本名 */
|
||||
calName: string;
|
||||
/** 日历本色标 */
|
||||
color: string;
|
||||
/** 是否全天 */
|
||||
allDay: boolean;
|
||||
/** 地点(可为空) */
|
||||
place: string;
|
||||
}
|
||||
|
||||
/** 日历本(calendar/calendars) */
|
||||
export interface FbCalBook {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string;
|
||||
/** 我能不能写(Calendar::canWrite) */
|
||||
canWrite: boolean;
|
||||
/** 是否显示(Calendar::hiddenCalendars 的反面) */
|
||||
visible: boolean;
|
||||
/** 是否共享给别人 */
|
||||
shared: boolean;
|
||||
/** 是否是从 events 派生的「观影」「旅行」两本默认本 */
|
||||
derived: boolean;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 任务(tasks)
|
||||
// ============================================================
|
||||
|
||||
/** 任务卡片(tasks/index.php 看板) */
|
||||
export interface FbTaskItem {
|
||||
id: number;
|
||||
title: string;
|
||||
/** 状态:todo(待办)/ doing(进行中)/ done(已完成) */
|
||||
status: string;
|
||||
/** 所属看板名 */
|
||||
board: string;
|
||||
/** 看板主题色 */
|
||||
boardColor: string;
|
||||
/** 截止时间文案(空表示没有期限,不进日历) */
|
||||
dueText: string;
|
||||
/** 是否逾期 */
|
||||
overdue: boolean;
|
||||
/** 负责人显示名(空表示未指派) */
|
||||
assignee: string;
|
||||
/** 子任务完成数 / 总数 */
|
||||
childDone: number;
|
||||
childTotal: number;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 纪念日(days)
|
||||
// ============================================================
|
||||
|
||||
/** 纪念日(days/index.php + person 生日) */
|
||||
export interface FbAnniversaryItem {
|
||||
id: number;
|
||||
title: string;
|
||||
/** 日期 'MM-DD' 或 'YYYY-MM-DD' */
|
||||
date: string;
|
||||
/** 距今还有几天(0 = 今天) */
|
||||
daysLeft: number;
|
||||
/** 类别:holiday(节日)/ anniversary(纪念日)/ birthday(生日)/ solar_term(节气) */
|
||||
kind: string;
|
||||
/** 相关的人(生日才有) */
|
||||
people: FbKinChip[];
|
||||
/** 是否每年重复 */
|
||||
yearly: boolean;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 财务(ledger)
|
||||
// ============================================================
|
||||
|
||||
/** 一本账(ledger/index.php 顶部概览) */
|
||||
export interface FbLedgerBook {
|
||||
id: number;
|
||||
name: string;
|
||||
/** 净资产(分) */
|
||||
netCent: number;
|
||||
/** 我的权限:owner / read / write */
|
||||
myPerm: string;
|
||||
/** 账目条数 */
|
||||
entryCount: number;
|
||||
}
|
||||
|
||||
/** 一条账目 */
|
||||
export interface FbLedgerEntry {
|
||||
id: number;
|
||||
/** 账目名,如「工资卡」「房贷」 */
|
||||
name: string;
|
||||
/** 类别:deposit(存款)/ fund(基金)/ insurance(保险)/ house(房子)/ car(车子)/ debt(负债)… */
|
||||
kind: string;
|
||||
/** 金额(分;负债为负) */
|
||||
amountCent: number;
|
||||
/** 挂名的人 */
|
||||
ownerName: string;
|
||||
/** 记录/盘点时间 */
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 资源(resources)
|
||||
// ============================================================
|
||||
|
||||
/** 一项资源(resources/index.php) */
|
||||
export interface FbResourceItem {
|
||||
id: number;
|
||||
/** 名称,如「户口本」 */
|
||||
name: string;
|
||||
/** 类别:document(证件)/ material(资料)/ other */
|
||||
kind: string;
|
||||
/** 归属人(空表示全家) */
|
||||
ownerName: string;
|
||||
/** 附件数 */
|
||||
fileCount: number;
|
||||
/** 是否共享给我 */
|
||||
shared: boolean;
|
||||
/** 更新时间 */
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 读书(books)
|
||||
// ============================================================
|
||||
|
||||
/** 一本书(books/index.php 书架) */
|
||||
export interface FbBookItem {
|
||||
id: number;
|
||||
title: string;
|
||||
author: string;
|
||||
/** 状态:want(想读)/ reading(在读)/ done(读完) */
|
||||
status: string;
|
||||
/** 评分 0–5(0 表示未评) */
|
||||
rate: number;
|
||||
/** 笔记条数 */
|
||||
noteCount: number;
|
||||
/** 阅读进度(0–100,done 时为 100) */
|
||||
progress: number;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 导航(nav)
|
||||
// ============================================================
|
||||
|
||||
/** 一条个人导航(nav/index.php) */
|
||||
export interface FbNavLink {
|
||||
id: number;
|
||||
title: string;
|
||||
url: string;
|
||||
/** 分组名 */
|
||||
group: string;
|
||||
/** 图标名(web 端用 favicon,端上先留空用首字母) */
|
||||
icon: string;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 笔记(notes)
|
||||
// ============================================================
|
||||
|
||||
/** 一条笔记 */
|
||||
export interface FbNoteItem {
|
||||
id: number;
|
||||
title: string;
|
||||
/** 摘要(正文前若干字) */
|
||||
excerpt: string;
|
||||
/** 标签 */
|
||||
tags: string[];
|
||||
/** 更新时间 */
|
||||
updatedAt: string;
|
||||
/** 是否共享给别人 */
|
||||
shared: boolean;
|
||||
/** 附件数 */
|
||||
attachCount: number;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 通知(notifications)
|
||||
// ============================================================
|
||||
|
||||
/** 一条站内通知(Notification::mine) */
|
||||
export interface FbNotificationItem {
|
||||
id: number;
|
||||
title: string;
|
||||
body: string;
|
||||
/** 相对时间,如「3 分钟前」 */
|
||||
timeText: string;
|
||||
/** 是否未读 */
|
||||
unread: boolean;
|
||||
/** 触发人(头像色 + 显示名) */
|
||||
from: FbKinChip;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 通用:列表分组(多个页面共用「按字母/类别分组」的形态)
|
||||
// ============================================================
|
||||
|
||||
/** 一个分组(名册按分组、导航按分类、日历按日历本…都用它) */
|
||||
export interface FbGroup<T> {
|
||||
/** 分组键(唯一) */
|
||||
key: string;
|
||||
/** 分组标题 */
|
||||
title: string;
|
||||
/** 组内条目 */
|
||||
items: T[];
|
||||
/** 组标题旁的小字(如「12 人」) */
|
||||
count: string;
|
||||
}
|
||||
Reference in New Issue
Block a user