feat: click 照護頁 - 代辦事項的 table row 可顯示 forge 該床位的 popover

This commit is contained in:
MJM_2025_05\polly 2025-09-22 16:31:32 +08:00
parent 1834af24c9
commit 3a6b61db1d
4 changed files with 1196 additions and 795 deletions

View File

@ -460,6 +460,28 @@ const {
__staticDevices, __staticDevices,
} = useForgeSprite(); } = useForgeSprite();
// === Date ===
function __fmtYMD(d) {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}/${m}/${day}`;
}
//
function seededStartTime(bedKey, idx = 0) {
const n = parseInt(String(bedKey).replace("-", ""), 10) || 0;
const base = new Date(2024, 0, 1); // 2024/01/01
const offset = (n * 13 + idx * 17) % 540; // 18
let d = new Date(base.getTime() + offset * 86400000);
const today = new Date();
if (d > today) {
const back = (n % 60) + 10; // 10
d = new Date(today.getTime() - back * 86400000);
}
return __fmtYMD(d);
}
/** ===================== 常數 / 主題色 ===================== */ /** ===================== 常數 / 主題色 ===================== */
const FLOOR_DBIDS = { "1F": null, "2F": null }; const FLOOR_DBIDS = { "1F": null, "2F": null };
const BRAND_RED = "#FF8678"; // const BRAND_RED = "#FF8678"; //
@ -486,6 +508,7 @@ function loadResidentStore() {
return new Map(); return new Map();
} }
} }
function saveResidentStore(map) { function saveResidentStore(map) {
try { try {
localStorage.setItem( localStorage.setItem(
@ -521,7 +544,7 @@ function ensureResidentFor(bedKey, genderHint) {
residentsName: fixed.name, residentsName: fixed.name,
residentsSex: fixed.sex || genderHint || "男", residentsSex: fixed.sex || genderHint || "男",
residentsAge: fixed.age ?? 78, residentsAge: fixed.age ?? 78,
startTime: fixed.startTime ?? "2023/01/15", startTime: fixed.startTime ?? "2025/01/01",
healthStatus: fixed.healthStatus ?? "一般", healthStatus: fixed.healthStatus ?? "一般",
medicationStatus: fixed.medicationStatus ?? "規律服藥", medicationStatus: fixed.medicationStatus ?? "規律服藥",
specialEvent: fixed.specialEvent ?? "-", specialEvent: fixed.specialEvent ?? "-",
@ -532,14 +555,14 @@ function ensureResidentFor(bedKey, genderHint) {
return rec; return rec;
} }
// 🧰 localStorage // localStorage
const existed = RESIDENT_BY_BED.get(key); const existed = RESIDENT_BY_BED.get(key);
if (existed) { if (existed) {
if (!existed.residentsSex && genderHint) existed.residentsSex = genderHint; if (!existed.residentsSex && genderHint) existed.residentsSex = genderHint;
return existed; return existed;
} }
// 🆘 //
const g = genderHint === "女" ? "女" : "男"; const g = genderHint === "女" ? "女" : "男";
const pool = g === "男" ? NAME_POOL_MALE : NAME_POOL_FEMALE; const pool = g === "男" ? NAME_POOL_MALE : NAME_POOL_FEMALE;
const idx = hashFNV1a(key) % pool.length; const idx = hashFNV1a(key) % pool.length;
@ -548,7 +571,7 @@ function ensureResidentFor(bedKey, genderHint) {
residentsName: pool[idx], residentsName: pool[idx],
residentsSex: g, residentsSex: g,
residentsAge: 78, residentsAge: 78,
startTime: "2023/01/15", startTime: seededStartTime(key, idx),
healthStatus: "一般", healthStatus: "一般",
medicationStatus: "規律服藥", medicationStatus: "規律服藥",
specialEvent: "-", specialEvent: "-",
@ -1156,6 +1179,49 @@ window.FORGE_API = {
}, },
}; };
// === popover ===
window.FORGE_API.focusBed = async function focusBed(bedKey, opts = {}) {
try {
if (!viewer || !__staticDevices?.length) return false;
const bed = String(bedKey || "").trim();
if (!bed) return false;
// 1xx1F2xx2F
const targetFloor = bed.charAt(0) === "2" ? "2F" : "1F";
if (activeFloor.value !== targetFloor) {
await applyFloor(targetFloor);
}
// sprite
const dev = __staticDevices.find(
(d) => d.bedKey === bed || d.name?.includes(bed)
);
if (!dev) return false;
// popover selectedInfo sprite status
selectedInfo.value = dev.status; // 'occupied' | 'vacant' | 'hospitalized' | 'leave'
soloSpriteId.value = dev.spriteDbId;
bringToFrontById(dev.spriteDbId);
// popover
rebuildLabelsAfterNextRender();
// sprite opts.fit=false
if (opts.fit !== false) {
await cardfitToView({
forge_dbid: dev.forge_dbid,
spriteDbId: dev.spriteDbId,
back: 30,
});
}
return true;
} catch (err) {
console.warn("[FORGE_API.focusBed] failed:", err);
return false;
}
};
/** ===================== Sprites 顏色規則 ===================== */ /** ===================== Sprites 顏色規則 ===================== */
function spriteColorBy(status, gender) { function spriteColorBy(status, gender) {
if (status === "occupied") return gender === "男" ? BRAND_GREEN : BRAND_RED; if (status === "occupied") return gender === "男" ? BRAND_GREEN : BRAND_RED;
@ -1508,6 +1574,39 @@ async function onClickFloor(next) {
/** ===================== 生命週期 ===================== */ /** ===================== 生命週期 ===================== */
onMounted(async () => { onMounted(async () => {
(function migrateResidentStore() {
let changed = false;
for (const [k, v] of RESIDENT_BY_BED.entries()) {
// startTime 2023/01/15
if (!v || v.startTime === "2023/01/15") {
const fixed = RESIDENTS_BY_BED?.[k];
if (fixed) {
RESIDENT_BY_BED.set(k, {
roomGender: fixed.sex,
residentsName: fixed.name,
residentsSex: fixed.sex,
residentsAge: fixed.age,
startTime: fixed.startTime,
healthStatus: fixed.healthStatus,
medicationStatus: fixed.medicationStatus,
specialEvent: fixed.specialEvent,
note: fixed.note,
});
} else {
// seededStartTime
const g = v?.residentsSex || "男";
RESIDENT_BY_BED.set(k, {
...v,
startTime: seededStartTime(k, 0),
residentsSex: g,
});
}
changed = true;
}
}
if (changed) saveResidentStore(RESIDENT_BY_BED);
})();
document.addEventListener("click", onClickOutsideInfo); document.addEventListener("click", onClickOutsideInfo);
document.addEventListener("keydown", onKeydownInfo); document.addEventListener("keydown", onKeydownInfo);
activeFloor.value = resolveInitialFloor(); activeFloor.value = resolveInitialFloor();

View File

@ -7,6 +7,7 @@
* @property {string} medicationStatus * @property {string} medicationStatus
* @property {string} specialEvent * @property {string} specialEvent
* @property {string} note * @property {string} note
*
*/ */
// ========== 固定姓名池A 機構床號 74 → 男/女各 37========== // ========== 固定姓名池A 機構床號 74 → 男/女各 37==========
@ -233,6 +234,31 @@ export function ageFromBedKey(key, base = 66) {
} }
// ========== 建立固定住民表 ========== // ========== 建立固定住民表 ==========
// 工具:格式化成 YYYY/MM/DD
function fmtYMD(d) {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}/${m}/${day}`;
}
// 工具:依床位與索引產生「穩定且分散」的入住日期
// 規則:以 2025/01/01 為起點,往後偏移 0~540 天(約 18 個月)
// 若超過今天,會往回扣 (n % 60 + 10) 天,確保不會是未來日期
function seededStartTime(bedKey, idx) {
const n = parseInt(String(bedKey).replace("-", ""), 10) || 0;
const base = new Date(2025, 0, 1); // 2025/01/01
const OFFSET_DAYS = (n * 13 + idx * 17) % 540; // 分散 18 個月內
let d = new Date(base.getTime() + OFFSET_DAYS * 86400000);
const today = new Date();
if (d > today) {
const back = (n % 60) + 10; // 至少退 10 天,最多 ~70 天
d = new Date(today.getTime() - back * 86400000);
}
return fmtYMD(d);
}
function buildFixedResidents(keys, names, sex) { function buildFixedResidents(keys, names, sex) {
/** @type {Record<string, ResidentFixed>} */ /** @type {Record<string, ResidentFixed>} */
const out = {}; const out = {};
@ -243,7 +269,7 @@ function buildFixedResidents(keys, names, sex) {
sex, sex,
age: ageFromBedKey(k, sex === "女" ? 65 : 66), age: ageFromBedKey(k, sex === "女" ? 65 : 66),
healthStatus: HEALTH_STATUS_POOL[n % HEALTH_STATUS_POOL.length], healthStatus: HEALTH_STATUS_POOL[n % HEALTH_STATUS_POOL.length],
startTime: "2023/01/15", startTime: seededStartTime(k, i), // ← 用分散演算法產生
medicationStatus: "規律服藥", medicationStatus: "規律服藥",
specialEvent: "-", specialEvent: "-",
note: "-", note: "-",
@ -559,5 +585,41 @@ export const BASE_FACILITY_DATA = {
}, },
}; };
// ====== Nursing 待辦:與 Forge 對齊的示範假資料(用 RESIDENTS_BY_BED 反查姓名) ======
/** @typedef {{date:string, bed:string, name:string, type:string, desc:string}} NursingTodoSeed */
export const NURSING_TODOS_SEED = Object.freeze(
(() => {
const getName = (bed) => RESIDENTS_BY_BED?.[bed]?.name || "-";
/** @type {NursingTodoSeed[]} */
const seeds = [
// (a) 你指定的三筆,姓名會由 RESIDENTS_BY_BED 對應床位自動帶入,確保與 Forge 一致
{
date: "2025/09/21",
bed: "201-1",
name: getName("201-1"),
type: "一般",
desc: "轉換床位",
},
{
date: "2025/09/19",
bed: "105-1",
name: getName("105-1"),
type: "一般",
desc: "情緒問題、Alb偏低",
},
{
date: "2025/09/20",
bed: "207-3",
name: getName("207-3"),
type: "緊急安置",
desc: "適應輔導、衛教需求",
},
];
return seeds;
})()
);
// 兼容性輸出(有些檔案以 FACILITY_DATA 讀取) // 兼容性輸出(有些檔案以 FACILITY_DATA 讀取)
export const FACILITY_DATA = BASE_FACILITY_DATA; export const FACILITY_DATA = BASE_FACILITY_DATA;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff