2026-01-22 15:54:15 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<!-- Mobile 来源: ykt-management-mobile/src/pages/customer/customer-detail/health-profile/health-profile.vue -->
|
|
|
|
|
|
<view class="wrap">
|
|
|
|
|
|
<view class="filters">
|
|
|
|
|
|
<picker mode="selector" :range="typeRange" range-key="name" @change="pickType">
|
|
|
|
|
|
<view class="filter-pill">
|
2026-01-22 17:39:23 +08:00
|
|
|
|
<view class="pill-text">{{ currentType.value === 'ALL' ? '全部病历' : currentType.name }}</view>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
<uni-icons type="arrowdown" size="12" color="#666" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</picker>
|
|
|
|
|
|
|
2026-02-10 15:47:35 +08:00
|
|
|
|
<uni-datetime-picker type="daterange" v-model="dateRange" @change="pickTimeRange">
|
2026-01-22 17:39:23 +08:00
|
|
|
|
<view class="filter-pill">
|
2026-02-02 14:51:40 +08:00
|
|
|
|
<view class="pill-text">{{ dateRangeLabel }}</view>
|
2026-02-10 15:47:35 +08:00
|
|
|
|
<view class="pill-icons">
|
|
|
|
|
|
<view v-if="isDateRangeActive" class="pill-clear" @tap.stop="clearTimeRange" @click.stop="clearTimeRange">
|
|
|
|
|
|
<uni-icons type="closeempty" size="16" color="#666" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<uni-icons type="arrowdown" size="12" color="#666" />
|
|
|
|
|
|
</view>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
2026-02-02 14:51:40 +08:00
|
|
|
|
</uni-datetime-picker>
|
2026-01-22 17:39:23 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="showShareTip" class="share-tip">
|
|
|
|
|
|
<text class="share-tip-text">患者已授权其他团队,病历信息互通</text>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="list">
|
|
|
|
|
|
<view v-for="r in records" :key="r._id" class="card record" @click="edit(r)">
|
|
|
|
|
|
<view class="record-head">
|
2026-01-22 17:39:23 +08:00
|
|
|
|
<view class="record-date">{{ r.dateStr || '--' }}</view>
|
|
|
|
|
|
<view class="record-tag" :class="tagClass[r.templateType] || 'bg-blue'">{{ r.tempName || '病历' }}</view>
|
|
|
|
|
|
<view v-if="r.corpName === '其他' || r.corp === '其他'" class="record-tag bg-rose">外院</view>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
2026-01-22 15:54:15 +08:00
|
|
|
|
<view class="record-body">
|
2026-01-27 16:46:36 +08:00
|
|
|
|
<view v-for="(l, idx) in getDisplayLines(r)" :key="`${r._id}_${idx}`" class="line">
|
|
|
|
|
|
<text class="line-label">{{ l.label }}</text>
|
|
|
|
|
|
<text class="line-value">{{ l.value }}</text>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
|
|
|
|
|
<view v-if="getFiles(r).length" class="thumbs">
|
|
|
|
|
|
<view v-for="(f, idx) in getFiles(r).slice(0, 3)" :key="idx" class="thumb" @click.stop="previewFiles(r, idx)">
|
|
|
|
|
|
<image class="thumb-img" :src="f.url" mode="aspectFill" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-if="getFiles(r).length > 3" class="thumb-more">+{{ getFiles(r).length - 3 }}</view>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="record-foot">
|
2026-01-27 16:46:36 +08:00
|
|
|
|
<view class="foot-left">{{ getCreateFooter(r) }}</view>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-if="records.length === 0" class="empty">暂无数据</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-02-06 17:15:09 +08:00
|
|
|
|
<picker
|
|
|
|
|
|
class="fab-picker"
|
|
|
|
|
|
mode="selector"
|
|
|
|
|
|
:range="selectableTemplates"
|
|
|
|
|
|
range-key="name"
|
2026-02-06 18:04:34 +08:00
|
|
|
|
:disabled="fabPickerDisabled"
|
2026-02-06 17:15:09 +08:00
|
|
|
|
:style="{ bottom: `${floatingBottom}px` }"
|
|
|
|
|
|
@change="pickAddType"
|
|
|
|
|
|
>
|
2026-02-06 18:04:34 +08:00
|
|
|
|
<view class="fab" :class="{ 'fab--disabled': selectableTemplates.length === 0 }" @tap="onFabTap">
|
2026-02-06 17:15:09 +08:00
|
|
|
|
<uni-icons type="plusempty" size="24" color="#fff" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</picker>
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-01-27 16:46:36 +08:00
|
|
|
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
2026-02-11 17:11:46 +08:00
|
|
|
|
import { storeToRefs } from 'pinia';
|
2026-01-27 16:46:36 +08:00
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
|
import api from '@/utils/api';
|
2026-02-11 17:11:46 +08:00
|
|
|
|
import useAccountStore from '@/store/account';
|
2026-02-06 17:15:09 +08:00
|
|
|
|
import { loading, hideLoading, toast } from '@/utils/widget';
|
|
|
|
|
|
import { normalizeTemplate } from '../../utils/template';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
import { normalizeVisitRecordFormData } from '../../utils/visit-record';
|
|
|
|
|
|
import { normalizeFileUrl } from '@/utils/file';
|
2026-01-22 15:54:15 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
data: { type: Object, default: () => ({}) },
|
|
|
|
|
|
archiveId: { type: String, default: '' },
|
|
|
|
|
|
floatingBottom: { type: Number, default: 16 },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-02-11 17:11:46 +08:00
|
|
|
|
const accountStore = useAccountStore();
|
|
|
|
|
|
const { account, doctorInfo } = storeToRefs(accountStore);
|
|
|
|
|
|
const { getDoctorInfo } = accountStore;
|
|
|
|
|
|
|
2026-02-10 15:47:35 +08:00
|
|
|
|
const FALLBACK_TEMPLATE_TYPES = ['outpatient', 'inhospital', 'preConsultationRecord', 'physicalExaminationTemplate'];
|
2026-02-06 17:15:09 +08:00
|
|
|
|
const templates = ref([]);
|
|
|
|
|
|
const selectableTemplates = computed(() => templates.value.filter((i) => i && i.templateType && typeof i.name === 'string' && i.name.trim()));
|
2026-02-06 18:04:34 +08:00
|
|
|
|
const useActionSheet = computed(() => selectableTemplates.value.length > 0 && selectableTemplates.value.length <= 6);
|
|
|
|
|
|
const fabPickerDisabled = computed(() => selectableTemplates.value.length === 0 || useActionSheet.value);
|
2026-02-06 17:15:09 +08:00
|
|
|
|
const templateMap = computed(() => templates.value.reduce((m, t) => {
|
|
|
|
|
|
if (t?.templateType) m[String(t.templateType)] = t;
|
|
|
|
|
|
return m;
|
|
|
|
|
|
}, {}));
|
|
|
|
|
|
const availableTypes = computed(() => (templates.value.length ? templates.value.map((i) => i.templateType) : FALLBACK_TEMPLATE_TYPES));
|
2026-01-22 15:54:15 +08:00
|
|
|
|
|
|
|
|
|
|
const typeRange = computed(() => [{ name: '全部', value: 'ALL' }, ...templates.value.map((t) => ({ name: t.name, value: t.templateType }))]);
|
|
|
|
|
|
const currentType = ref({ name: '全部', value: 'ALL' });
|
|
|
|
|
|
|
|
|
|
|
|
const records = ref([]);
|
|
|
|
|
|
|
2026-02-02 14:51:40 +08:00
|
|
|
|
const dateRange = ref([]);
|
2026-02-10 15:47:35 +08:00
|
|
|
|
const isDateRangeActive = computed(
|
|
|
|
|
|
() => Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]
|
|
|
|
|
|
);
|
2026-02-02 14:51:40 +08:00
|
|
|
|
const dateRangeLabel = computed(() => {
|
|
|
|
|
|
if (Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]) {
|
|
|
|
|
|
return `${dateRange.value[0]} 至 ${dateRange.value[1]}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
return '全部时间';
|
|
|
|
|
|
});
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
2026-01-27 16:46:36 +08:00
|
|
|
|
function getCurrentTeamId() {
|
|
|
|
|
|
const team = uni.getStorageSync('ykt_case_current_team') || {};
|
|
|
|
|
|
return team?.teamId ? String(team.teamId) : '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 17:39:23 +08:00
|
|
|
|
const teamId = ref(getCurrentTeamId());
|
|
|
|
|
|
const showShareTip = computed(() => {
|
|
|
|
|
|
if (props.data && typeof props.data.shareAllTeams === 'boolean') return props.data.shareAllTeams;
|
|
|
|
|
|
const list = props.data?.authorizedTeams;
|
|
|
|
|
|
return Array.isArray(list) && list.length > 1;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const shareAllTeamsForQuery = computed(() => {
|
|
|
|
|
|
if (props.data && typeof props.data.shareAllTeams === 'boolean') return props.data.shareAllTeams;
|
|
|
|
|
|
const list = props.data?.authorizedTeams;
|
|
|
|
|
|
if (Array.isArray(list)) return list.length > 1;
|
|
|
|
|
|
// mock 场景:没有授权信息时默认互通,避免误隐藏数据
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-01-27 16:46:36 +08:00
|
|
|
|
function getCorpId() {
|
|
|
|
|
|
const team = uni.getStorageSync('ykt_case_current_team') || {};
|
2026-02-11 17:11:46 +08:00
|
|
|
|
const d = doctorInfo.value || {};
|
|
|
|
|
|
const a = account.value || {};
|
|
|
|
|
|
return String(team.corpId || d.corpId || a.corpId || '') || '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureDoctor() {
|
|
|
|
|
|
if (doctorInfo.value) return;
|
|
|
|
|
|
if (!account.value?.openid) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await getDoctorInfo();
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let corpMemberBatchInflight = null;
|
|
|
|
|
|
async function batchLoadCorpMembers(userIds) {
|
|
|
|
|
|
const ids = Array.isArray(userIds) ? userIds.map((v) => String(v || '').trim()).filter(Boolean) : [];
|
|
|
|
|
|
if (!ids.length) return;
|
|
|
|
|
|
const uniq = Array.from(new Set(ids));
|
|
|
|
|
|
const unknown = uniq.filter((id) => {
|
|
|
|
|
|
const existing = userNameMap.value?.[id];
|
|
|
|
|
|
return !existing || existing === id;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!unknown.length) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (corpMemberBatchInflight) return corpMemberBatchInflight;
|
|
|
|
|
|
|
|
|
|
|
|
await ensureDoctor();
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
if (!corpId) return;
|
|
|
|
|
|
|
|
|
|
|
|
corpMemberBatchInflight = (async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await api(
|
|
|
|
|
|
'getCorpMember',
|
|
|
|
|
|
{
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
pageSize: Math.min(Math.max(unknown.length, 10), 500),
|
|
|
|
|
|
params: {
|
|
|
|
|
|
corpId,
|
|
|
|
|
|
memberList: unknown,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
false
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!res?.success) return;
|
|
|
|
|
|
|
|
|
|
|
|
const rows = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : [];
|
|
|
|
|
|
if (!rows.length) return;
|
|
|
|
|
|
|
|
|
|
|
|
const patch = rows.reduce((acc, m) => {
|
|
|
|
|
|
const id = String(m?.userid || m?.userId || m?.corpUserId || '').trim();
|
|
|
|
|
|
if (!id) return acc;
|
|
|
|
|
|
const existing = userNameMap.value?.[id];
|
|
|
|
|
|
if (existing && existing !== id) return acc;
|
|
|
|
|
|
const display = String(m?.anotherName || m?.name || '').trim();
|
|
|
|
|
|
if (!display || display === id) return acc;
|
|
|
|
|
|
acc[id] = display;
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
|
|
|
|
if (Object.keys(patch).length) userNameMap.value = { ...(userNameMap.value || {}), ...patch };
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
})().finally(() => {
|
|
|
|
|
|
corpMemberBatchInflight = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return corpMemberBatchInflight;
|
2026-01-27 16:46:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 17:15:09 +08:00
|
|
|
|
const loadedCorpId = ref('');
|
2026-02-09 15:40:03 +08:00
|
|
|
|
let loadVisitTemplatesPromise = null;
|
|
|
|
|
|
let loadVisitTemplatesCorpId = '';
|
2026-02-06 17:15:09 +08:00
|
|
|
|
async function loadVisitTemplates() {
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
if (!corpId) return;
|
|
|
|
|
|
if (loadedCorpId.value === corpId && templates.value.length) return;
|
2026-02-09 15:40:03 +08:00
|
|
|
|
if (loadVisitTemplatesPromise && loadVisitTemplatesCorpId === corpId) return loadVisitTemplatesPromise;
|
2026-02-06 17:15:09 +08:00
|
|
|
|
|
2026-02-09 15:40:03 +08:00
|
|
|
|
loadVisitTemplatesCorpId = corpId;
|
|
|
|
|
|
loadVisitTemplatesPromise = (async () => {
|
2026-02-06 17:15:09 +08:00
|
|
|
|
const groupRes = await api('getTemplateGroup', { corpId, parentType: 'medicalRecord' });
|
|
|
|
|
|
const group = groupRes?.data && Array.isArray(groupRes.data?.data) ? groupRes.data.data : Array.isArray(groupRes?.data) ? groupRes.data : [];
|
|
|
|
|
|
const list = Array.isArray(group) ? group : [];
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const groupNameMap = list.reduce((m, i) => {
|
|
|
|
|
|
const t = i?.templateType ? String(i.templateType) : '';
|
|
|
|
|
|
const name = i?.name ? String(i.name) : '';
|
|
|
|
|
|
if (t && name) m[t] = name;
|
|
|
|
|
|
return m;
|
|
|
|
|
|
}, {});
|
2026-02-06 17:15:09 +08:00
|
|
|
|
const enabled = list.filter((i) => i && i.templateType !== 'healthTemplate' && i.templateStatus !== 'disable');
|
|
|
|
|
|
const typeList = enabled.map((i) => String(i.templateType || '')).filter(Boolean);
|
|
|
|
|
|
if (!typeList.length) return;
|
|
|
|
|
|
|
|
|
|
|
|
const detailRes = await api('getTemplateListByTemptype', { corpId, templateTypeList: typeList });
|
|
|
|
|
|
const detail = detailRes?.data && Array.isArray(detailRes.data?.data) ? detailRes.data.data : Array.isArray(detailRes?.data) ? detailRes.data : [];
|
|
|
|
|
|
const temps = Array.isArray(detail) ? detail : [];
|
|
|
|
|
|
const byType = temps.reduce((m, t) => {
|
|
|
|
|
|
const k = t?.templateType ? String(t.templateType) : '';
|
|
|
|
|
|
if (k) m[k] = t;
|
|
|
|
|
|
return m;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
const ordered = typeList.map((t) => byType[String(t)]).filter(Boolean);
|
|
|
|
|
|
|
|
|
|
|
|
const next = ordered
|
|
|
|
|
|
.map((t) => {
|
|
|
|
|
|
const temp = normalizeTemplate(t);
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const rawType = String(temp?.templateType || '');
|
|
|
|
|
|
const name = String(groupNameMap[rawType] || temp?.name || temp?.templateName || temp?.templateTypeName || '') || rawType;
|
2026-02-06 17:15:09 +08:00
|
|
|
|
return {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
templateType: rawType,
|
2026-02-06 17:15:09 +08:00
|
|
|
|
name,
|
|
|
|
|
|
service: temp?.service || {},
|
|
|
|
|
|
templateList: Array.isArray(temp?.templateList) ? temp.templateList : [],
|
|
|
|
|
|
};
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter((i) => i && i.templateType);
|
|
|
|
|
|
|
2026-02-09 15:40:03 +08:00
|
|
|
|
if (next.length) {
|
|
|
|
|
|
templates.value = next;
|
|
|
|
|
|
loadedCorpId.value = corpId;
|
|
|
|
|
|
}
|
|
|
|
|
|
})()
|
|
|
|
|
|
.catch((e) => {
|
|
|
|
|
|
console.error('loadVisitTemplates error:', e);
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
if (loadVisitTemplatesCorpId === corpId) {
|
|
|
|
|
|
loadVisitTemplatesPromise = null;
|
|
|
|
|
|
loadVisitTemplatesCorpId = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return loadVisitTemplatesPromise;
|
2026-02-06 17:15:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 16:46:36 +08:00
|
|
|
|
const userNameMap = ref({});
|
|
|
|
|
|
const loadedTeamId = ref('');
|
|
|
|
|
|
function resolveUserName(userId) {
|
|
|
|
|
|
const id = String(userId || '');
|
|
|
|
|
|
if (!id) return '';
|
|
|
|
|
|
const map = userNameMap.value || {};
|
|
|
|
|
|
return String(map[id] || '') || id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadTeamMembers() {
|
|
|
|
|
|
const team = uni.getStorageSync('ykt_case_current_team') || {};
|
|
|
|
|
|
const teamId = team?.teamId ? String(team.teamId) : '';
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
if (!teamId || !corpId) return;
|
|
|
|
|
|
if (loadedTeamId.value === teamId && Object.keys(userNameMap.value || {}).length > 0) return;
|
|
|
|
|
|
loadedTeamId.value = teamId;
|
|
|
|
|
|
|
|
|
|
|
|
const res = await api('getTeamData', { corpId, teamId });
|
|
|
|
|
|
if (!res?.success) return;
|
|
|
|
|
|
const t = res?.data && typeof res.data === 'object' ? res.data : {};
|
|
|
|
|
|
const members = Array.isArray(t.memberList) ? t.memberList : [];
|
|
|
|
|
|
userNameMap.value = members.reduce((acc, m) => {
|
|
|
|
|
|
const uid = String(m?.userid || '');
|
|
|
|
|
|
if (!uid) return acc;
|
|
|
|
|
|
acc[uid] = String(m?.anotherName || m?.name || m?.userid || '') || uid;
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getSortTimeTitle(templateType) {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const rawType = String(templateType || '');
|
2026-02-10 15:47:35 +08:00
|
|
|
|
const ui = normalizeMedicalType(rawType);
|
|
|
|
|
|
// 预问诊记录:列表时间固定使用就诊日期字段,避免被模板配置的 timeTitle 覆盖
|
|
|
|
|
|
if (ui === 'preConsultationRecord') return 'consultationDate';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const t = templateMap.value[rawType] || {};
|
2026-02-06 17:15:09 +08:00
|
|
|
|
if (t?.service?.timeTitle) return String(t.service.timeTitle);
|
2026-02-09 15:40:03 +08:00
|
|
|
|
if (ui === 'outpatient') return 'visitTime';
|
|
|
|
|
|
if (ui === 'inhospital') return 'inhosDate';
|
|
|
|
|
|
if (ui === 'physicalExaminationTemplate') return 'inspectDate';
|
2026-01-27 16:46:36 +08:00
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function normalizeText(v) {
|
|
|
|
|
|
if (Array.isArray(v)) return v.filter((i) => i !== null && i !== undefined && String(i).trim()).join(',');
|
|
|
|
|
|
if (v === 0) return '0';
|
|
|
|
|
|
return v ? String(v) : '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatPositiveFind(v, { withOpinion = false } = {}) {
|
|
|
|
|
|
if (Array.isArray(v)) {
|
|
|
|
|
|
const list = v
|
|
|
|
|
|
.map((i) => (i && typeof i === 'object' ? { category: i.category, opinion: i.opinion } : null))
|
|
|
|
|
|
.filter((i) => i && (i.category || i.opinion));
|
|
|
|
|
|
if (!list.length) return '';
|
|
|
|
|
|
if (!withOpinion) return list.map((i) => String(i.category || '').trim()).filter(Boolean).join(':');
|
|
|
|
|
|
return list
|
|
|
|
|
|
.map((i) => {
|
|
|
|
|
|
const c = String(i.category || '').trim();
|
|
|
|
|
|
const o = String(i.opinion || '').trim();
|
|
|
|
|
|
if (c && o) return `${c}:${o}`;
|
|
|
|
|
|
return c || o;
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.join(';');
|
|
|
|
|
|
}
|
|
|
|
|
|
return normalizeText(v);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getTemplateName(type) {
|
2026-02-06 17:15:09 +08:00
|
|
|
|
const t = templateMap.value[String(type || '')];
|
|
|
|
|
|
return t?.name ? String(t.name) : '';
|
2026-01-27 16:46:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function toDateStr(sortTime) {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
return formatAnyDate(sortTime, 'YYYY-MM-DD');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function normalizeMedicalType(raw) {
|
|
|
|
|
|
const s = String(raw || '').trim();
|
|
|
|
|
|
if (!s) return '';
|
|
|
|
|
|
const lower = s.toLowerCase();
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultationRecord';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient';
|
|
|
|
|
|
if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital';
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (lower === 'preconsultation' || lower === 'pre_consultation' || lower === 'pre-consultation') return 'preConsultationRecord';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
|
|
|
|
|
|
if (s === 'outPatient') return 'outpatient';
|
|
|
|
|
|
if (s === 'inHospital') return 'inhospital';
|
2026-02-12 15:54:50 +08:00
|
|
|
|
if (s === 'preConsultationRecord') return 'preConsultationRecord';
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (s === 'preConsultationRecord') return 'preConsultationRecord';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
|
|
|
|
|
|
return s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function parseAnyTimeMs(v) {
|
|
|
|
|
|
if (v === null || v === undefined) return 0;
|
|
|
|
|
|
if (typeof v === 'number') {
|
|
|
|
|
|
// 10位秒级时间戳
|
|
|
|
|
|
if (v > 1e9 && v < 1e12) return v * 1000;
|
|
|
|
|
|
return v;
|
|
|
|
|
|
}
|
|
|
|
|
|
const s = String(v).trim();
|
|
|
|
|
|
if (!s) return 0;
|
|
|
|
|
|
if (/^\d{10,13}$/.test(s)) return Number(s.length === 10 ? `${s}000` : s);
|
|
|
|
|
|
const d = dayjs(s);
|
|
|
|
|
|
return d.isValid() ? d.valueOf() : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatAnyDate(v, fmt = 'YYYY-MM-DD') {
|
|
|
|
|
|
const ms = parseAnyTimeMs(v);
|
|
|
|
|
|
if (!ms) return '';
|
|
|
|
|
|
const d = dayjs(ms);
|
|
|
|
|
|
return d.isValid() ? d.format(fmt) : '';
|
2026-01-27 16:46:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function toDateTimeStr(ts) {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
return formatAnyDate(ts, 'YYYY-MM-DD HH:mm');
|
2026-01-27 16:46:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function refreshList() {
|
2026-01-22 15:54:15 +08:00
|
|
|
|
if (!props.archiveId) return;
|
2026-01-27 16:46:36 +08:00
|
|
|
|
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
if (!corpId) return;
|
2026-02-06 17:15:09 +08:00
|
|
|
|
await loadVisitTemplates();
|
2026-01-27 16:46:36 +08:00
|
|
|
|
loadTeamMembers();
|
|
|
|
|
|
|
|
|
|
|
|
loading('加载中...');
|
|
|
|
|
|
try {
|
|
|
|
|
|
const params = { memberId: props.archiveId, corpId };
|
|
|
|
|
|
|
|
|
|
|
|
// 添加类型筛选
|
|
|
|
|
|
params.medicalType =
|
2026-02-06 17:15:09 +08:00
|
|
|
|
currentType.value.value === 'ALL' ? availableTypes.value : currentType.value.value;
|
2026-01-27 16:46:36 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加时间筛选
|
2026-02-02 14:51:40 +08:00
|
|
|
|
if (Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]) {
|
|
|
|
|
|
params.startTime = dayjs(dateRange.value[0]).startOf('day').valueOf();
|
|
|
|
|
|
params.endTime = dayjs(dateRange.value[1]).endOf('day').valueOf();
|
2026-01-27 16:46:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const res = await api('getCustomerMedicalRecord', params);
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
|
|
|
|
|
|
// 接口返回结构兼容:
|
|
|
|
|
|
// - { success, message, list: [] }
|
|
|
|
|
|
// - { success, message, data: { list: [] } }
|
|
|
|
|
|
const list = Array.isArray(res?.list)
|
|
|
|
|
|
? res.list
|
|
|
|
|
|
: Array.isArray(res?.data?.list)
|
|
|
|
|
|
? res.data.list
|
2026-02-09 15:40:03 +08:00
|
|
|
|
: Array.isArray(res?.data?.data)
|
|
|
|
|
|
? res.data.data
|
|
|
|
|
|
: Array.isArray(res?.data?.data?.list)
|
|
|
|
|
|
? res.data.data.list
|
2026-01-27 16:46:36 +08:00
|
|
|
|
: Array.isArray(res?.data)
|
|
|
|
|
|
? res.data
|
|
|
|
|
|
: [];
|
|
|
|
|
|
if (list.length) {
|
|
|
|
|
|
const mapped = list.map((r) => {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const rawType = String(r?.medicalType || r?.templateType || '') || '';
|
|
|
|
|
|
const uiType = normalizeMedicalType(rawType);
|
|
|
|
|
|
const normalized = normalizeVisitRecordFormData(uiType, r);
|
|
|
|
|
|
const timeTitle = getSortTimeTitle(rawType);
|
|
|
|
|
|
const rawTime = timeTitle ? (normalized?.[timeTitle] ?? r?.[timeTitle]) : '';
|
|
|
|
|
|
const dateStr = rawTime ? formatAnyDate(rawTime, 'YYYY-MM-DD') : '';
|
2026-01-27 16:46:36 +08:00
|
|
|
|
return {
|
|
|
|
|
|
...r,
|
2026-02-09 15:40:03 +08:00
|
|
|
|
...normalized,
|
|
|
|
|
|
medicalType: rawType,
|
|
|
|
|
|
templateType: uiType,
|
|
|
|
|
|
rawTemplateType: rawType,
|
2026-01-27 16:46:36 +08:00
|
|
|
|
dateStr: dateStr || toDateStr(r?.sortTime),
|
2026-02-09 15:40:03 +08:00
|
|
|
|
createDateStr: r?.createTime ? formatAnyDate(r.createTime, 'YYYY-MM-DD') : '',
|
2026-01-27 16:46:36 +08:00
|
|
|
|
createTimeStr: toDateTimeStr(r?.createTime),
|
2026-02-09 15:40:03 +08:00
|
|
|
|
tempName: r?.tempName || getTemplateName(rawType) || '病历',
|
2026-01-27 16:46:36 +08:00
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 未互通时,仅展示本团队数据
|
|
|
|
|
|
records.value = !shareAllTeamsForQuery.value && teamId.value
|
|
|
|
|
|
? mapped.filter((i) => String(i?.teamId || '') === String(teamId.value))
|
|
|
|
|
|
: mapped;
|
2026-02-11 17:11:46 +08:00
|
|
|
|
|
|
|
|
|
|
const creatorIds = mapped
|
|
|
|
|
|
.map(
|
|
|
|
|
|
(i) =>
|
|
|
|
|
|
i.creator ||
|
|
|
|
|
|
i.creatorUserId ||
|
|
|
|
|
|
i.createUserId ||
|
|
|
|
|
|
i.executor ||
|
|
|
|
|
|
i.executorUserId
|
|
|
|
|
|
)
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
void batchLoadCorpMembers(creatorIds);
|
2026-01-27 16:46:36 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
records.value = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
console.error('获取病历记录失败:', error);
|
|
|
|
|
|
records.value = [];
|
|
|
|
|
|
}
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const tagClass = {
|
|
|
|
|
|
outpatient: 'bg-amber',
|
|
|
|
|
|
inhospital: 'bg-teal',
|
2026-02-10 15:47:35 +08:00
|
|
|
|
preConsultationRecord: 'bg-indigo',
|
2026-01-22 15:54:15 +08:00
|
|
|
|
physicalExaminationTemplate: 'bg-green',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-09 15:40:03 +08:00
|
|
|
|
function resolveRecordType(r) {
|
|
|
|
|
|
if (!r) return '';
|
|
|
|
|
|
const direct = normalizeMedicalType(r.uiType || r.templateType || r.rawTemplateType || r.medicalType || '');
|
|
|
|
|
|
if (direct) return direct;
|
|
|
|
|
|
// fallback by known fields
|
|
|
|
|
|
if (r.inspectDate || r.positiveFind || r.inspectSummary) return 'physicalExaminationTemplate';
|
|
|
|
|
|
if (r.inhosDate || r.surgeryName || r.surgeryDate || r.operationDate) return 'inhospital';
|
|
|
|
|
|
if (r.visitTime || r.disposePlan || r.treatmentPlan) return 'outpatient';
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (r.consultationDate || r.consultDate || r.presentIllness || r.presentIllnessHistory || r.pastHistory) return 'preConsultationRecord';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 17:39:23 +08:00
|
|
|
|
function getDiagnosis(r) {
|
|
|
|
|
|
if (!r) return '--';
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const t = resolveRecordType(r);
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (t === 'preConsultationRecord') return normalizeText(r.chiefComplaint) || normalizeText(r.summary) || '--';
|
2026-01-27 16:46:36 +08:00
|
|
|
|
if (t === 'physicalExaminationTemplate') return formatPositiveFind(r.positiveFind) || normalizeText(r.summary) || '--';
|
|
|
|
|
|
if (t === 'outpatient' || t === 'inhospital') return normalizeText(r.diagnosisName || r.diagnosis) || normalizeText(r.summary) || '--';
|
|
|
|
|
|
return normalizeText(r.diagnosisName || r.diagnosis || r.summary) || '--';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function firstLine(v) {
|
|
|
|
|
|
const s = normalizeText(v);
|
|
|
|
|
|
return s || '--';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getDisplayLines(r) {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
const t = resolveRecordType(r);
|
2026-01-27 16:46:36 +08:00
|
|
|
|
if (t === 'outpatient') {
|
|
|
|
|
|
return [{ label: '门诊诊断:', value: firstLine(r.diagnosisName || r.diagnosis) }];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (t === 'inhospital') {
|
|
|
|
|
|
const lines = [{ label: '入院诊断:', value: firstLine(r.diagnosisName || r.diagnosis) }];
|
|
|
|
|
|
const surgery = normalizeText(r.surgeryName);
|
|
|
|
|
|
if (surgery) lines.push({ label: '手术名称:', value: surgery });
|
|
|
|
|
|
return lines;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (t === 'physicalExaminationTemplate') {
|
2026-02-09 15:40:03 +08:00
|
|
|
|
return [{ label: '体检小结:', value: firstLine(r.summary || r.inspectSummary) }];
|
2026-01-27 16:46:36 +08:00
|
|
|
|
}
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (t === 'preConsultationRecord') {
|
2026-01-27 16:46:36 +08:00
|
|
|
|
const lines = [
|
|
|
|
|
|
{ label: '主诉:', value: firstLine(r.chiefComplaint) },
|
2026-02-09 15:40:03 +08:00
|
|
|
|
{ label: '现病史:', value: firstLine(r.presentIllness || r.presentIllnessHistory) },
|
2026-01-27 16:46:36 +08:00
|
|
|
|
];
|
|
|
|
|
|
const past = normalizeText(r.pastHistory);
|
|
|
|
|
|
if (past) lines.push({ label: '既往史:', value: past });
|
|
|
|
|
|
return lines;
|
|
|
|
|
|
}
|
|
|
|
|
|
return [{ label: '摘要:', value: firstLine(r.summary) }];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getCreateFooter(r) {
|
|
|
|
|
|
const time = r?.createTimeStr || r?.createDateStr || '';
|
|
|
|
|
|
const byCustomer = r?.ignore === 'checkIn';
|
|
|
|
|
|
if (byCustomer) return time ? `${time} 患者自建` : '患者自建';
|
|
|
|
|
|
|
2026-02-11 17:11:46 +08:00
|
|
|
|
const creatorId = String(r?.creator || r?.creatorUserId || r?.createUserId || r?.executor || r?.executorUserId || '');
|
2026-01-27 16:46:36 +08:00
|
|
|
|
if (!creatorId) return time ? `创建时间:${time}` : '';
|
|
|
|
|
|
const name = resolveUserName(creatorId);
|
|
|
|
|
|
return time ? `${time} ${name}代建` : `${name}代建`;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function pickType(e) {
|
|
|
|
|
|
currentType.value = typeRange.value[e.detail.value] || { name: '全部', value: 'ALL' };
|
|
|
|
|
|
refreshList();
|
|
|
|
|
|
}
|
2026-02-02 14:51:40 +08:00
|
|
|
|
function pickTimeRange(val) {
|
2026-02-10 15:47:35 +08:00
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
|
|
dateRange.value = val;
|
|
|
|
|
|
} else if (val && typeof val === 'object' && Array.isArray(val.value)) {
|
|
|
|
|
|
dateRange.value = val.value;
|
|
|
|
|
|
} else if (val && typeof val === 'object' && val.detail && Array.isArray(val.detail.value)) {
|
|
|
|
|
|
dateRange.value = val.detail.value;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dateRange.value = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
refreshList();
|
|
|
|
|
|
}
|
|
|
|
|
|
function clearTimeRange() {
|
|
|
|
|
|
dateRange.value = [];
|
2026-01-22 15:54:15 +08:00
|
|
|
|
refreshList();
|
|
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
|
|
|
|
|
function getFiles(r) {
|
|
|
|
|
|
const arr = r?.files;
|
2026-02-09 15:40:03 +08:00
|
|
|
|
return Array.isArray(arr)
|
|
|
|
|
|
? arr
|
|
|
|
|
|
.filter((i) => i && i.url)
|
|
|
|
|
|
.map((i) => ({ ...i, url: normalizeFileUrl(i.url) }))
|
|
|
|
|
|
: [];
|
2026-01-22 17:39:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function previewFiles(r, idx) {
|
|
|
|
|
|
const urls = getFiles(r).map((i) => i.url);
|
|
|
|
|
|
if (!urls.length) return;
|
|
|
|
|
|
uni.previewImage({ urls, current: urls[idx] });
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 18:04:34 +08:00
|
|
|
|
function goAdd(t) {
|
|
|
|
|
|
if (!t?.templateType) return;
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/case/visit-record-detail?archiveId=${encodeURIComponent(props.archiveId)}&type=${encodeURIComponent(t.templateType)}&name=${encodeURIComponent(props.data?.name || '')}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 17:15:09 +08:00
|
|
|
|
function pickAddType(e) {
|
|
|
|
|
|
if (!props.archiveId) return toast('缺少档案信息');
|
|
|
|
|
|
const idx = Number(e?.detail?.value ?? -1);
|
|
|
|
|
|
const t = selectableTemplates.value[idx];
|
2026-02-06 18:04:34 +08:00
|
|
|
|
if (!t) return;
|
|
|
|
|
|
goAdd(t);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showAddActionSheet() {
|
|
|
|
|
|
if (!props.archiveId) return toast('缺少档案信息');
|
|
|
|
|
|
const list = selectableTemplates.value;
|
|
|
|
|
|
if (!list.length) return toast('暂无可用病历模板');
|
|
|
|
|
|
uni.showActionSheet({
|
|
|
|
|
|
itemList: list.map((i) => i.name),
|
|
|
|
|
|
success: ({ tapIndex }) => {
|
|
|
|
|
|
const t = list[tapIndex];
|
|
|
|
|
|
if (!t) return;
|
|
|
|
|
|
goAdd(t);
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (e) => {
|
|
|
|
|
|
// 用户取消无需提示;其他错误忽略
|
|
|
|
|
|
const errMsg = String(e?.errMsg || '');
|
|
|
|
|
|
if (errMsg && errMsg.includes('cancel')) return;
|
|
|
|
|
|
if (errMsg) console.warn('[health-profile-tab] showActionSheet fail:', errMsg);
|
|
|
|
|
|
},
|
2026-01-22 15:54:15 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 18:04:34 +08:00
|
|
|
|
async function onFabTap() {
|
|
|
|
|
|
if (!props.archiveId) return toast('缺少档案信息');
|
|
|
|
|
|
|
|
|
|
|
|
// 模板可能尚未加载:先拉一次
|
|
|
|
|
|
if (!templates.value.length) {
|
|
|
|
|
|
loading('加载模板...');
|
|
|
|
|
|
try {
|
|
|
|
|
|
await loadVisitTemplates();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <=6:用 actionSheet;>6:由 picker 自己弹(这里不做额外处理)
|
|
|
|
|
|
if (useActionSheet.value) showAddActionSheet();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 15:54:15 +08:00
|
|
|
|
function edit(record) {
|
2026-01-27 16:46:36 +08:00
|
|
|
|
const type = String(record?.medicalType || record?.templateType || '') || '';
|
2026-01-22 15:54:15 +08:00
|
|
|
|
uni.navigateTo({
|
2026-01-27 16:46:36 +08:00
|
|
|
|
url: `/pages/case/visit-record-view?archiveId=${encodeURIComponent(props.archiveId)}&id=${encodeURIComponent(record._id)}&type=${encodeURIComponent(type)}`,
|
2026-01-22 15:54:15 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
uni.$on('archive-detail:visit-record-changed', refreshList);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
uni.$off('archive-detail:visit-record-changed', refreshList);
|
|
|
|
|
|
});
|
2026-01-27 16:46:36 +08:00
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => props.archiveId,
|
|
|
|
|
|
(v, old) => {
|
|
|
|
|
|
const next = v ? String(v) : '';
|
|
|
|
|
|
const prev = old ? String(old) : '';
|
|
|
|
|
|
if (next && next !== prev) refreshList();
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
);
|
2026-01-22 15:54:15 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.wrap {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 24rpx 0 192rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.filters {
|
|
|
|
|
|
display: flex;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
padding: 20rpx 28rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
background: #f5f6f8;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
border-bottom: 2rpx solid #f2f2f2;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
2026-02-11 15:24:39 +08:00
|
|
|
|
|
2026-01-22 15:54:15 +08:00
|
|
|
|
.filter-pill {
|
|
|
|
|
|
background: #fff;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
border: 2rpx solid #e6e6e6;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
padding: 20rpx 24rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
gap: 20rpx;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
flex: 1;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.pill-text {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 26rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
color: #333;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
max-width: 360rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
2026-02-10 15:47:35 +08:00
|
|
|
|
.pill-icons {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.pill-clear {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
|
|
|
|
|
.share-tip {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 20rpx 28rpx 0;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
.share-tip-text {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
background: #eef2ff;
|
|
|
|
|
|
color: #4338ca;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
padding: 12rpx 20rpx;
|
|
|
|
|
|
border-radius: 12rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.list {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 0 28rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
2026-02-11 15:24:39 +08:00
|
|
|
|
|
2026-01-22 15:54:15 +08:00
|
|
|
|
.card {
|
|
|
|
|
|
background: #fff;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
margin-top: 20rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
overflow: hidden;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
box-shadow: 0 12rpx 28rpx rgba(0, 0, 0, 0.06);
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.record {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 24rpx 24rpx 20rpx;
|
|
|
|
|
|
gap: 16rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.record-title {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 30rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #1f1f1f;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-date {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 28rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
.record-body {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 0 24rpx 24rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
.line {
|
2026-01-22 15:54:15 +08:00
|
|
|
|
display: flex;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding-top: 20rpx;
|
|
|
|
|
|
font-size: 26rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
color: #333;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
line-height: 36rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
.line-label {
|
2026-01-22 15:54:15 +08:00
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
.line-value {
|
2026-01-22 15:54:15 +08:00
|
|
|
|
flex: 1;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
min-width: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
2026-01-27 16:46:36 +08:00
|
|
|
|
white-space: nowrap;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.thumbs {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding-top: 20rpx;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
display: flex;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
gap: 20rpx;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.thumb {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
width: 168rpx;
|
|
|
|
|
|
height: 128rpx;
|
|
|
|
|
|
border-radius: 12rpx;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
background: #f3f4f6;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
border: 2rpx solid #e5e7eb;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
.thumb-img {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
width: 168rpx;
|
|
|
|
|
|
height: 128rpx;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
.thumb-more {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 24rpx;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
color: #6b7280;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.record-foot {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 24rpx 24rpx;
|
|
|
|
|
|
border-top: 2rpx solid #f2f2f2;
|
|
|
|
|
|
font-size: 24rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
|
|
|
|
|
.foot-left {
|
|
|
|
|
|
flex-shrink: 0;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
margin-right: 20rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.record-tag {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 24rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
color: #fff;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 8rpx 16rpx;
|
|
|
|
|
|
border-radius: 16rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.bg-blue {
|
2026-02-02 15:15:51 +08:00
|
|
|
|
background: #0877F1;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
.bg-amber {
|
|
|
|
|
|
background: #d97706;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bg-teal {
|
|
|
|
|
|
background: #0f766e;
|
|
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
.bg-indigo {
|
|
|
|
|
|
background: #4f46e5;
|
|
|
|
|
|
}
|
2026-01-22 15:54:15 +08:00
|
|
|
|
.bg-green {
|
|
|
|
|
|
background: #16a34a;
|
|
|
|
|
|
}
|
|
|
|
|
|
.bg-rose {
|
|
|
|
|
|
background: #f43f5e;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
padding: 240rpx 0;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
text-align: center;
|
|
|
|
|
|
color: #9aa0a6;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
font-size: 26rpx;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 17:15:09 +08:00
|
|
|
|
.fab-picker {
|
2026-01-22 15:54:15 +08:00
|
|
|
|
position: fixed;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
right: 32rpx;
|
2026-02-06 17:15:09 +08:00
|
|
|
|
width: 104rpx;
|
|
|
|
|
|
height: 104rpx;
|
|
|
|
|
|
z-index: 20;
|
|
|
|
|
|
}
|
|
|
|
|
|
.fab {
|
2026-01-28 20:01:28 +08:00
|
|
|
|
width: 104rpx;
|
|
|
|
|
|
height: 104rpx;
|
|
|
|
|
|
border-radius: 52rpx;
|
2026-02-02 15:15:51 +08:00
|
|
|
|
background: #0877F1;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-01-28 20:01:28 +08:00
|
|
|
|
box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35);
|
2026-02-06 17:15:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
.fab--disabled {
|
|
|
|
|
|
opacity: 0.5;
|
2026-01-22 15:54:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|