From b858386e8605561dbe65164e8f7a3a9e9762e9d3 Mon Sep 17 00:00:00 2001 From: owenchen Date: Thu, 15 Oct 2026 23:20:36 +0800 Subject: [PATCH] ow --- .../family-tree-chart/family-tree-chart.vue | 578 +++++++++ .../family-tree-chart/family-tree-node.vue | 362 ++++++ .../src/components/family-tree-chart/types.ts | 70 ++ .../person-quick-create.vue | 240 ++++ .../src/composables/usePolicyOptions.ts | 64 + uniapp/youjiala/src/locale/lang/en-US.json | 122 +- uniapp/youjiala/src/locale/lang/zh-CN.json | 122 +- uniapp/youjiala/src/pages.json | 14 + .../pages/security/family-person-enquiry.vue | 192 +++ .../security/family-person-operation.vue | 466 +++++++ .../pages/security/family-tree-enquiry.vue | 236 ++++ .../pages/security/family-tree-operation.vue | 1071 +++++++++++++++++ .../youjiala/src/pages/security/security.vue | 134 +-- uniapp/youjiala/src/pages/tab/mine.vue | 26 + uniapp/youjiala/src/router/routes.ts | 6 + 15 files changed, 3634 insertions(+), 69 deletions(-) create mode 100644 uniapp/youjiala/src/components/family-tree-chart/family-tree-chart.vue create mode 100644 uniapp/youjiala/src/components/family-tree-chart/family-tree-node.vue create mode 100644 uniapp/youjiala/src/components/family-tree-chart/types.ts create mode 100644 uniapp/youjiala/src/components/person-quick-create/person-quick-create.vue create mode 100644 uniapp/youjiala/src/composables/usePolicyOptions.ts create mode 100644 uniapp/youjiala/src/pages/security/family-person-enquiry.vue create mode 100644 uniapp/youjiala/src/pages/security/family-person-operation.vue create mode 100644 uniapp/youjiala/src/pages/security/family-tree-enquiry.vue create mode 100644 uniapp/youjiala/src/pages/security/family-tree-operation.vue 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + 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) }); } 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") }); +}