411 lines
18 KiB
JavaScript
411 lines
18 KiB
JavaScript
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);
|
||
}
|