ow
This commit is contained in:
1 parent
7f5bf3c68d
commit
816209fc35
37 files changed
+3236
-4515
No files matched your search
@@ -57,7 +57,9 @@ const getPolicyText = (options: PolicyOption[], value?: string | null) => {
|
||||
const genderLabel = (g?: string) => getPolicyText(genderOptions, g);
|
||||
const relationLabel = (r?: string) => getPolicyText(relationTypeOptions, r);
|
||||
|
||||
/** 性别为空时默认按男性处理 */
|
||||
const isMale = (g?: string) => {
|
||||
if (!g) return true;
|
||||
const t = (genderLabel(g) ?? "").toLowerCase();
|
||||
return t.includes("男") || t.includes("male") || g === "M" || g === "1";
|
||||
};
|
||||
@@ -66,6 +68,21 @@ const hasPerson = computed(() => !!props.data?.CurrentPerson?.FamilyPerson);
|
||||
|
||||
const rootPersonId = computed(() => props.data?.CurrentPerson?.FamilyPerson?.PersonId ?? "");
|
||||
|
||||
/** personId -> 头像访问地址(本人 / 全族人员 / 已加载族员) */
|
||||
const avatarMap = computed(() => {
|
||||
const map: Record<string, string> = {};
|
||||
const root: any = props.data?.CurrentPerson?.FamilyPerson;
|
||||
if (root?.PersonId && root?.AvatarUrl) map[root.PersonId] = root.AvatarUrl;
|
||||
((props.data?.CurrentPerson?.FamilyTreePersons ?? []) as any[]).forEach((p: any) => {
|
||||
if (p?.PersonId && p?.AvatarUrl) map[p.PersonId] = p.AvatarUrl;
|
||||
});
|
||||
Object.values((props.loadedNodes ?? {}) as Record<string, any>).forEach((n: any) => {
|
||||
const p = n?.person;
|
||||
if (p?.PersonId && p?.AvatarUrl) map[p.PersonId] = p.AvatarUrl;
|
||||
});
|
||||
return map;
|
||||
});
|
||||
|
||||
/** HTML 字符串转义,防止人名中的特殊字符破坏卡片结构 */
|
||||
const esc = (v?: string | null) =>
|
||||
(v ?? "")
|
||||
@@ -74,13 +91,22 @@ const esc = (v?: string | null) =>
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
|
||||
/** 无头像时的默认头像图标(白底 + 性别色人像:男蓝 / 女粉) */
|
||||
const USER_ICON_SVG =
|
||||
'<svg viewBox="64 64 896 896" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg>';
|
||||
|
||||
/** 自定义人员卡片 HTML */
|
||||
const renderCard = (d: any) => {
|
||||
const data = d?.data ?? {};
|
||||
const male = isMale(data.gender);
|
||||
// 本人=橙色,男性=蓝色,女性=粉色
|
||||
const theme = data.kind === "self" ? "#fa8c16" : male ? "#1890ff" : "#eb2f96";
|
||||
const ico = male ? "♂" : "♀";
|
||||
// 无头像占位图标颜色:男蓝 / 女粉(白底)
|
||||
const avatarColor = male ? "#1890ff" : "#eb2f96";
|
||||
// 有头像显示头像,无头像显示默认头像(白底 + 性别色人像图标)
|
||||
const avatar = data.avatarUrl
|
||||
? `<img class="ftc-avatar" src="${esc(data.avatarUrl)}" alt="" onerror="this.style.visibility='hidden'" />`
|
||||
: "";
|
||||
const common = data.commonName
|
||||
? `<div class="ftc-sub">常用名:${esc(data.commonName)}</div>`
|
||||
: "";
|
||||
@@ -92,11 +118,15 @@ const renderCard = (d: any) => {
|
||||
const clickable = data.hasChildren || !data.loaded ? "ftc-clickable" : "";
|
||||
return `
|
||||
<div class="ftc-card ftc-card-lg ${clickable}" style="border-top: 3px solid ${theme};">
|
||||
<div class="ftc-head">
|
||||
<span class="ftc-ico" style="color:${theme};">${ico}</span>
|
||||
<span class="ftc-name" title="${esc(data.name)}">${esc(data.name)}</span>
|
||||
<div class="ftc-main">
|
||||
<div class="ftc-avatar-wrap" style="border-color:${theme};">
|
||||
<span class="ftc-avatar-ph" style="color:${avatarColor};">${USER_ICON_SVG}</span>${avatar}
|
||||
</div>
|
||||
<div class="ftc-info">
|
||||
<div class="ftc-name" title="${esc(data.name)}">${esc(data.name)}</div>
|
||||
${common}
|
||||
</div>
|
||||
</div>
|
||||
${common}
|
||||
<div class="ftc-footer">
|
||||
<div class="ftc-tag" style="color:${theme}; background:${theme}1a;">${esc(data.tag)}</div>
|
||||
${actionTag}
|
||||
@@ -139,7 +169,7 @@ const buildGraphData = () => {
|
||||
if (!root) return { nodes, edges };
|
||||
|
||||
const CARD_WIDTH = 360;
|
||||
const CARD_HEIGHT = 192;
|
||||
const CARD_HEIGHT = 220;
|
||||
const H_GAP = 60;
|
||||
const V_GAP = 40;
|
||||
const X_STEP = CARD_WIDTH + H_GAP; // 420,避免水平重叠
|
||||
@@ -169,6 +199,7 @@ const buildGraphData = () => {
|
||||
name: (p?.Surname ?? "") + (p?.GenealogyName ?? ""),
|
||||
commonName: p?.CommonName,
|
||||
gender: p?.Gender,
|
||||
avatarUrl: p?.AvatarUrl ?? avatarMap.value[personId] ?? "",
|
||||
hasChildren,
|
||||
expanded,
|
||||
loaded: desc.loaded,
|
||||
@@ -183,9 +214,11 @@ const buildGraphData = () => {
|
||||
style: { x: xBase + X_STEP + i * SPOUSE_GAP, y },
|
||||
data: {
|
||||
kind: "spouse",
|
||||
personId: s.SpouseId,
|
||||
tag: "配偶",
|
||||
name: (s.SpouseSurname ?? "") + (s.SpouseGenealogyName ?? ""),
|
||||
gender: s.Gender,
|
||||
avatarUrl: avatarMap.value[s.SpouseId] ?? "",
|
||||
loaded: true,
|
||||
hasChildren: false,
|
||||
expanded: false,
|
||||
@@ -213,6 +246,7 @@ const buildGraphData = () => {
|
||||
name: (c.ChildrenSurname ?? "") + (c.ChildrenGenealogyName ?? ""),
|
||||
commonName: c.ChildrenCommonName,
|
||||
gender: c.Gender,
|
||||
avatarUrl: avatarMap.value[childId] ?? "",
|
||||
hasChildren: false,
|
||||
expanded: false,
|
||||
loaded: false,
|
||||
@@ -280,9 +314,9 @@ const initGraph = async () => {
|
||||
node: {
|
||||
type: "html",
|
||||
style: {
|
||||
size: [360, 192],
|
||||
size: [360, 220],
|
||||
dx: -180,
|
||||
dy: -96,
|
||||
dy: -110,
|
||||
// 官方文档推荐函数形式,类型声明仅支持 string/HTMLElement,故断言
|
||||
innerHTML: renderCard as any,
|
||||
},
|
||||
@@ -443,10 +477,6 @@ const onCancel = () => {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.ftc-card-lg .ftc-ico {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.ftc-card-lg .ftc-name {
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -467,12 +497,63 @@ const onCancel = () => {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.ftc-ico {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
/* 人员卡片:左侧头像 + 右侧姓名信息 */
|
||||
.ftc-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ftc-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ftc-avatar-wrap {
|
||||
position: relative;
|
||||
flex: none;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 2px solid #e8e8e8;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.ftc-card-lg .ftc-avatar-wrap {
|
||||
width: 104px;
|
||||
height: 104px;
|
||||
}
|
||||
|
||||
/* 无头像占位:白底 + 性别色人像图标(男蓝 / 女粉,颜色由内联 style 指定) */
|
||||
.ftc-avatar-ph {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #1890ff;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ftc-card-lg .ftc-avatar-ph {
|
||||
font-size: 52px;
|
||||
}
|
||||
|
||||
/* 头像图片绝对定位覆盖占位图标,加载失败时隐藏,露出下方默认头像 */
|
||||
.ftc-avatar {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.ftc-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
Reference in new issue
Block a user