diff --git a/uniapp/youjiala/src/components/family-tree-chart/family-tree-chart.vue b/uniapp/youjiala/src/components/family-tree-chart/family-tree-chart.vue
new file mode 100644
index 0000000..4752df5
--- /dev/null
+++ b/uniapp/youjiala/src/components/family-tree-chart/family-tree-chart.vue
@@ -0,0 +1,578 @@
+
+
+
+
+
+
+
+ {{ props.title || t('FamilyTreePage.pedigreeChart') }}
+
+
+
+
+
+
+ +
+
+
+ {{ zoomLabel }}
+
+
+ -
+
+
+ ⇅
+
+
+
+
+
+
+
+ {{ loading ? t('AdminPage.loadingTip') : t('FamilyTreePage.chartEmpty') }}
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/components/family-tree-chart/family-tree-node.vue b/uniapp/youjiala/src/components/family-tree-chart/family-tree-node.vue
new file mode 100644
index 0000000..615cd69
--- /dev/null
+++ b/uniapp/youjiala/src/components/family-tree-chart/family-tree-node.vue
@@ -0,0 +1,362 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ card.name || "-" }}
+
+
+ 常用名:{{ card.commonName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ sp.name || "-" }}
+
+
+ 常用名:{{ sp.commonName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/components/family-tree-chart/types.ts b/uniapp/youjiala/src/components/family-tree-chart/types.ts
new file mode 100644
index 0000000..5714f35
--- /dev/null
+++ b/uniapp/youjiala/src/components/family-tree-chart/types.ts
@@ -0,0 +1,70 @@
+/**
+ * 族谱图(原生递归树)的共享形状与注入 key
+ *
+ * 单列出来只为一个原因:family-tree-chart(持有数据源与展开状态)与
+ * family-tree-node(递归渲染单个节点)要共享「节点长什么样、页面能调哪些动作」这两个契约,
+ * 放进任何一个 .vue 里都会变成互相 import 的循环引用。
+ */
+import type { Ref } from "vue";
+import type { FamilyPersonDisplayData } from "@/data/auto/familyPersonDisplayData";
+import type { FamilyTreeChildrenMapViewEntity } from "@/data/auto/familyTreeChildrenMapViewEntity";
+import type { FamilyTreeSpouseMapViewEntity } from "@/data/auto/familyTreeSpouseMapViewEntity";
+
+/** 卡片显示所需的最小人员信息(本人 / 配偶 / 子女预览共用) */
+export interface FamilyCard {
+ personId: string;
+ /** 姓 + 谱名拼出的展示名 */
+ name: string;
+ commonName?: string;
+ gender?: string;
+ avatarUrl?: string;
+ /** 左上角身份标签:本人 / 始祖 / 族员 / 配偶 / 子女(或具体关系) */
+ tag: string;
+}
+
+/** 一条子女引用:父节点列出孩子时携带,子节点据此渲染占位或递归 */
+export interface ChildRef {
+ personId: string;
+ card: FamilyCard;
+ /** 该子女的数据是否已加载(子树模式懒加载;全树模式恒为 true) */
+ loaded: boolean;
+ /** 是否可就地展开(已加载且确有后代、且本父是其「承嗣/主承担」父,避免重复子树) */
+ expandable: boolean;
+}
+
+/** describe(personId) 的产物:一个节点渲染自身所需的全部信息 */
+export interface ResolvedNode {
+ self: FamilyCard;
+ spouses: FamilyCard[];
+ children: ChildRef[];
+ /** self 是否已加载(未加载时只有卡片预览,点击触发 select-child 去拉取) */
+ loaded: boolean;
+}
+
+/** chart 通过 provide 注入给(递归)node 的数据源与动作集 */
+export interface FamilyTreeChartApi {
+ /** 解析某人的节点数据;无法解析(未加载且无预览)返回 null */
+ describe: (personId: string) => ResolvedNode | null;
+ /** 展开状态:personId -> 是否展开 */
+ expanded: Ref>;
+ /** 切换展开 */
+ toggle: (personId: string) => void;
+ /** 点击「未加载」的子女:通知页面去懒加载其子树 */
+ requestLoad: (personId: string) => void;
+ /** 点击「无后代」的人:通知页面把它设为当前族员 */
+ requestPick: (personId: string) => void;
+ /** 性别是否男性(空值按男处理,对齐旧图) */
+ isMale: (gender?: string) => boolean;
+ /** 布局方向:false=世代向右(横向阶梯),true=世代向下(纵向排列),由容器 ⇅ 按钮切换 */
+ vertical: Ref;
+}
+
+/** sub 模式懒加载缓存的一项:某人的原始「人 + 配偶 + 子女」记录 */
+export interface LoadedTreeNode {
+ person: FamilyPersonDisplayData;
+ spouses: FamilyTreeSpouseMapViewEntity[];
+ children: FamilyTreeChildrenMapViewEntity[];
+}
+
+/** provide / inject 的键 */
+export const FAMILY_TREE_CHART_API = "familyTreeChartApi";
diff --git a/uniapp/youjiala/src/components/person-quick-create/person-quick-create.vue b/uniapp/youjiala/src/components/person-quick-create/person-quick-create.vue
new file mode 100644
index 0000000..a4cb402
--- /dev/null
+++ b/uniapp/youjiala/src/components/person-quick-create/person-quick-create.vue
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+ {{ props.title || t('FamilyPersonPage.quickCreateTitle') }}
+
+
+
+
+ {{ t("FamilyPersonPage.surname") }}*
+
+
+
+
+
+
+ {{ t("FamilyPersonPage.genealogyName") }}*
+
+
+
+
+
+
+ {{ t("FamilyPersonPage.gender") }}*
+
+
+
+ {{ genderLabel || t('FamilyPersonPage.genderPlaceholder') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/composables/usePolicyOptions.ts b/uniapp/youjiala/src/composables/usePolicyOptions.ts
new file mode 100644
index 0000000..74fdfff
--- /dev/null
+++ b/uniapp/youjiala/src/composables/usePolicyOptions.ts
@@ -0,0 +1,64 @@
+/**
+ * 枚举策略选项(性别、关系类型等)——移动端对旧 WebVue hooks/useLocalePolicy 的等价实现
+ *
+ * 数据来源不额外发请求:应用启动时 locale/db.ts 的 mergeDbLocaleMessages 已把
+ * /Locale/FindAll 的结果按 `{GroupName: {Name: Value}}` 就地合并进 uView 的当前语言包,
+ * 因此这里直接读 useLocale().currentLocale(响应式 ref,指向那个语言包对象)即可,
+ * 切换语言时 computed 自动失效重算。
+ *
+ * 映射规则与后端 LocaleService.FindSelectOption、旧 useLocalePolicy 一致:
+ * BindValue = Name(对象 key),DisplayName = Value(对象 value)。
+ */
+import type { ComputedRef } from "vue";
+import { useLocale } from "uview-pro";
+import { computed } from "vue";
+
+/** 供 option-picker 直接消费的单选项:key=后端存储值,label=显示文案 */
+export interface PolicyOption {
+ key: string;
+ label: string;
+}
+
+export function usePolicyOptions() {
+ const { currentLocale } = useLocale();
+
+ /** 取当前语言包下某策略组的 { 值: 文案 };缺失/非对象一律回空表 */
+ function readGroup(groupName: string): Record {
+ const locale = currentLocale.value as Record | null;
+ const group = locale?.[groupName];
+ return group && typeof group === "object"
+ ? (group as Record)
+ : {};
+ }
+
+ /** 策略组的选项列表;blank=true 时在最前补一条空值项(供"全部/未选"场景) */
+ function getPolicyOptions(groupName: string, blank = false): PolicyOption[] {
+ const options: PolicyOption[] = [];
+ if (blank) {
+ options.push({ key: "", label: "" });
+ }
+ for (const [key, value] of Object.entries(readGroup(groupName))) {
+ options.push({ key, label: String(value ?? "") });
+ }
+ return options;
+ }
+
+ /** 把存储值翻译成显示文案;查不到原样返回(对齐旧 getPolicyText) */
+ function getPolicyText(groupName: string, value?: string | null): string {
+ if (value === null || value === undefined || value === "") {
+ return "";
+ }
+ const group = readGroup(groupName);
+ return group[value] != null ? String(group[value]) : String(value);
+ }
+
+ /** 响应式选项:语言切换后自动重算 */
+ function policyOptions(
+ groupName: string,
+ blank = false,
+ ): ComputedRef {
+ return computed(() => getPolicyOptions(groupName, blank));
+ }
+
+ return { getPolicyOptions, getPolicyText, policyOptions };
+}
diff --git a/uniapp/youjiala/src/locale/lang/en-US.json b/uniapp/youjiala/src/locale/lang/en-US.json
index 25767d6..3e948af 100644
--- a/uniapp/youjiala/src/locale/lang/en-US.json
+++ b/uniapp/youjiala/src/locale/lang/en-US.json
@@ -18,6 +18,8 @@
"title": "Me",
"myTheme": "My Theme",
"mySecurity": "Security",
+ "familyTree": "Family Trees",
+ "familyPerson": "Family Members",
"currentTheme": "Current Theme",
"aboutTitle": "About",
"appName": "App",
@@ -57,6 +59,122 @@
"helpFeature": "Help Features",
"helpFeatureDesc": "Help topics and grouping maintenance"
},
+ "FamilyTreePage": {
+ "title": "Family Tree Query",
+ "detailTitle": "Family Tree Detail",
+ "infoTitle": "Tree Information",
+ "treeId": "Tree ID",
+ "treeIdPlaceholder": "Enter tree ID",
+ "treeName": "Tree Name",
+ "brief": "Brief",
+ "publicIndc": "Public",
+ "treeCover": "Cover File",
+ "ancestor": "Ancestor Genealogy Name",
+ "ancestorSurname": "Ancestor Surname",
+ "ancestorGenealogyName": "Ancestor Genealogy Name",
+ "yes": "Yes",
+ "no": "No",
+ "searchBut": "Search",
+ "resetBut": "Reset",
+ "addTreeBut": "New Tree",
+ "currentPerson": "Current Member",
+ "gender": "Gender",
+ "genealogyName": "Genealogy Name",
+ "commonName": "Common Name",
+ "personSearch": "Member Search",
+ "pedigreeChart": "Pedigree Chart",
+ "ancestryChart": "Ancestry Chart",
+ "chartEmpty": "No family tree data",
+ "applyManage": "Apply for Manager",
+ "requestSentTip": "Request submitted, waiting for approval",
+ "addAncestor": "Add Ancestor",
+ "spouseTitle": "Spouses",
+ "addSpouse": "Add Spouse",
+ "pickSpouse": "Pick Member",
+ "spouseEmpty": "No spouse record",
+ "marryDateText": "Marriage Date Text",
+ "marryDate": "Marriage Date",
+ "divorceDateText": "Divorce Date Text",
+ "divorceDate": "Divorce Date",
+ "marryType": "Marriage Type",
+ "isCurrent": "Current Marriage",
+ "childTitle": "Children",
+ "addChild": "Add Child",
+ "childEmpty": "No child record",
+ "parent": "Parent",
+ "parentEmpty": "Add a spouse first",
+ "relationType": "Relation Type",
+ "relationEmpty": "No relation available",
+ "remark": "Remark",
+ "permTitle": "Permission Maintenance",
+ "permPending": "Pending Requests",
+ "permManager": "Managers",
+ "permEmpty": "No record",
+ "approve": "Approve",
+ "setManager": "Set Manager",
+ "reject": "Reject",
+ "remove": "Remove",
+ "managerTag": "Manager",
+ "memberTag": "Member",
+ "inputTreeIdTip": "Please enter tree ID",
+ "noAmendTip": "You have no permission to amend this family tree",
+ "treeNameRequired": "Please enter tree name"
+ },
+ "FamilyPersonPage": {
+ "title": "Family Member Query",
+ "detailTitle": "Member Detail",
+ "personId": "Member ID",
+ "treeId": "Tree ID",
+ "surname": "Surname",
+ "genealogyName": "Genealogy Name",
+ "commonName": "Common Name",
+ "gender": "Gender",
+ "createdDatetime": "Created Date",
+ "basicTitle": "Basic Information",
+ "familyTitle": "Family & Education",
+ "workTitle": "Career & Status",
+ "introTitle": "Biography",
+ "alias": "Alias",
+ "zi": "Courtesy Name (Zi)",
+ "hao": "Art Name (Hao)",
+ "birthOrder": "Birth Order",
+ "maritalStatus": "Marital Status",
+ "fatherId": "Father ID",
+ "motherName": "Mother Name",
+ "school": "School",
+ "major": "Major",
+ "education": "Education",
+ "graduationStatus": "Graduation Status",
+ "duty": "Duty",
+ "title": "Title",
+ "privacy": "Public Profile",
+ "liveStatus": "Living Status",
+ "bindUserId": "Bound User ID",
+ "bindUser": "Bound User",
+ "birthText": "Birth Text",
+ "birthDate": "Birth Date",
+ "deathText": "Death Text",
+ "deathDate": "Death Date",
+ "introduction": "Biography",
+ "yes": "Yes",
+ "no": "No",
+ "avatarTitle": "Avatar",
+ "uploadAvatar": "Upload Avatar",
+ "clearAvatar": "Clear Avatar",
+ "uploadingTip": "Uploading…",
+ "avatarUploadedTip": "Avatar uploaded",
+ "avatarUploadFailed": "Avatar upload failed",
+ "readonlyTip": "Detailed profile is private, only basic information is visible",
+ "quickCreateTitle": "Quick Add Member",
+ "surnamePlaceholder": "Enter surname",
+ "genealogyNamePlaceholder": "Enter genealogy name",
+ "genderPlaceholder": "Select gender",
+ "genderEmpty": "No gender available",
+ "surnameRequired": "Please enter surname",
+ "genealogyNameRequired": "Please enter genealogy name",
+ "genderRequired": "Please select gender",
+ "quickCreateFailed": "Failed to create member"
+ },
"GeneratorPage": {
"title": "Number Generators",
"detailTitle": "Number Rule Detail",
@@ -491,7 +609,9 @@
"noPermissionText": "No accessible admin function",
"menuLoading": "Loading menu…",
"menuRetryBut": "Reload",
- "menuEmptyText": "The server returned no authorized function"
+ "menuEmptyText": "The server returned no authorized function",
+ "loadingTip": "Loading…",
+ "savingTip": "Saving…"
},
"UserPage": {
"title": "User Query",
diff --git a/uniapp/youjiala/src/locale/lang/zh-CN.json b/uniapp/youjiala/src/locale/lang/zh-CN.json
index 14ab6f7..0891337 100644
--- a/uniapp/youjiala/src/locale/lang/zh-CN.json
+++ b/uniapp/youjiala/src/locale/lang/zh-CN.json
@@ -18,6 +18,8 @@
"title": "我的",
"myTheme": "我的主题",
"mySecurity": "安全管理",
+ "familyTree": "族谱管理",
+ "familyPerson": "家族成员",
"currentTheme": "当前主题",
"aboutTitle": "关于",
"appName": "应用名称",
@@ -57,6 +59,122 @@
"helpFeature": "帮助专题",
"helpFeatureDesc": "帮助专题与分组维护"
},
+ "FamilyTreePage": {
+ "title": "族谱查询",
+ "detailTitle": "族谱详情",
+ "infoTitle": "族谱信息",
+ "treeId": "族谱编号",
+ "treeIdPlaceholder": "请输入族谱编号",
+ "treeName": "族谱名称",
+ "brief": "简介",
+ "publicIndc": "是否公开",
+ "treeCover": "封面文件",
+ "ancestor": "祖先谱名",
+ "ancestorSurname": "祖先姓氏",
+ "ancestorGenealogyName": "祖先谱名",
+ "yes": "是",
+ "no": "否",
+ "searchBut": "查询",
+ "resetBut": "重置",
+ "addTreeBut": "新增族谱",
+ "currentPerson": "当前族员",
+ "gender": "性别",
+ "genealogyName": "谱名",
+ "commonName": "常用名",
+ "personSearch": "族员搜索",
+ "pedigreeChart": "谱系图",
+ "ancestryChart": "祖谱系图",
+ "chartEmpty": "暂无族谱数据",
+ "applyManage": "申请管理",
+ "requestSentTip": "申请已提交,等待管理员审批",
+ "addAncestor": "新增祖先",
+ "spouseTitle": "夫妻记录",
+ "addSpouse": "新增配偶",
+ "pickSpouse": "选择成员",
+ "spouseEmpty": "暂无配偶记录",
+ "marryDateText": "结婚日期文本",
+ "marryDate": "结婚日期",
+ "divorceDateText": "离婚日期文本",
+ "divorceDate": "离婚日期",
+ "marryType": "婚姻类型",
+ "isCurrent": "当前婚姻",
+ "childTitle": "子女记录",
+ "addChild": "新增孩子",
+ "childEmpty": "暂无孩子记录",
+ "parent": "父母",
+ "parentEmpty": "请先添加配偶",
+ "relationType": "关系类型",
+ "relationEmpty": "暂无关系可选",
+ "remark": "备注",
+ "permTitle": "权限维护",
+ "permPending": "待审核申请",
+ "permManager": "管理名单",
+ "permEmpty": "暂无记录",
+ "approve": "通过",
+ "setManager": "设为管理员",
+ "reject": "拒绝",
+ "remove": "移出",
+ "managerTag": "管理员",
+ "memberTag": "成员",
+ "inputTreeIdTip": "请输入族谱编号",
+ "noAmendTip": "您没有该族谱的修改权限",
+ "treeNameRequired": "请输入族谱名称"
+ },
+ "FamilyPersonPage": {
+ "title": "家族成员查询",
+ "detailTitle": "成员详情",
+ "personId": "成员编号",
+ "treeId": "族谱编号",
+ "surname": "姓",
+ "genealogyName": "谱名",
+ "commonName": "常用名",
+ "gender": "性别",
+ "createdDatetime": "创建日期",
+ "basicTitle": "基本资料",
+ "familyTitle": "家庭与学业",
+ "workTitle": "任职与状态",
+ "introTitle": "生平简介",
+ "alias": "别名",
+ "zi": "字",
+ "hao": "号",
+ "birthOrder": "排行",
+ "maritalStatus": "婚姻状况",
+ "fatherId": "父亲编号",
+ "motherName": "母亲姓名",
+ "school": "学校",
+ "major": "专业",
+ "education": "学历",
+ "graduationStatus": "毕业情况",
+ "duty": "职务",
+ "title": "头衔",
+ "privacy": "公开资料",
+ "liveStatus": "在世状态",
+ "bindUserId": "绑定用户编号",
+ "bindUser": "绑定用户",
+ "birthText": "出生文本",
+ "birthDate": "出生日期",
+ "deathText": "逝世文本",
+ "deathDate": "逝世日期",
+ "introduction": "生平简介",
+ "yes": "是",
+ "no": "否",
+ "avatarTitle": "头像",
+ "uploadAvatar": "上传头像",
+ "clearAvatar": "清除头像",
+ "uploadingTip": "上传中…",
+ "avatarUploadedTip": "头像上传成功",
+ "avatarUploadFailed": "头像上传失败",
+ "readonlyTip": "该成员未公开详细资料,仅可查看基本资料",
+ "quickCreateTitle": "快速新增成员",
+ "surnamePlaceholder": "请输入姓氏",
+ "genealogyNamePlaceholder": "请输入谱名",
+ "genderPlaceholder": "请选择性别",
+ "genderEmpty": "暂无性别可选",
+ "surnameRequired": "请输入姓氏",
+ "genealogyNameRequired": "请输入谱名",
+ "genderRequired": "请选择性别",
+ "quickCreateFailed": "成员创建失败"
+ },
"GeneratorPage": {
"title": "编号生成器",
"detailTitle": "编号规则详情",
@@ -491,7 +609,9 @@
"noPermissionText": "没有可访问的管理功能",
"menuLoading": "菜单加载中…",
"menuRetryBut": "重新加载",
- "menuEmptyText": "后端没有返回你有权限的功能"
+ "menuEmptyText": "后端没有返回你有权限的功能",
+ "loadingTip": "加载中…",
+ "savingTip": "保存中…"
},
"UserPage": {
"title": "用户查询",
diff --git a/uniapp/youjiala/src/pages.json b/uniapp/youjiala/src/pages.json
index 3da21d9..4b32fc7 100644
--- a/uniapp/youjiala/src/pages.json
+++ b/uniapp/youjiala/src/pages.json
@@ -91,6 +91,20 @@
{
"path": "pages/security/help-feature-operation"
},
+ // 族谱管理(/family-tree-enquiry、/family-tree-operation):旧 master/FamilyTree 两页迁移,挂安全管理枢纽下
+ {
+ "path": "pages/security/family-tree-enquiry"
+ },
+ {
+ "path": "pages/security/family-tree-operation"
+ },
+ // 家族成员(/family-person-enquiry、/family-person-operation):旧 master/FamilyPerson 两页迁移,含族员选择器
+ {
+ "path": "pages/security/family-person-enquiry"
+ },
+ {
+ "path": "pages/security/family-person-operation"
+ },
// 底部导航测试页(首页复用上面的 dashboard):文件/多媒体/共享/我的
{
"path": "pages/tab/file"
diff --git a/uniapp/youjiala/src/pages/security/family-person-enquiry.vue b/uniapp/youjiala/src/pages/security/family-person-enquiry.vue
new file mode 100644
index 0000000..2f18da7
--- /dev/null
+++ b/uniapp/youjiala/src/pages/security/family-person-enquiry.vue
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/pages/security/family-person-operation.vue b/uniapp/youjiala/src/pages/security/family-person-operation.vue
new file mode 100644
index 0000000..791c915
--- /dev/null
+++ b/uniapp/youjiala/src/pages/security/family-person-operation.vue
@@ -0,0 +1,466 @@
+
+
+
+
+
+
+
+
+ {{ t("FamilyPersonPage.readonlyTip") }}
+
+
+
+
+
+
+
+
+ {{ t("FamilyPersonPage.avatarTitle") }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ uploading ? t("FamilyPersonPage.uploadingTip") :
+ t("FamilyPersonPage.uploadAvatar") }}
+
+
+
+
+
+ {{ t("FamilyPersonPage.clearAvatar") }}
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyPersonPage.gender") }}
+
+
+
+ {{ genderLabel || t("FamilyPersonPage.genderPlaceholder") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/pages/security/family-tree-enquiry.vue b/uniapp/youjiala/src/pages/security/family-tree-enquiry.vue
new file mode 100644
index 0000000..a30738d
--- /dev/null
+++ b/uniapp/youjiala/src/pages/security/family-tree-enquiry.vue
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/pages/security/family-tree-operation.vue b/uniapp/youjiala/src/pages/security/family-tree-operation.vue
new file mode 100644
index 0000000..ef2becf
--- /dev/null
+++ b/uniapp/youjiala/src/pages/security/family-tree-operation.vue
@@ -0,0 +1,1071 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.searchBut") }}
+
+
+
+ {{ t("FamilyTreePage.resetBut") }}
+
+
+
+ {{ t("FamilyTreePage.addTreeBut") }}
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.currentPerson") }}
+
+
+
+ {{ t("FamilyTreePage.gender") }}
+ {{ genderText(currentPerson?.Gender) || "-" }}
+
+
+ {{ t("FamilyTreePage.genealogyName") }}
+ {{ currentPerson?.GenealogyName || "-" }}
+
+
+ {{ t("FamilyTreePage.commonName") }}
+ {{ currentPerson?.CommonName || "-" }}
+
+
+
+
+ {{ t("FamilyTreePage.personSearch") }}
+
+
+
+ {{ t("FamilyTreePage.pedigreeChart") }}
+
+
+
+ {{ t("FamilyTreePage.applyManage") }}
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.ancestryChart") }}
+
+
+
+ {{ t("FamilyTreePage.addAncestor") }}
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.spouseTitle") }}
+
+
+
+ {{ t("FamilyTreePage.addSpouse") }}
+
+
+
+ {{ t("FamilyTreePage.pickSpouse") }}
+
+
+
+
+
+ {{ t("FamilyTreePage.spouseEmpty") }}
+
+
+
+
+ {{ record.SpouseSurname }}{{ record.SpouseGenealogyName }}
+ {{ genderText(record.Gender) }}
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.marryDateText") }}
+
+
+
+
+ {{ t("FamilyTreePage.marryDate") }}
+
+ {{ datePart(record.MarryDate) || t("AdminPage.notSetTip") }}
+
+
+
+
+
+ {{ t("FamilyTreePage.divorceDateText") }}
+
+
+
+
+ {{ t("FamilyTreePage.divorceDate") }}
+
+ {{ datePart(record.DivorceDate) || t("AdminPage.notSetTip") }}
+
+
+
+
+
+ {{ t("FamilyTreePage.marryType") }}
+
+
+
+ {{ t("FamilyTreePage.isCurrent") }}
+
+
+
+ {{ t("FamilyTreePage.remark") }}
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.childTitle") }}
+
+
+
+ {{ t("FamilyTreePage.addChild") }}
+
+
+
+
+
+ {{ t("FamilyTreePage.childEmpty") }}
+
+
+
+
+ {{ record.ChildrenSurname }}{{ record.ChildrenGenealogyName }}
+ {{ genderText(record.Gender) }}
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.parent") }}
+
+ {{ spouseLabel(record.SpouseId) }}
+
+
+
+
+ {{ t("FamilyTreePage.relationType") }}
+
+ {{ relationLabel(record.RelationType) }}
+
+
+
+
+ {{ t("FamilyTreePage.remark") }}
+
+
+
+
+
+
+
+
+
+
+ {{ t("FamilyTreePage.permTitle") }}
+
+
+ {{ t("FamilyTreePage.permPending") }}
+
+ {{ t("FamilyTreePage.permEmpty") }}
+
+
+
+ {{ record.UserLocalName || record.UserName }}
+ {{ record.PersonLocalName }}
+
+
+
+ {{ t("FamilyTreePage.approve") }}
+
+
+ {{ t("FamilyTreePage.setManager") }}
+
+
+ {{ t("FamilyTreePage.reject") }}
+
+
+
+
+ {{ t("FamilyTreePage.permManager") }}
+
+ {{ t("FamilyTreePage.permEmpty") }}
+
+
+
+ {{ record.UserLocalName || record.UserName }}
+ {{ record.ManagerIndc === "Y" ? t("FamilyTreePage.managerTag") :
+ t("FamilyTreePage.memberTag") }}
+
+
+
+ {{ t("FamilyTreePage.remove") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/youjiala/src/pages/security/security.vue b/uniapp/youjiala/src/pages/security/security.vue
index 4420fe0..58c25ed 100644
--- a/uniapp/youjiala/src/pages/security/security.vue
+++ b/uniapp/youjiala/src/pages/security/security.vue
@@ -14,8 +14,8 @@ import { pageUrl } from "@/router/routes";
/** 一条功能入口:i18n 标题/副标题键 + 目标列表页名(null=旧系统尚未迁移) */
interface SecurityEntry {
- key: string;
- target: PageName | null;
+ key: string;
+ target: PageName | null;
}
const { t } = useLocale();
@@ -23,109 +23,109 @@ const { t } = useLocale();
useAuthGuard();
const entries: SecurityEntry[] = [
- { key: "user", target: "userEnquiry" },
- { key: "actor", target: "actorEnquiry" },
- { key: "group", target: "groupEnquiry" },
- { key: "function", target: "functionEnquiry" },
- { key: "permission", target: "permissionEnquiry" },
- { key: "generator", target: "generatorEnquiry" },
- { key: "master", target: "masterEnquiry" },
- { key: "helpFunction", target: "helpFunctionEnquiry" },
- { key: "helpFeature", target: "helpFeatureEnquiry" },
+ { key: "user", target: "userEnquiry" },
+ { key: "actor", target: "actorEnquiry" },
+ { key: "group", target: "groupEnquiry" },
+ { key: "function", target: "functionEnquiry" },
+ { key: "permission", target: "permissionEnquiry" },
+ { key: "generator", target: "generatorEnquiry" },
+ { key: "master", target: "masterEnquiry" },
+ { key: "helpFunction", target: "helpFunctionEnquiry" },
+ { key: "helpFeature", target: "helpFeatureEnquiry" },
];
function onPick(entry: SecurityEntry): void {
- if (!entry.target) {
- uni.showToast({ title: t("SecurityPage.pendingTip"), icon: "none" });
- return;
- }
- uni.navigateTo({ url: pageUrl(entry.target) });
+ if (!entry.target) {
+ uni.showToast({ title: t("SecurityPage.pendingTip"), icon: "none" });
+ return;
+ }
+ uni.navigateTo({ url: pageUrl(entry.target) });
}
-
-
-
- {{ t("SecurityPage.pageHint") }}
+
+
+
+ {{ t("SecurityPage.pageHint") }}
+
+
+
+
+
+ {{ t(`SecurityPage.${entry.key}`) }}
-
-
-
-
- {{ t(`SecurityPage.${entry.key}`) }}
-
-
- {{ t(`SecurityPage.${entry.key}Desc`) }}
-
-
-
- {{ t("SecurityPage.pendingTag") }}
-
-
-
-
+
+ {{ t(`SecurityPage.${entry.key}Desc`) }}
+
+
+
+ {{ t("SecurityPage.pendingTag") }}
+
+
-
+
+
+
diff --git a/uniapp/youjiala/src/pages/tab/mine.vue b/uniapp/youjiala/src/pages/tab/mine.vue
index caf63e5..650edbc 100644
--- a/uniapp/youjiala/src/pages/tab/mine.vue
+++ b/uniapp/youjiala/src/pages/tab/mine.vue
@@ -23,6 +23,16 @@ function goTheme(): void {
function goSecurity(): void {
uni.navigateTo({ url: pageUrl("securityPage") });
}
+
+/** 进入「族谱管理」列表页(/family-tree-enquiry) */
+function goFamilyTree(): void {
+ uni.navigateTo({ url: pageUrl("familyTreeEnquiry") });
+}
+
+/** 进入「家族成员」列表页(/family-person-enquiry) */
+function goFamilyPerson(): void {
+ uni.navigateTo({ url: pageUrl("familyPersonEnquiry") });
+}
@@ -44,6 +54,22 @@ function goSecurity(): void {
+
+
+
+ {{ t("MinePage.familyTree") }}
+
+
+
+
+
+
+
+ {{ t("MinePage.familyPerson") }}
+
+
+
+
diff --git a/uniapp/youjiala/src/router/routes.ts b/uniapp/youjiala/src/router/routes.ts
index ff8f5b0..da5ac8c 100644
--- a/uniapp/youjiala/src/router/routes.ts
+++ b/uniapp/youjiala/src/router/routes.ts
@@ -45,6 +45,12 @@ export const PAGE_PATHS = {
// 帮助特性(短地址 /help-feature-enquiry、/help-feature-operation):旧 SecurityHelpFeature 两页迁移
helpFeatureEnquiry: "pages/security/help-feature-enquiry",
helpFeatureOperation: "pages/security/help-feature-operation",
+ // 族谱管理(短地址 /family-tree-enquiry、/family-tree-operation):旧 master/FamilyTree 两页迁移,挂安全管理枢纽下
+ familyTreeEnquiry: "pages/security/family-tree-enquiry",
+ familyTreeOperation: "pages/security/family-tree-operation",
+ // 家族成员(短地址 /family-person-enquiry、/family-person-operation):旧 master/FamilyPerson 两页迁移,含族员选择器
+ familyPersonEnquiry: "pages/security/family-person-enquiry",
+ familyPersonOperation: "pages/security/family-person-operation",
// 底部导航 tab 页(首页复用 dashboard):先各建一个简单测试页占位
fileTab: "pages/tab/file",
mediaTab: "pages/tab/media",