411 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import dayjs from 'dayjs';
const DB_KEY = 'ykt_case_archive_detail_mockdb_v1';
export const VISIT_RECORD_TEMPLATES = [
{
templateType: 'outpatient',
templateName: '门诊记录',
templateList: [
{ title: 'visitTime', name: '就诊日期', type: 'date', operateType: 'formCell', required: true, format: 'YYYY-MM-DD' },
{ title: 'corpName', name: '就诊机构', type: 'input', operateType: 'formCell', required: false, wordLimit: 30, inputType: 'text' },
{ title: 'deptName', name: '科室', type: 'input', operateType: 'formCell', required: false, wordLimit: 30, inputType: 'text' },
{ title: 'doctor', name: '医生', type: 'input', operateType: 'formCell', required: false, wordLimit: 30, inputType: 'text' },
{ title: 'diagnosisName', name: '门诊诊断', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 200 },
{ title: 'treatmentPlan', name: '治疗方案', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 1000 },
{ title: 'disposePlan', name: '处置计划', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 1000 },
{ title: 'summary', name: '备注/摘要', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 200 },
{ title: 'files', name: '文件上传', type: 'files', required: false },
],
},
{
templateType: 'inhospital',
templateName: '住院记录',
templateList: [
{ title: 'inhosDate', name: '入院日期', type: 'date', operateType: 'formCell', required: true, format: 'YYYY-MM-DD' },
{ title: 'corpName', name: '住院机构', type: 'input', operateType: 'formCell', required: false, wordLimit: 30, inputType: 'text' },
{ title: 'diagnosisName', name: '入院诊断', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 200 },
{ title: 'surgeryName', name: '手术名称', type: 'input', operateType: 'formCell', required: false, wordLimit: 50, inputType: 'text' },
{ title: 'summary', name: '摘要', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 200 },
{ title: 'files', name: '文件上传', type: 'files', required: false },
],
},
{
templateType: 'preConsultation',
templateName: '预问诊记录',
templateList: [
{ title: 'consultDate', name: '问诊日期', type: 'date', operateType: 'formCell', required: true, format: 'YYYY-MM-DD' },
{ title: 'chiefComplaint', name: '主诉', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 300 },
{ title: 'presentIllness', name: '现病史', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 800 },
{ title: 'pastHistory', name: '既往史', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 800 },
{ title: 'allergyHistory', name: '过敏史', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 300 },
{ title: 'summary', name: '摘要', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 200 },
{ title: 'files', name: '文件上传', type: 'files', required: false },
],
},
{
templateType: 'physicalExaminationTemplate',
templateName: '体检记录',
templateList: [
{ title: 'inspectDate', name: '体检日期', type: 'date', operateType: 'formCell', required: true, format: 'YYYY-MM-DD' },
{ title: 'corpName', name: '体检机构', type: 'input', operateType: 'formCell', required: false, wordLimit: 30, inputType: 'text' },
{ title: 'inspectPakageName', name: '体检套餐', type: 'input', operateType: 'formCell', required: false, wordLimit: 50, inputType: 'text' },
{ title: 'positiveFind', name: '阳性发现', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 300 },
{ title: 'summary', name: '摘要', type: 'textarea', operateType: 'formCell', required: false, wordLimit: 200 },
{ title: 'files', name: '文件上传', type: 'files', required: false },
],
},
];
export function getVisitRecordTemplates() {
return VISIT_RECORD_TEMPLATES.map((i) => ({ templateType: i.templateType, name: i.templateName, templateList: i.templateList }));
}
export function getVisitRecordTemplate(templateType) {
return VISIT_RECORD_TEMPLATES.find((i) => i.templateType === templateType) || null;
}
function safeParse(json) {
try {
return JSON.parse(json);
} catch {
return null;
}
}
function getDb() {
const raw = uni.getStorageSync(DB_KEY);
const db = raw && typeof raw === 'string' ? safeParse(raw) : raw;
const next = db && typeof db === 'object' ? db : {};
next.visitRecordsByArchiveId = next.visitRecordsByArchiveId || {};
next.serviceRecordsByArchiveId = next.serviceRecordsByArchiveId || {};
next.followupsByArchiveId = next.followupsByArchiveId || {};
return next;
}
function setDb(db) {
uni.setStorageSync(DB_KEY, JSON.stringify(db));
}
function uid(prefix) {
return `${prefix}_${Date.now()}_${Math.random().toString(16).slice(2)}`;
}
export function ensureSeed(archiveId, archive) {
if (!archiveId) return;
const db = getDb();
if (!Array.isArray(db.visitRecordsByArchiveId[archiveId]) || db.visitRecordsByArchiveId[archiveId].length === 0) {
const now = Date.now();
db.visitRecordsByArchiveId[archiveId] = [
{
_id: uid('mr'),
medicalType: 'outpatient',
tempName: '门诊记录',
templateType: 'outpatient',
teamId: 'team_1',
sortTime: now - 1000 * 60 * 60 * 24 * 2,
visitTime: dayjs(now - 1000 * 60 * 60 * 24 * 2).format('YYYY-MM-DD'),
corpName: '某某医院',
deptName: '呼吸内科',
doctor: '李医生',
diagnosisName: '急性上呼吸道感染mock',
treatmentPlan: '建议1对症处理退热2多饮水休息3如出现呼吸困难及时就医。',
disposePlan: '建议1继续对症治疗2监测体温与血压3如3天无缓解或加重立即复诊。',
summary: '初诊:对症处理并随访。',
files: [{ url: '/static/tabbar/home.png', name: '示例图片1' }],
createTime: now - 1000 * 60 * 60 * 24 * 2,
creatorName: '李珊珊',
},
{
_id: uid('mr'),
medicalType: 'inhospital',
tempName: '住院记录',
templateType: 'inhospital',
teamId: 'team_2',
sortTime: now - 1000 * 60 * 60 * 24 * 15,
inhosDate: dayjs(now - 1000 * 60 * 60 * 24 * 15).format('YYYY-MM-DD'),
corpName: '某某医院',
diagnosisName: '术后复查mock',
surgeryName: '阑尾切除术',
summary: '复诊:术后复查,恢复良好。',
files: [],
createTime: now - 1000 * 60 * 60 * 24 * 15,
creatorName: '王护士',
},
{
_id: uid('mr'),
medicalType: 'preConsultation',
tempName: '预问诊记录',
templateType: 'preConsultation',
teamId: 'team_1',
sortTime: now - 1000 * 60 * 60 * 6,
consultDate: dayjs(now - 1000 * 60 * 60 * 6).format('YYYY-MM-DD'),
chiefComplaint: '咽痛、流涕 2 天mock',
presentIllness: '近2天受凉后出现咽痛、流涕体温最高 38.2℃。',
pastHistory: '既往体健。',
allergyHistory: '无明确过敏史。',
summary: '建议对症处理,必要时线下就医。',
files: [],
createTime: now - 1000 * 60 * 60 * 6,
creatorName: '李珊珊',
},
];
}
if (!Array.isArray(db.serviceRecordsByArchiveId[archiveId]) || db.serviceRecordsByArchiveId[archiveId].length === 0) {
const now = Date.now();
db.serviceRecordsByArchiveId[archiveId] = Array.from({ length: 18 }).map((_, idx) => {
const eventType =
idx % 5 === 0 ? 'questionnaire' : idx % 4 === 0 ? 'article' : idx % 3 === 0 ? 'sms' : 'phone';
const executionTime = now - 1000 * 60 * 60 * (idx * 6 + 3);
const executeTeamId = idx % 2 === 0 ? 'team_1' : 'team_2';
const executeTeamName = executeTeamId === 'team_1' ? '口腔一科(示例)' : '正畸团队(示例)';
return {
_id: uid('sr'),
eventType,
taskContent: `服务内容示例 #${idx + 1}:这里是任务描述,支持长文本展开收起。`,
result: idx % 7 === 0 ? '已联系患者,已确认到院时间。' : '',
executorName: idx % 2 === 0 ? '李医生' : '王护士',
executeTeamId,
executeTeamName,
executionTime,
pannedEventSendFile:
eventType === 'article'
? { type: 'article', url: 'https://example.com/article/1' }
: eventType === 'questionnaire'
? { type: 'questionnaire', surveryId: 'q_1' }
: null,
archiveName: archive?.name || '',
};
});
}
if (!Array.isArray(db.followupsByArchiveId[archiveId]) || db.followupsByArchiveId[archiveId].length === 0) {
const now = Date.now();
db.followupsByArchiveId[archiveId] = Array.from({ length: 22 }).map((_, idx) => {
const plannedExecutionTime = now + 1000 * 60 * 60 * 24 * ((idx % 9) - 2);
const createTime = now - 1000 * 60 * 60 * (idx * 5 + 2);
const statusPool = ['processing', 'notStart', 'treated', 'cancelled', 'expired'];
const status = statusPool[idx % statusPool.length];
const eventTypePool = ['followup', 'revisit', 'questionnaire', 'other'];
const eventType = eventTypePool[idx % eventTypePool.length];
const executeTeamId = idx % 2 === 0 ? 'team_1' : 'team_2';
const executeTeamName = executeTeamId === 'team_1' ? '口腔一科(示例)' : '正畸团队(示例)';
return {
_id: uid('td'),
plannedExecutionTime,
planDate: dayjs(plannedExecutionTime).format('YYYY-MM-DD'),
createTime,
createTimeStr: dayjs(createTime).format('YYYY-MM-DD HH:mm'),
executorName: idx % 2 === 0 ? '李医生' : '王护士',
executeTeamId,
executeTeamName,
creatorName: idx % 3 === 0 ? '系统' : '管理员A',
status,
eventType,
eventTypeLabel:
eventType === 'followup' ? '回访' : eventType === 'revisit' ? '复诊提醒' : eventType === 'questionnaire' ? '问卷' : '其他',
eventStatusLabel:
status === 'processing'
? '待处理'
: status === 'notStart'
? '未开始'
: status === 'treated'
? '已完成'
: status === 'cancelled'
? '已取消'
: '已过期',
taskContent: `回访任务示例 #${idx + 1}:电话回访/提醒到院等。`,
result: status === 'treated' ? '已完成回访,患者反馈良好。' : '',
archiveName: archive?.name || '',
};
});
}
setDb(db);
}
export function getCurrentTeamId() {
const v = uni.getStorageSync('ykt_mock_current_team_id');
return v ? String(v) : 'team_1';
}
function getSortTimeTitle(templateType) {
if (templateType === 'outpatient') return 'visitTime';
if (templateType === 'inhospital') return 'inhosDate';
if (templateType === 'preConsultation') return 'consultDate';
if (templateType === 'physicalExaminationTemplate') return 'inspectDate';
return '';
}
export function queryVisitRecords({ archiveId, medicalType = 'ALL', timeRange = 'ALL', teamId = '', shareAllTeams = false }) {
const db = getDb();
const list = Array.isArray(db.visitRecordsByArchiveId[archiveId]) ? db.visitRecordsByArchiveId[archiveId] : [];
const withDate = list.map((i) => {
const type = i.templateType || i.medicalType || '';
const timeTitle = getSortTimeTitle(type);
const rawDate = timeTitle ? i[timeTitle] : '';
const fallback = i.sortTime ? dayjs(i.sortTime).format('YYYY-MM-DD') : '';
const date = rawDate || fallback;
return {
...i,
dateStr: date ? String(date) : '',
date: date ? String(date) : '',
createDateStr: i.createTime ? dayjs(i.createTime).format('YYYY-MM-DD') : '',
};
});
let filtered = [...withDate];
if (medicalType !== 'ALL') filtered = filtered.filter((i) => (i.medicalType || i.templateType) === medicalType);
if (!shareAllTeams && teamId) filtered = filtered.filter((i) => !i.teamId || i.teamId === teamId);
if (timeRange && timeRange !== 'ALL') {
const days = timeRange === 'today' ? 0 : timeRange === '7d' ? 7 : timeRange === '30d' ? 30 : null;
if (days !== null) {
const start = days === 0 ? dayjs().startOf('day') : dayjs().subtract(days, 'day').startOf('day');
const startMs = start.valueOf();
filtered = filtered.filter((i) => (i.sortTime || 0) >= startMs);
}
}
filtered.sort((a, b) => (b.sortTime || 0) - (a.sortTime || 0));
return filtered;
}
export function getVisitRecord({ archiveId, id }) {
const db = getDb();
const list = Array.isArray(db.visitRecordsByArchiveId[archiveId]) ? db.visitRecordsByArchiveId[archiveId] : [];
return list.find((i) => i._id === id) || null;
}
export function upsertVisitRecord({ archiveId, record }) {
const db = getDb();
const list = Array.isArray(db.visitRecordsByArchiveId[archiveId]) ? db.visitRecordsByArchiveId[archiveId] : [];
const next = { ...record };
if (!next._id) next._id = uid('mr');
if (!next.sortTime) next.sortTime = Date.now();
if (!next.createTime) next.createTime = Date.now();
const idx = list.findIndex((i) => i._id === next._id);
if (idx >= 0) list[idx] = { ...list[idx], ...next };
else list.unshift(next);
db.visitRecordsByArchiveId[archiveId] = list;
setDb(db);
return next;
}
export function removeVisitRecord({ archiveId, id }) {
const db = getDb();
const list = Array.isArray(db.visitRecordsByArchiveId[archiveId]) ? db.visitRecordsByArchiveId[archiveId] : [];
db.visitRecordsByArchiveId[archiveId] = list.filter((i) => i._id !== id);
setDb(db);
}
export function queryServiceRecords({ archiveId, page = 1, pageSize = 10, eventType = 'ALL', teamId = 'ALL', dateRange = [] }) {
const db = getDb();
const list = Array.isArray(db.serviceRecordsByArchiveId[archiveId]) ? db.serviceRecordsByArchiveId[archiveId] : [];
let filtered = [...list];
if (eventType !== 'ALL') filtered = filtered.filter((i) => i.eventType === eventType);
if (teamId !== 'ALL') filtered = filtered.filter((i) => i.executeTeamId === teamId);
if (Array.isArray(dateRange) && dateRange.length === 2 && dateRange[0] && dateRange[1]) {
filtered = filtered.filter((i) => {
const d = i.executionTime ? dayjs(i.executionTime).format('YYYY-MM-DD') : '';
return d >= dateRange[0] && d <= dateRange[1];
});
}
filtered.sort((a, b) => (b.executionTime || 0) - (a.executionTime || 0));
const total = filtered.length;
const pages = Math.ceil(total / pageSize) || 1;
const start = (page - 1) * pageSize;
const slice = filtered.slice(start, start + pageSize);
return { list: slice, total, pages };
}
export function getServiceRecord({ archiveId, id }) {
const db = getDb();
const list = Array.isArray(db.serviceRecordsByArchiveId[archiveId]) ? db.serviceRecordsByArchiveId[archiveId] : [];
return list.find((i) => i._id === id) || null;
}
export function upsertServiceRecord({ archiveId, record }) {
const db = getDb();
const list = Array.isArray(db.serviceRecordsByArchiveId[archiveId]) ? db.serviceRecordsByArchiveId[archiveId] : [];
const next = { ...record };
if (!next._id) next._id = uid('sr');
if (!next.executionTime) next.executionTime = Date.now();
const idx = list.findIndex((i) => i._id === next._id);
if (idx >= 0) list[idx] = { ...list[idx], ...next };
else list.unshift(next);
db.serviceRecordsByArchiveId[archiveId] = list;
setDb(db);
return next;
}
export function removeServiceRecord({ archiveId, id }) {
const db = getDb();
const list = Array.isArray(db.serviceRecordsByArchiveId[archiveId]) ? db.serviceRecordsByArchiveId[archiveId] : [];
db.serviceRecordsByArchiveId[archiveId] = list.filter((i) => i._id !== id);
setDb(db);
}
export function queryFollowups({ archiveId, page = 1, pageSize = 10, status = 'all', isMy = false, eventTypes = [], teamId = 'ALL', planRange = ['', ''] }) {
const db = getDb();
const list = Array.isArray(db.followupsByArchiveId[archiveId]) ? db.followupsByArchiveId[archiveId] : [];
let filtered = [...list];
if (status !== 'all') filtered = filtered.filter((i) => i.status === status);
if (isMy) filtered = filtered.filter((i) => i.executorName === '李医生');
if (Array.isArray(eventTypes) && eventTypes.length) filtered = filtered.filter((i) => eventTypes.includes(i.eventType));
if (teamId !== 'ALL') filtered = filtered.filter((i) => i.executeTeamId === teamId);
if (planRange && (planRange[0] || planRange[1])) {
if (planRange[0]) filtered = filtered.filter((i) => i.planDate >= planRange[0]);
if (planRange[1]) filtered = filtered.filter((i) => i.planDate <= planRange[1]);
}
filtered.sort((a, b) => (b.plannedExecutionTime || 0) - (a.plannedExecutionTime || 0));
const total = filtered.length;
const pages = Math.ceil(total / pageSize) || 1;
const start = (page - 1) * pageSize;
const slice = filtered.slice(start, start + pageSize);
return { list: slice, total, pages };
}
export function getFollowup({ archiveId, id }) {
const db = getDb();
const list = Array.isArray(db.followupsByArchiveId[archiveId]) ? db.followupsByArchiveId[archiveId] : [];
return list.find((i) => i._id === id) || null;
}
export function upsertFollowup({ archiveId, followup }) {
const db = getDb();
const list = Array.isArray(db.followupsByArchiveId[archiveId]) ? db.followupsByArchiveId[archiveId] : [];
const next = { ...followup };
if (!next._id) next._id = uid('td');
if (!next.plannedExecutionTime) next.plannedExecutionTime = Date.now();
next.planDate = dayjs(next.plannedExecutionTime).format('YYYY-MM-DD');
if (!next.createTime) next.createTime = Date.now();
next.createTimeStr = dayjs(next.createTime).format('YYYY-MM-DD HH:mm');
const idx = list.findIndex((i) => i._id === next._id);
if (idx >= 0) list[idx] = { ...list[idx], ...next };
else list.unshift(next);
db.followupsByArchiveId[archiveId] = list;
setDb(db);
return next;
}
export function removeFollowup({ archiveId, id }) {
const db = getDb();
const list = Array.isArray(db.followupsByArchiveId[archiveId]) ? db.followupsByArchiveId[archiveId] : [];
db.followupsByArchiveId[archiveId] = list.filter((i) => i._id !== id);
setDb(db);
}