ykt-wxapp/pages/case/patient-inner-info.vue
2026-01-29 13:36:43 +08:00

500 lines
14 KiB
Vue
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.

<template>
<view class="page">
<view class="body">
<scroll-view scroll-y class="scroll">
<view class="form-wrap">
<form-template v-if="items.length" ref="formRef" :items="items" :form="forms" :rule="rules" :filterRule="filterRule" @change="onChange" />
</view>
<view class="scroll-spacer" />
</scroll-view>
<!-- #ifdef MP-WEIXIN -->
<cover-view class="footer">
<cover-view class="btn plain" @tap="prev">上一步</cover-view>
<cover-view class="btn primary" @tap="save">保存</cover-view>
</cover-view>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="footer">
<button class="btn plain" @click="prev">上一步</button>
<button class="btn primary" @click="save">保存</button>
</view>
<!-- #endif -->
</view>
</view>
</template>
<script setup>
import { computed, reactive, ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import FormTemplate from '@/components/form-template/index.vue';
import { storeToRefs } from 'pinia';
import api from '@/utils/api';
import useAccountStore from '@/store/account';
import { hideLoading, loading, toast } from '@/utils/widget';
const BASE_KEY = 'patient-create-base';
const INNER_KEY = 'patient-create-inner';
const CURRENT_TEAM_STORAGE_KEY = 'ykt_case_current_team';
const NEED_RELOAD_STORAGE_KEY = 'ykt_case_need_reload';
const formRef = ref(null);
const form = reactive({});
const baseForm = reactive({});
const forms = computed(() => ({ ...baseForm, ...form }));
const items = ref([]);
const rules = {};
const filterRule = {
reference(formModel) {
const customerSource = Array.isArray(formModel.customerSource)
? formModel.customerSource
: typeof formModel.customerSource === 'string'
? [formModel.customerSource]
: [];
return ['同事推荐', '客户推荐'].includes(customerSource[0]) && customerSource.length === 1;
},
};
const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore);
const { getDoctorInfo } = accountStore;
function normalizeChangeValue(value) {
if (value && typeof value === 'object' && 'value' in value) return value.value;
return value;
}
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: 'tagPicker',
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 unwrapTemplate(res) {
// 兼容后端返回结构:{ success, data: { data: template } } / { success, data: template } / { templateList: [] }
const d = res?.data;
if (d && typeof d === 'object') {
if (d.data && typeof d.data === 'object') return d.data;
return d;
}
return res && typeof res === 'object' ? res : {};
}
function ensureInternalDefaults(list, { stageOptions = [], tagOptions = [] } = {}) {
const items = Array.isArray(list) ? [...list] : [];
const has = (title) => items.some((i) => i && String(i.title || '') === title);
// 与 mobile 内部信息页一致:标签 / 备注 / 阶段
if (!has('tagIds')) {
items.unshift({
title: 'tagIds',
name: '标签',
type: 'tag',
operateType: 'formCell',
required: false,
wordLimit: 200,
range: tagOptions,
});
}
if (!has('notes')) {
items.push({
title: 'notes',
name: '备注',
type: 'textarea',
operateType: 'formCell',
required: false,
wordLimit: 200,
});
}
if (!has('customerStage')) {
items.push({
title: 'customerStage',
name: '阶段',
type: 'customerStage',
operateType: 'formCell',
required: false,
range: stageOptions,
});
}
return items;
}
function unwrapListPayload(res) {
const root = res && typeof res === 'object' ? res : {};
const d = root.data && typeof root.data === 'object' ? root.data : root;
if (!d) return [];
if (Array.isArray(d)) return d;
if (Array.isArray(d.data)) return d.data;
if (Array.isArray(d.list)) return d.list;
if (d.data && typeof d.data === 'object') {
if (Array.isArray(d.data.data)) return d.data.data;
if (Array.isArray(d.data.list)) return d.data.list;
}
return [];
}
function parseCustomerStageOptions(res) {
const list = unwrapListPayload(res);
return list
.map((i) => {
if (typeof i === 'string') return { label: i, value: i };
const label = i?.name ?? i?.label ?? '';
const value = i?.type ?? 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);
}
function parseTagOptions(res) {
const list = unwrapListPayload(res);
let flat = [];
// 形态1[{ groupName, tag: [{id,name}] }]
if (list.length && typeof list[0] === 'object' && Array.isArray(list[0]?.tag)) {
flat = list.reduce((acc, g) => {
if (Array.isArray(g?.tag)) acc.push(...g.tag);
return acc;
}, []);
} else {
flat = list;
}
const options = flat
.map((i) => {
if (typeof i === 'string') return { label: i, value: i };
const label = i?.name ?? i?.label ?? i?.text ?? '';
const value = i?.id ?? i?.value ?? i?.key ?? label;
if (!label && (value === undefined || value === null || value === '')) return null;
return { label: String(label || value), value: String(value) };
})
.filter(Boolean);
const seen = new Set();
return options.filter((i) => {
if (!i?.value) return false;
if (seen.has(i.value)) return false;
seen.add(i.value);
return true;
});
}
function isStageItem(i) {
const title = String(i?.title || '');
const type = String(i?.type || '');
const name = String(i?.name || '');
return title === 'customerStage' || type === 'customerStage' || name.includes('阶段');
}
function isTagItem(i) {
const title = String(i?.title || '');
const type = String(i?.type || '');
const name = String(i?.name || '');
return title === 'tagIds' || title === 'tag' || type === 'tag' || name.includes('标签');
}
async function loadInternalTemplate() {
const corpId = String(account.value?.corpId || doctorInfo.value?.corpId || '') || '';
if (!corpId) return;
loading('加载中...');
const [res, stageRes, tagRes] = await Promise.all([
api('getCurrentTemplate', { corpId, templateType: 'internalTemplate' }),
api('getCustomerType', { corpId }),
api('getCorpTags', { corpId }),
]);
hideLoading();
if (!res?.success) {
toast(res?.message || '获取模板失败');
return;
}
console.log('[debug][wxapp][patient-inner-info] corpId:', corpId);
console.log('[debug][wxapp][patient-inner-info] getCustomerType:', stageRes);
console.log('[debug][wxapp][patient-inner-info] getCorpTags:', tagRes);
const stageOptions = parseCustomerStageOptions(stageRes);
const tagOptions = parseTagOptions(tagRes);
console.log('[debug][wxapp][patient-inner-info] parsed stageOptions:', stageOptions.length, stageOptions);
console.log('[debug][wxapp][patient-inner-info] parsed tagOptions:', tagOptions.length, tagOptions);
const temp = unwrapTemplate(res);
const list = ensureInternalDefaults(Array.isArray(temp.templateList) ? temp.templateList : [], { stageOptions, tagOptions }).map((i) => {
const item = { ...(i || {}) };
if (isStageItem(item) && (!Array.isArray(item.range) || item.range.length === 0)) item.range = stageOptions;
if (isTagItem(item) && (!Array.isArray(item.range) || item.range.length === 0)) item.range = tagOptions;
if (isTagItem(item) && item.title === 'tag') item.title = 'tagIds';
return item;
});
items.value = list
.filter((i) => i && i.fieldStatus !== 'disable')
.filter((i) => i.operateType !== 'onlyRead')
.map(normalizeTemplateItem);
const debugStage = items.value.find(isStageItem);
const debugTag = items.value.find(isTagItem);
console.log('[debug][wxapp][patient-inner-info] final stage item:', debugStage);
console.log('[debug][wxapp][patient-inner-info] final tag item:', debugTag);
}
onLoad(async () => {
const base = uni.getStorageSync(BASE_KEY);
if (!base || typeof base !== 'object') {
uni.showToast({ title: '请先填写基础信息', icon: 'none' });
uni.navigateBack();
return;
}
Object.assign(baseForm, base);
const cached = uni.getStorageSync(INNER_KEY);
if (cached && typeof cached === 'object') {
Object.assign(form, cached);
}
if (!doctorInfo.value && account.value?.openid) {
try {
await getDoctorInfo();
} catch (e) {
// ignore
}
}
await loadInternalTemplate();
});
function onChange({ title, value }) {
form[title] = normalizeChangeValue(value);
}
function prev() {
uni.navigateBack();
}
function getUserId() {
const d = doctorInfo.value || {};
const a = account.value || {};
return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || '') || '';
}
function normalizeFormValue(value) {
if (value && typeof value === 'object' && 'value' in value) return value.value;
if (Array.isArray(value)) return value.map(normalizeFormValue);
return value;
}
function normalizeFormObject(obj) {
if (!obj || typeof obj !== 'object') return {};
return Object.keys(obj).reduce((acc, key) => {
acc[key] = normalizeFormValue(obj[key]);
return acc;
}, {});
}
function buildPayload(base, inner) {
const team = uni.getStorageSync(CURRENT_TEAM_STORAGE_KEY) || {};
const corpId = String(account.value?.corpId || team?.corpId || base?.corpId || '') || '';
const baseForm = normalizeFormObject(base);
const innerForm = normalizeFormObject(inner);
const payload = {
...baseForm,
...innerForm,
corpId,
teamId: baseForm.teamId || (team?.teamId ? String(team.teamId) : ''),
creator: getUserId(),
addMethod: 'manual',
};
// 兼容旧字段命名(历史缓存)
if (payload.gender && !payload.sex) payload.sex = payload.gender;
if (payload.idNo && !payload.idCard) payload.idCard = payload.idNo;
if (payload.idType && !payload.cardType) payload.cardType = payload.idType;
if (payload.tag && !payload.tagIds) payload.tagIds = payload.tag;
if (Array.isArray(payload.teamId)) payload.teamId = payload.teamId[0] || '';
if (payload.customerSource && typeof payload.customerSource === 'string') {
payload.customerSource = [payload.customerSource];
}
return { payload, team };
}
async function save() {
if (formRef.value?.verify && !formRef.value.verify()) return;
const base = uni.getStorageSync(BASE_KEY) || {};
uni.setStorageSync(INNER_KEY, { ...form });
const { payload, team } = buildPayload(base, form);
if (!payload.corpId || !payload.teamId || !payload.creator) {
toast('缺少用户/团队信息,请先完成登录与个人信息');
return;
}
loading('请求中...');
try {
const createTeamId = Array.isArray(payload.teamId) ? (payload.teamId[0] || '') : String(payload.teamId || '');
const res = await api('addCustomer', {
createTeamId,
createTeamName: team?.name || '',
params: payload,
});
hideLoading();
if (!res?.success) {
toast(res?.message || '新增失败');
return;
}
toast('新增成功');
uni.removeStorageSync(BASE_KEY);
uni.removeStorageSync(INNER_KEY);
uni.setStorageSync(NEED_RELOAD_STORAGE_KEY, 1);
uni.navigateBack({ delta: 2 });
} catch (e) {
hideLoading();
toast('新增失败');
}
}
</script>
<style lang="scss" scoped>
.page {
height: 100vh;
background: #f6f6f6;
display: flex;
flex-direction: column;
}
.body {
flex: 1;
display: flex;
flex-direction: column;
}
.scroll {
flex: 1;
min-height: 0;
height: 0;
}
.form-wrap {
background: #fff;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
display: flex;
gap: 12px;
z-index: 10;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.06);
}
.scroll-spacer {
height: calc(120px + env(safe-area-inset-bottom));
}
.btn {
flex: 1;
height: 44px;
line-height: 44px;
border-radius: 6px;
font-size: 15px;
text-align: center;
}
.btn::after {
border: none;
}
.btn.plain {
background: #fff;
color: #666;
border: 1px solid #ddd;
}
.btn.primary {
background: #5d8aff;
color: #fff;
}
</style>