2026-01-22 15:54:15 +08:00
|
|
|
<template>
|
|
|
|
|
<view class="page">
|
2026-01-23 17:10:23 +08:00
|
|
|
<CustomerProfileTab
|
|
|
|
|
:data="archive"
|
|
|
|
|
:baseItems="baseItems"
|
|
|
|
|
:internalItems="internalItems"
|
|
|
|
|
:floatingBottom="16"
|
|
|
|
|
@save="savePatch"
|
|
|
|
|
/>
|
2026-01-22 15:54:15 +08:00
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref } from 'vue';
|
|
|
|
|
import { onLoad, onShow } from '@dcloudio/uni-app';
|
2026-01-23 17:10:23 +08:00
|
|
|
import { storeToRefs } from 'pinia';
|
2026-01-22 15:54:15 +08:00
|
|
|
import CustomerProfileTab from '@/components/archive-detail/customer-profile-tab.vue';
|
2026-01-23 17:10:23 +08:00
|
|
|
import api from '@/utils/api';
|
|
|
|
|
import useAccountStore from '@/store/account';
|
|
|
|
|
import { hideLoading, loading, toast } from '@/utils/widget';
|
2026-01-22 15:54:15 +08:00
|
|
|
|
|
|
|
|
const STORAGE_KEY = 'ykt_case_archive_detail';
|
2026-01-23 17:10:23 +08:00
|
|
|
const CURRENT_TEAM_STORAGE_KEY = 'ykt_case_current_team';
|
|
|
|
|
const NEED_RELOAD_STORAGE_KEY = 'ykt_case_need_reload';
|
2026-01-22 15:54:15 +08:00
|
|
|
|
|
|
|
|
const archiveId = ref('');
|
|
|
|
|
const archive = ref({
|
|
|
|
|
name: '',
|
|
|
|
|
sex: '',
|
|
|
|
|
age: '',
|
|
|
|
|
avatar: '',
|
|
|
|
|
mobile: '',
|
|
|
|
|
outpatientNo: '',
|
|
|
|
|
inpatientNo: '',
|
|
|
|
|
medicalRecordNo: '',
|
|
|
|
|
createTime: '',
|
|
|
|
|
creator: '',
|
|
|
|
|
createdByDoctor: true,
|
|
|
|
|
hasBindWechat: false,
|
|
|
|
|
notes: '',
|
|
|
|
|
groups: [],
|
|
|
|
|
groupOptions: [],
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-23 17:10:23 +08:00
|
|
|
const baseItems = ref([]);
|
|
|
|
|
const internalItems = ref([]);
|
|
|
|
|
|
|
|
|
|
const accountStore = useAccountStore();
|
|
|
|
|
const { account, doctorInfo } = storeToRefs(accountStore);
|
|
|
|
|
const { getDoctorInfo } = accountStore;
|
|
|
|
|
|
|
|
|
|
function normalizeOptions(options) {
|
|
|
|
|
if (!Array.isArray(options)) return [];
|
|
|
|
|
if (!options.length) return [];
|
|
|
|
|
if (typeof options[0] === 'string') return options.filter((i) => typeof i === 'string');
|
|
|
|
|
if (typeof options[0] === 'object') {
|
|
|
|
|
return options
|
|
|
|
|
.map((i) => {
|
|
|
|
|
const label = i?.label ?? i?.name ?? i?.text ?? i?.title ?? '';
|
|
|
|
|
const value = i?.value ?? i?.id ?? i?.key ?? label;
|
|
|
|
|
if (!label && (value === undefined || value === null || value === '')) return null;
|
|
|
|
|
return { label: String(label || value), value: String(value) };
|
|
|
|
|
})
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeTemplateItem(item) {
|
|
|
|
|
const next = { ...(item || {}) };
|
|
|
|
|
if (next.operateType === 'custom') next.operateType = 'formCell';
|
|
|
|
|
|
|
|
|
|
const originalType = next.type;
|
|
|
|
|
const customTypeMap = {
|
|
|
|
|
customerSource: 'select',
|
|
|
|
|
customerStage: 'select',
|
|
|
|
|
tag: 'multiSelectAndOther',
|
|
|
|
|
reference: 'input',
|
|
|
|
|
selectWwuser: 'select',
|
|
|
|
|
files: 'files',
|
|
|
|
|
corpProject: 'select',
|
|
|
|
|
diagnosis: 'textarea',
|
|
|
|
|
BMI: 'input',
|
|
|
|
|
bloodPressure: 'textarea',
|
|
|
|
|
selfMultipleDiseases: 'textarea',
|
|
|
|
|
};
|
|
|
|
|
if (originalType && customTypeMap[originalType]) {
|
|
|
|
|
next.__originType = originalType;
|
|
|
|
|
next.type = customTypeMap[originalType];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const aliasTypeMap = {
|
|
|
|
|
text: 'input',
|
|
|
|
|
string: 'input',
|
|
|
|
|
number: 'input',
|
|
|
|
|
integer: 'input',
|
|
|
|
|
int: 'input',
|
|
|
|
|
};
|
|
|
|
|
if (next.type && aliasTypeMap[next.type]) {
|
|
|
|
|
next.type = aliasTypeMap[next.type];
|
|
|
|
|
if (!next.inputType && (originalType === 'number' || originalType === 'integer' || originalType === 'int')) next.inputType = 'number';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rawRange = next.range || next.options || next.optionList || next.values || [];
|
|
|
|
|
const range = normalizeOptions(rawRange);
|
|
|
|
|
if (next.type === 'select' || next.type === 'selectAndOther' || next.type === 'selectAndImage') {
|
|
|
|
|
next.range = range;
|
|
|
|
|
} else if (next.type === 'radio') {
|
|
|
|
|
if (range.length && typeof range[0] === 'object') {
|
|
|
|
|
next.type = 'select';
|
|
|
|
|
next.range = range;
|
|
|
|
|
} else {
|
|
|
|
|
next.range = Array.isArray(rawRange) ? rawRange : [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!next.operateType) next.operateType = 'formCell';
|
|
|
|
|
next.required = Boolean(next.required);
|
|
|
|
|
if (next.type === 'input' && (next.wordLimit === undefined || next.wordLimit === null || next.wordLimit === '')) next.wordLimit = 20;
|
|
|
|
|
if (next.type === 'textarea' && (next.wordLimit === undefined || next.wordLimit === null || next.wordLimit === '')) next.wordLimit = 200;
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getUserId() {
|
|
|
|
|
const d = doctorInfo.value || {};
|
|
|
|
|
const a = account.value || {};
|
|
|
|
|
return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || '') || '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCorpId() {
|
|
|
|
|
const d = doctorInfo.value || {};
|
|
|
|
|
const a = account.value || {};
|
|
|
|
|
const team = uni.getStorageSync(CURRENT_TEAM_STORAGE_KEY) || {};
|
|
|
|
|
return String(d.corpId || a.corpId || team.corpId || '') || '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function ensureDoctor() {
|
|
|
|
|
if (doctorInfo.value) return;
|
|
|
|
|
if (!account.value?.openid) return;
|
|
|
|
|
try {
|
|
|
|
|
await getDoctorInfo();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// ignore
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-22 15:54:15 +08:00
|
|
|
function loadFromStorage() {
|
|
|
|
|
const cached = uni.getStorageSync(STORAGE_KEY);
|
|
|
|
|
if (cached && typeof cached === 'object') {
|
|
|
|
|
archive.value = {
|
|
|
|
|
...archive.value,
|
|
|
|
|
...cached,
|
|
|
|
|
groups: Array.isArray(cached.groups) ? cached.groups : archive.value.groups,
|
|
|
|
|
groupOptions: Array.isArray(cached.groupOptions) ? cached.groupOptions : archive.value.groupOptions,
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-01-23 17:10:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadTemplates() {
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
if (!corpId) return;
|
|
|
|
|
const [baseRes, internalRes] = await Promise.all([
|
|
|
|
|
api('getCurrentTemplate', { corpId, templateType: 'baseTemplate' }),
|
|
|
|
|
api('getCurrentTemplate', { corpId, templateType: 'internalTemplate' }),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (baseRes?.success) {
|
|
|
|
|
const temp = baseRes?.data && typeof baseRes.data === 'object' ? baseRes.data : baseRes;
|
|
|
|
|
const list = Array.isArray(temp.templateList) ? temp.templateList : [];
|
|
|
|
|
baseItems.value = list
|
|
|
|
|
.filter((i) => i && i.fieldStatus !== 'disable')
|
|
|
|
|
.filter((i) => i.operateType !== 'onlyRead')
|
|
|
|
|
.map(normalizeTemplateItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (internalRes?.success) {
|
|
|
|
|
const temp = internalRes?.data && typeof internalRes.data === 'object' ? internalRes.data : internalRes;
|
|
|
|
|
const list = Array.isArray(temp.templateList) ? temp.templateList : [];
|
|
|
|
|
internalItems.value = list
|
|
|
|
|
.filter((i) => i && i.fieldStatus !== 'disable')
|
|
|
|
|
.filter((i) => i.operateType !== 'onlyRead')
|
|
|
|
|
.map(normalizeTemplateItem);
|
2026-01-22 15:54:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 17:10:23 +08:00
|
|
|
async function loadArchiveFromApi() {
|
|
|
|
|
if (!archiveId.value) return;
|
|
|
|
|
loading('加载中...');
|
|
|
|
|
try {
|
|
|
|
|
const res = await api('getCustomerByCustomerId', { customerId: archiveId.value });
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
toast(res?.message || '获取档案失败');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
archive.value = { ...archive.value, ...(res.data && typeof res.data === 'object' ? res.data : {}) };
|
|
|
|
|
uni.setStorageSync(STORAGE_KEY, { ...archive.value });
|
|
|
|
|
} catch (e) {
|
|
|
|
|
toast('获取档案失败');
|
|
|
|
|
} finally {
|
|
|
|
|
hideLoading();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function savePatch(patch) {
|
|
|
|
|
const p = patch && typeof patch === 'object' ? patch : {};
|
|
|
|
|
const params = Object.keys(p).reduce((acc, k) => {
|
|
|
|
|
const v = p[k];
|
|
|
|
|
if (v !== undefined) acc[k] = v;
|
|
|
|
|
return acc;
|
|
|
|
|
}, {});
|
|
|
|
|
if (Object.keys(params).length === 0) return;
|
|
|
|
|
|
|
|
|
|
await ensureDoctor();
|
|
|
|
|
const userId = getUserId();
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
const team = uni.getStorageSync(CURRENT_TEAM_STORAGE_KEY) || {};
|
|
|
|
|
const createTeamId = team?.teamId ? String(team.teamId) : '';
|
|
|
|
|
const createTeamName = team?.name ? String(team.name) : '';
|
|
|
|
|
if (!archiveId.value || !userId || !corpId) {
|
|
|
|
|
toast('缺少用户/团队信息,请先完成登录与个人信息');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loading('保存中...');
|
|
|
|
|
try {
|
|
|
|
|
const res = await api('updateCustomer', {
|
|
|
|
|
id: archiveId.value,
|
|
|
|
|
params,
|
|
|
|
|
userId,
|
|
|
|
|
corpId,
|
|
|
|
|
createTeamId,
|
|
|
|
|
createTeamName,
|
|
|
|
|
});
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
toast(res?.message || '保存失败');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
toast('保存成功');
|
|
|
|
|
uni.setStorageSync(NEED_RELOAD_STORAGE_KEY, 1);
|
|
|
|
|
await loadArchiveFromApi();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
toast('保存失败');
|
|
|
|
|
} finally {
|
|
|
|
|
hideLoading();
|
|
|
|
|
}
|
2026-01-22 15:54:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onLoad((options) => {
|
|
|
|
|
archiveId.value = options?.archiveId ? String(options.archiveId) : '';
|
|
|
|
|
loadFromStorage();
|
2026-01-23 17:10:23 +08:00
|
|
|
ensureDoctor().then(async () => {
|
|
|
|
|
await Promise.all([loadTemplates(), loadArchiveFromApi()]);
|
|
|
|
|
});
|
2026-01-22 15:54:15 +08:00
|
|
|
});
|
|
|
|
|
|
2026-01-23 17:10:23 +08:00
|
|
|
onShow(() => {
|
|
|
|
|
loadFromStorage();
|
|
|
|
|
loadArchiveFromApi();
|
|
|
|
|
});
|
2026-01-22 15:54:15 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.page {
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
background: #f5f6f8;
|
|
|
|
|
}
|
|
|
|
|
</style>
|