| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365 |
- <script setup lang="ts">
- import { useRoute } from 'vue-router';
- import { ref, computed, nextTick, h, watch, onMounted } from 'vue';
- import { MinusCircleOutlined, EyeOutlined, EditOutlined } from '@ant-design/icons-vue';
- import { notification } from 'ant-design-vue';
- import { message } from 'ant-design-vue';
- import type { OpenConditioningSchemeModel, SystemCwModel } from '@/model/care.model';
- import PatientTagWidget from '@/widgets/PatientTagWidget.vue';
- import AcupointEdit from '@/service/AcupointEdit.vue';
- import ServiceDetail from '@/service/ServiceDetail.vue';
- import {
- getCpContentListMethod,
- getPatientListMethod,
- getConditioningRecordDetailMethod,
- getPatientConditioningRecordMethod,
- addConditioningSchemeMethod,
- getCpDetailMethod,
- getProvinceMethod,
- getCityMethod,
- getAreaMethod,
- getAvailableCwMethod,
- } from '@/request/api/care.api';
- import { patientMethod, getPatientTagsMethod } from '@/request/api/patient.api';
- import { useRequest, usePagination } from 'alova/client';
- import ServicePackageDetail from '@/service/ServicePackageDetail.vue';
- import { VxeUI } from 'vxe-pc-ui';
- import type { HealthReportVO } from '@/model/health-report.model';
- import type { PatientTagVO } from '@/model/patient.model';
- import dayjs from 'dayjs';
- import { getPatientHealthRecordsMethod } from '@/request/api/report.api';
- type FollowModel = Partial<OpenConditioningSchemeModel>;
- // const props = defineProps<{ data: FollowModel }>();
- const form = reactive<FollowModel>({
- id: 0,
- patientId: '',
- patientName: '',
- patientSex: '',
- patientAge: 0,
- diagnosis: '',
- symptom: '',
- conditioningWrapId: '',
- conditioningWrapName: '',
- estimatedStartDate: '',
- estimatedEndDate: '',
- isDelivery: null,
- cost: 0,
- healthAnalysisReport: {
- willillStateName: '',
- willillDegreeName: '',
- willillSocialName: '',
- willillFunctionName: '',
- },
- patientMedicalRecord: {
- patientId: '', // 患者ID
- institutionId: '', // 机构ID
- institutionName: '', // 机构名称
- diagnosis: '', // 疾病
- symptom: '', // 症型
- syndrome: '', // 证状
- },
- items: [],
- provinceName: '',
- provinceCode: '',
- cityName: '',
- cityCode: '',
- // districtName: '',
- // districtCode: '',
- areaName: '',
- areaCode: '',
- detailAddress: '',
- phone: '',
- progress: '0',
- photo: '',
- });
- const searchName = ref('');
- const selectedPackage = ref('');
- const currentSelectedPackage = ref<any>(null);
- const deliveryChecked = ref(true);
- const address = ref({
- province: '',
- city: '',
- district: '',
- detail: '',
- phone: '',
- });
- // 机构调理包
- const lifeCwData = ref([]);
- // 获取可用的服务包
- const suggestCwData = ref<string[]>([]);
- async function loadAvailableCw(patientId: string,patientConditioningRecordId:string) {
- const res: any = await getAvailableCwMethod(patientId,patientConditioningRecordId);
- if (res.length > 0) {
- lifeCwData.value = res;
- // 筛选出可推荐的
- suggestCwData.value = lifeCwData.value.filter((item: any) => item.isSuggest==='Y');
- }
- }
- export interface PatientModel {
- id: string;
- patientId: string;
- patientName: string;
- patientSex: string;
- patientAge: number;
- diagnosis: string;
- symptom: string;
- healthAnalysisReportId: string;
- healthAnalysisReport: {
- willillStateName: string;
- willillDegreeName: string;
- willillSocialName: string;
- willillFunctionName: string;
- };
- constitutionGroupName: string;
- status?: string;
- }
- // 患者列表
- const patients = ref([]);
- // 患者详细信息
- interface CpDetail {
- womenSpecialPeriod?: string;
- cardno?: string;
- }
- let cpDetail = ref<CpDetail & Partial<PatientModel>>({});
- async function getCpDetail(id: string) {
- await patientMethod(id).then((res2) => {
- cpDetail.value = res2;
- });
- }
- interface PatientRecord {
- id: string;
- estimatedStartDate: string;
- estimatedEndDate: string;
- }
- // 患者健康分析报告记录
- const patientHealthRecords = ref<HealthReportVO[]>([]);
- async function loadHealthRecords(patientId?: string) {
- try {
- const { data } = await getPatientHealthRecordsMethod(1, 100, { patientId: patientId ?? currentPatient.value?.patientId! });
- patientHealthRecords.value = data;
- } catch (err) {
- patientHealthRecords.value = [];
- }
- }
- const patientRecord = ref<PatientRecord[]>([]);
- // 获取患者调养记录
- function getPatientRecord(id: any) {
- patientRecord.value = [];
- getPatientConditioningRecordMethod(id).then((res) => {
- if (res && res.length > 0) {
- patientRecord.value = res;
- }
- });
- }
- // const report=ref({})
- async function getCpRecordDetail(id: string) {
- await getCpDetailMethod({ id }).then((res) => {
- formData.items = res?.items ?? [];
- // form = res;
- form.conditioningWrapName = res?.conditioningWrapName;
- form.estimatedStartDate = res?.estimatedStartDate;
- form.estimatedEndDate = res?.estimatedEndDate;
- form.provinceName = res?.provinceName;
- form.cityName = res?.cityName;
- form.areaName = res?.areaName;
- form.detailAddress = res?.detailAddress;
- form.phone = res?.phone;
- form.healthAnalysisReport = res.healthAnalysisReport;
- selectedProvince.value=res.provinceCode;
- selectedCity.value=res.cityCode;
- selectedArea.value=res.areaCode
- });
- }
- function getPatientList(id: string) {
- if (id) {
- getCpDetail(id);
- loadTags(id);
- loadHealthRecords(id);
- getPatientRecord(id);
- }
- }
- const route = useRoute();
- onMounted(async () => {
- const id = Number(route.query.id); // 获取查询参数 id
- // 获取患者列表
- const res: any = await getPatientListMethod();
- if (res && res.length > 0) {
- patients.value = res;
- if (id) {
- let index = res.findIndex((item: any) => item.patientId === id);
- currentPatient.value = res[index];
- } else {
- currentPatient.value = res[0];
- }
-
- }
- if (currentPatient.value?.patientId) {
- // 获取患者列表
- await getPatientList(currentPatient.value?.patientId || '');
- // 获取服务包选择列表
- await loadAvailableCw(currentPatient.value?.patientId || '',currentPatient.value?.id || '');
- }
- if (currentPatient.value?.id) {
- // 获取调养记录
- await getCpRecordDetail(currentPatient.value?.id || '');
- }
- // 获取省份
- await loadProvinces();
- });
- // 患者标签
- const patientTags = ref<PatientTagVO[]>([]);
- function loadTags(patientId: string) {
- getPatientTagsMethod(patientId).then((res) => {
- patientTags.value = res;
- });
- }
- // 展示的患者数据
- const filteredPatients: any = computed(() => {
- if(patients.value.length > 0){
- return patients.value.filter((p: any) => (p.patientName || '').includes(searchName.value));
- }
- return [];
- });
- // 默认显示第一个患者
- const currentPatient = ref<PatientModel>();
- // 点击切换患者
- async function selectPatient(item: any) {
- currentPatient.value = item;
- // 获取患者信息
- await getPatientList(item.patientId);
- await loadAvailableCw(item.patientId || '',item.id || '');
- // 获取患者调养记录
- await getCpRecordDetail(item.id);
- // 清空服务包选择
- selectedPackage.value = '';
- currentSelectedPackage.value = null;
- btnType.value=''
- }
- const btnType = ref('');
- // 打开调养记录
- function openRecord(item: any) {
- const status=currentPatient.value.status;
- const types = 'record';
- VxeUI.modal.open({
- id: 'servicePackageDetail-modal',
- title: '调养记录详情',
- height: window.innerHeight,
- width: window.innerWidth,
- escClosable: true,
- destroyOnClose: true,
- slots: {
- default() {
- return h(ServicePackageDetail, <any>{
- data: { ...item, types ,status},
- onSubmit(data: any) {
- selectedPackage.value=data.conditioningWrapId;
- formData.items = data?.items ?? [];
- form.conditioningWrapName = data.conditioningWrapName;
- form.conditioningWrapId = data.conditioningWrapId;
- },
- });
- },
- },
- });
- }
- const formData = reactive<FollowModel>({
- price: 0, //总计价格
- cwPatientMatchRules: [
- {
- diagnoseDiseaseNames: [],
- diagnoseSyndromeNames: [],
- constitutionGroupNames: [],
- diagnoseDisease: {
- id: '',
- code: '',
- name: '',
- optionalWords: '',
- attributes: [],
- children: [],
- },
- diagnoseSyndrome: {
- code: '',
- name: '',
- analysis: '',
- remark: '',
- },
- constitutionGroup: {
- id: '',
- code: '',
- name: '',
- definition: '',
- remark: '',
- },
- },
- ], // 适用情况
- items: [], // Initialize as empty array
- });
- const emptyRow = {
- id: '',
- conditioningWrapId: '',
- conditioningProgramId: 0,
- days: '',
- frequencyType: '',
- frequencyTypeing: [],
- frequencyMeasure: '',
- totalMeasure: '',
- totalPrice: '',
- initialDay: '',
- conditioningProgramDetail: {
- id: '',
- name: '',
- conditioningProgramType: '',
- pricingType: '',
- cpFixedPricingRule: {
- unitPrice: 0,
- pricingUnit: '',
- convertDose: 0,
- convertUnit: '',
- },
- cpDynamicPricingRule: [],
- cpMedicines: [],
- effect: '',
- isOffline: null,
- isDelivery: null,
- photo: '',
- institutionId: '',
- institutionName: '',
- remark: '',
- },
- cwcpAcuMeridians: [],
- cwcpAcuPoints: [],
- conditioningProgramSupplierName: '',
- };
- const projectSearch = ref('');
- const showProjectPopover = ref(false);
- const displayTableData = computed(() => {
- return [...(formData.items ?? []), { ...emptyRow }];
- });
- const isShowDelivery = ref<boolean>(false);
- // 监听 displayTableData 的变化
- watch(displayTableData, (newValue, oldValue) => {
- if (newValue.length > 0) {
- isShowDelivery.value = newValue.some((item) => {
- return item.conditioningProgramDetail?.isDelivery === 'Y';
- });
- newValue.forEach((row: any) => {
- row.frequencyTypeing = row.frequencyType ? [row.frequencyType] : [];
- });
- }
- });
- const totalPrice = computed(() => {
- return (formData.items ?? []).reduce((sum, row) => {
- const price = Number(row?.totalPrice) || 0;
- return sum + price;
- }, 0);
- });
- function onSelectProject({ row }: any) {
- if ((formData.items ?? []).some((item) => item.conditioningProgramDetail?.name === row.name)) {
- notification.warning({ message: '不能重复添加该项目' });
- return;
- }
- // 添加新行到主表格
- if (!formData.items) formData.items = [];
- formData.items.push({
- id: '',
- conditioningProgramId: row.id || '',
- // conditioningProgramId: 0,
- days: '',
- frequencyType: '',
- frequencyTypeing: [],
- frequencyMeasure: '',
- totalMeasure: '',
- totalPrice: '',
- initialDay: '',
- cwcpAcuMeridians: [],
- cwcpAcuPoints: [],
- conditioningProgramDetail: {
- ...row,
- id: row.id || '',
- name: row.name || '',
- conditioningProgramType: row.conditioningProgramType || '',
- effect: row.effect || '',
- pricingType: row.pricingType || '',
- cpFixedPricingRule: {
- unitPrice: row?.cpFixedPricingRule?.unitPrice || 0,
- pricingUnit: row?.cpFixedPricingRule?.pricingUnit || '',
- convertDose: row?.cpFixedPricingRule?.convertDose || 0,
- convertUnit: row?.cpFixedPricingRule?.convertUnit || '',
- },
- cpDynamicPricingRule: row?.cpDynamicPricingRule || [],
- cpMedicines: row?.cpMedicines || [],
- isOffline: row?.isOffline || null,
- isDelivery: row?.isDelivery || null,
- photo: row?.photo || '',
- conditioningProgramSupplierName: row?.conditioningProgramSupplierName || '',
- },
- remark: '',
- });
- // 关闭弹窗
- showProjectPopover.value = false;
- // 清空搜索
- projectSearch.value = '';
- }
- function removeTableRow(idx: number) {
- if (idx < (formData.items ?? []).length) {
- formData.items?.splice(idx, 1);
- }
- }
- function onPreview(row) {
- if (row.conditioningProgramDetail.id) {
- // 这里写你的预览逻辑
- VxeUI.modal.open({
- title: `预览`,
- height: 600,
- width: 950,
- escClosable: true,
- destroyOnClose: true,
- id: `preview-modal`,
- remember: true,
- storage: true,
- slots: {
- default() {
- return h(ServiceDetail, {
- data: row.conditioningProgramDetail,
- onSubmit(data: SystemCwModel) {
- VxeUI.modal.close(`preview-modal`);
- },
- });
- },
- },
- });
- } else {
- message.warning('请先添加服务包');
- }
- }
- function detailPreview(row) {
- if (row.id) {
- // 这里写你的预览逻辑
- VxeUI.modal.open({
- title: `预览`,
- height: 600,
- width: 950,
- escClosable: true,
- destroyOnClose: true,
- id: `preview-modal`,
- remember: true,
- storage: true,
- slots: {
- default() {
- return h(ServiceDetail, {
- data: row,
- onSubmit: (data) => {
- VxeUI.modal.close(`preview-modal`);
- },
- });
- },
- },
- });
- showProjectPopover.value = false;
- } else {
- message.warning('请先添加服务包');
- }
- }
- function editPart(row) {
- VxeUI.modal.open({
- title: `编辑部位`,
- height: 700,
- width: 750,
- escClosable: true,
- destroyOnClose: true,
- id: `edit-part-modal`,
- remember: true,
- storage: true,
- slots: {
- default() {
- return h(AcupointEdit, <any>{
- data: row,
- onSubmit(data: any) {
- VxeUI.modal.close(`edit-part-modal`);
- },
- });
- },
- },
- });
- }
- const allProjects = ref<
- Array<{
- name: string;
- conditioningProgramType?: string;
- effect?: string;
- }>
- >([]);
- const { loading: projectLoading, onSuccess } = useRequest(() => getCpContentListMethod(), {
- immediate: true,
- });
- onSuccess(({ data }) => {
- allProjects.value = data;
- });
- const filteredProjects = computed(() => {
- const searchText = projectSearch.value.toLowerCase();
- return allProjects.value.filter(
- (p) => p.name.toLowerCase().includes(searchText) || p?.conditioningProgramType?.toLowerCase().includes(searchText) || p.effect?.toLowerCase().includes(searchText)
- );
- });
- // 添加计算数量的函数
- function calculateCount(row: any) {
- const pricingType = row.conditioningProgramDetail.pricingType;
- const period = Number(row.days) || 0;
- const frequency = Number(row.frequencyMeasure) || 0;
- const maxCount = row.conditioningProgramDetail.cpDynamicPricingRule?.[1]?.max;
- const acCount = (row.acuMeridianNames?.length ?? 0) + (row.acuPointNames?.length ?? 0);
- // 一口价
- if (pricingType === '0') {
- // 检查是否选择了"不限"
- if (row.frequencyType === '不限') {
- row.frequencyMeasure = ''; // 重置 frequencyMeasure
- row.totalMeasure = 1;
- } else {
- const convertDose = Number(row.conditioningProgramDetail.cpFixedPricingRule.convertDose) || 0;
- const frequencyType = Number(row.frequencyType) || 0;
- row.totalMeasure = Math.ceil(((period / frequencyType) * frequency) / convertDose);
- }
- // 获取单价
- const unitPrice = Number(row.conditioningProgramDetail?.cpFixedPricingRule?.unitPrice) || 0;
- // 计算总价
- row.totalPrice = (row.totalMeasure * unitPrice).toFixed(2);
- } else if (pricingType === '1') {
- // 按穴位计价
- const frequencyType = Number(row.frequencyType) || 0;
- row.totalMeasure = Math.ceil((period / frequencyType) * frequency);
- if (acCount > maxCount) {
- if (row.conditioningProgramDetail.cpDynamicPricingRule?.[1]?.priceType === 0) {
- // 单价
- if (acCount > 0) {
- let unitPrice: number = row.conditioningProgramDetail.cpDynamicPricingRule[1].price * acCount;
- row.unitPrice = unitPrice;
- row.totalPrice = Math.ceil((period / frequencyType) * frequency * unitPrice);
- } else {
- row.unitPrice = '-';
- row.totalPrice = 0;
- }
- } else if (row.conditioningProgramDetail.cpDynamicPricingRule?.[1]?.priceType === 1) {
- // 一口价
- row.unitPrice = '-';
- row.totalPrice = row.conditioningProgramDetail.cpDynamicPricingRule[1].price;
- }
- } else {
- if (row.conditioningProgramDetail.cpDynamicPricingRule?.length > 0) {
- if (row.conditioningProgramDetail.cpDynamicPricingRule?.[0]?.priceType === 0) {
- // 单价
- if (acCount > 0) {
- let unitPrice: number = row.conditioningProgramDetail.cpDynamicPricingRule[0].price * acCount;
- row.unitPrice = unitPrice;
- row.totalPrice = Math.ceil((period / frequencyType) * frequency * unitPrice);
- } else {
- row.unitPrice = '-';
- row.totalPrice = 0;
- }
- } else if (row.conditioningProgramDetail.cpDynamicPricingRule?.[0]?.priceType === 1) {
- // 一口价
- row.unitPrice = '-';
- row.totalPrice = row.conditioningProgramDetail.cpDynamicPricingRule[0].price;
- }
- }
- }
- }
- }
- // 添加监听器
- watch(totalPrice, (val) => {
- formData.price = val;
- form.cost = val;
- });
- // 获取调理包详情
- async function selectCw(item: any) {
- currentSelectedPackage.value = item;
- selectedPackage.value = item.id;
- item.types = 'institution';
- const res: any = await getConditioningRecordDetailMethod(item);
- Object.assign(formData, res);
- formData.items = res?.items ?? [];
- form.conditioningWrapName = item.name;
- form.conditioningWrapId = item.id;
- form.phone = item.phone;
- }
- function handleCancel() {
- console.log('取消');
- }
- // 添加监听器
- watch(
- () => formData.items,
- (newData) => {
- if (!newData) return;
- newData.forEach((row) => {
- calculateCount(row);
- });
- },
- { deep: true }
- );
- // 添加电话号码验证函数
- function isValidPhone(phone: string): boolean {
- // 中国大陆手机号码正则表达式
- const phoneRegex = /^1[3-9]\d{9}$/;
- return phoneRegex.test(phone);
- }
- async function handleSubmit() {
- if (!currentPatient.value) {
- message.error('请选择患者');
- return;
- }
- if (formData.items) {
- formData.items.forEach((item) => {
- delete item.id;
- });
- }
- form.id = Number(currentPatient.value.id);
- form.patientId = currentPatient.value.patientId;
- form.patientName = currentPatient.value.patientName;
- form.patientSex = currentPatient.value.patientSex;
- form.patientAge = currentPatient.value.patientAge;
- form.diagnosis = currentPatient.value.diagnosis;
- form.symptom = currentPatient.value.symptom;
- form.items = formData.items;
- // 添加电话号码验证
- if (form.phone && !isValidPhone(form.phone)) {
- message.error('请输入有效的手机号码');
- return;
- }
- await addConditioningSchemeMethod(form).then(async () => {
- notification.success({ message: '开立成功' });
- // 开立成功之后 刷新列表
- // 刷新患者列表,并保持当前患者高亮
- const patientList = await getPatientListMethod();
- patients.value = patientList;
- // 重新设置 currentPatient 为刚才的患者
- if (currentPatient.value) {
- const newCurrent = patientList.find((p) => p.patientId === currentPatient.value.patientId);
- if (newCurrent) {
- currentPatient.value = newCurrent;
- // 获取患者列表
- await getPatientList(currentPatient.value?.patientId || '');
- // 获取服务包选择列表
- await loadAvailableCw(currentPatient.value?.patientId || '',currentPatient.value?.id || '');
- // 获取调养记录
- await getCpRecordDetail(currentPatient.value?.id || '');
- }
- }
- });
- }
- // 省市区的数据
- const provinceOptions = ref([]);
- const cityOptions = ref([]);
- const areaOptions = ref([]);
- const selectedProvince = ref('');
- const selectedCity = ref('');
- const selectedArea = ref('');
- async function loadProvinces() {
- const res: any = await getProvinceMethod();
- provinceOptions.value = res.map((item) => ({
- value: item.code,
- label: item.name,
- }));
- }
- async function loadCities(name: string, provincecode: string) {
- if (!provincecode) {
- cityOptions.value = [];
- areaOptions.value = [];
- selectedCity.value = '';
- selectedArea.value = '';
- return;
- }
- const res: any = await getCityMethod(name, provincecode);
- cityOptions.value = res.map((item) => ({
- value: item.code,
- label: item.name,
- }));
- }
- async function loadAreas(name: string, citycode: string) {
- if (!citycode) {
- areaOptions.value = [];
- selectedArea.value = '';
- return;
- }
- const res: any = await getAreaMethod(name, citycode);
- areaOptions.value = res.map((item) => ({
- value: item.code,
- label: item.name,
- }));
- }
- function handleProvinceChange(value: string) {
- if (btnType.value === '转方案') {
- selectedCity.value = '';
- selectedArea.value = '';
- form.cityCode = '';
- form.areaCode = '';
- form.detailAddress=''
- }
- selectedProvince.value = value;
- const selectedProvinceOption = provinceOptions.value.find((p) => p.value === value);
- form.provinceName = selectedProvinceOption?.label || '';
- form.provinceCode = selectedProvinceOption?.value || '';
- // 获取城市
- loadCities(selectedProvinceOption?.name, value);
- }
- function handleCityChange(value: string) {
- selectedCity.value = value;
- const selectedCityOption = cityOptions.value.find((c) => c.value === value);
- form.cityName = selectedCityOption?.label || '';
- form.cityCode = selectedCityOption?.value || '';
- if (btnType.value === '转方案') {
- selectedArea.value = '';
- form.areaCode = '';
- form.detailAddress=''
- }
- loadAreas(selectedCityOption?.name, value);
- }
- function handleAreaChange(value: string) {
- selectedArea.value = value;
- const selectedAreaOption = areaOptions.value.find((a) => a.value === value);
- form.areaName = selectedAreaOption?.label || '';
- form.areaCode = selectedAreaOption?.value || '';
- if (btnType.value === '转方案') {
- form.detailAddress=''
- }
- }
- interface PatientInfo {
- patientName?: string;
- patientSex?: string;
- patientAge?: number;
- }
- interface CpDetailInfo {
- womenSpecialPeriod?: string;
- cardno?: string;
- }
- function formatPatientInfo(patient: PatientInfo | null, cpDetail: CpDetailInfo | null): string {
- if (!patient) return '';
- const parts: string[] = [];
- // 姓名
- if (patient.patientName) {
- parts.push(patient.patientName);
- }
- // 性别
- if (patient.patientSex) {
- const gender = patient.patientSex === '1' ? '女' : patient.patientSex === '0' ? '男' : '';
- if (gender) {
- parts.push(gender);
- }
- }
- // 年龄
- if (patient.patientAge) {
- parts.push(`${patient.patientAge}岁`);
- }
- // 特殊时期
- parts.push(getWomenSpecialPeriod(cpDetail?.womenSpecialPeriod || '0'));
- // 身份证号
- if (cpDetail?.cardno) {
- parts.push(`身份证号:${cpDetail.cardno}`);
- }
- return parts.join(',');
- }
- function getWomenSpecialPeriod(type: string) {
- if (type === '0') {
- return '无';
- } else if (type === '1') {
- return '月经期';
- } else if (type === '2') {
- return '孕期';
- } else if (type === '3') {
- return '产后';
- } else if (type === '4') {
- return '哺乳期';
- }
- }
- // 处理日期选择
- const handleDateChange = (date: any) => {
- if (date) {
- form.estimatedStartDate = dayjs(date).format('YYYY-MM-DD');
- } else {
- form.estimatedStartDate = '';
- }
- };
- watch(showProjectPopover, (val) => {
- if (!val) {
- projectSearch.value = '';
- }
- });
- function openPatientTagEdit(event: MouseEvent) {
- const width = 500;
- const offset = 32;
- const component = defineAsyncComponent(() => import('@/components/PatientTagEdit.vue'));
- const id = `PatientTagEdit`;
- VxeUI.modal.open({
- id,
- title: '标签',
- type: 'modal',
- position: {
- top: event.pageY + offset,
- left: event.pageX - width,
- },
- escClosable: true,
- resize: true,
- width,
- minWidth: width,
- mask: false,
- slots: {
- default() {
- return h(component, {
- id: currentPatient.value?.patientId!,
- tags: patientTags.value,
- onDestroy(values?: PatientTagVO[]) {
- if (values) {
- patientTags.value = values;
- }
- VxeUI.modal.close(id);
- },
- });
- },
- },
- });
- }
- function openHistoryPreviewHandle() {
- const data = currentPatient.value;
- const patient = { id: data?.patientId };
- const report = { id: data?.healthAnalysisReportId };
- const component = defineAsyncComponent(() => import('@/components/PatientHealthRecordPreview.vue'));
- const id = `drawer:report-history:preview`;
- const onDestroy = () => {
- VxeUI.drawer.close(id);
- };
- onDestroy();
- VxeUI.drawer.open({
- id,
- title: `健康档案`,
- maskClosable: true,
- escClosable: true,
- padding: false,
- width: window.innerWidth - 256,
- slots: {
- default() {
- return h(component, {
- patient,
- report,
- onDestroy,
- onRefresh() {},
- });
- },
- },
- onHide() {
- VxeUI.modal.close();
- },
- });
- }
- function openPatientHealthRecord(row: { id: string }, showType: 'analysis' | 'scheme' = 'analysis') {
- const component = defineAsyncComponent(() => import('@/components/ReportPreview.vue'));
- const id = `drawer:report:preview`;
- const onDestroy = () => {
- VxeUI.drawer.close(id);
- };
- onDestroy();
- VxeUI.drawer.open({
- id,
- mask: true,
- lockView: false,
- padding: false,
- width: window.innerWidth - 256,
- escClosable: true,
- maskClosable: true,
- title: { analysis: `健康分析报告`, scheme: `调理方案` }[showType],
- slots: {
- default() {
- return h(component, {
- reportId: row.id.toString(),
- type: showType,
- onDestroy,
- });
- },
- },
- });
- }
- </script>
- <template>
- <div class="issue-service-page">
- <!-- 左侧患者列表 -->
- <div class="left-panel">
- <a-input v-model:value="searchName" placeholder="输入姓名搜索" style="margin-bottom: 12px" />
- <div
- v-if="filteredPatients.length > 0"
- class="patient-list"
- >
- <div
- class="patient-item"
- v-for="item in filteredPatients"
- :key="item.id"
- @click="selectPatient(item)"
- :class="{ active: currentPatient?.id === item.id }"
- style="cursor: pointer"
- >
- <span>{{ item.patientName }}</span><span v-if="item.patientAge">{{ item.patientAge }}岁</span>
- <span v-if="item.status === '0'" style="color: #aaa">(已开)</span>
- </div>
- </div>
- <div v-else style="padding-bottom: 8px; text-align: center; margin-top: 40px">暂无数据</div>
- </div>
- <!-- 中间主内容 -->
- <div class="main-panel">
- <!-- 顶部患者信息 -->
- <div class="patient-info">
- <div style="font-size: 12px">
- {{ formatPatientInfo(currentPatient || null, cpDetail) }}
- </div>
- <div style="margin: 12px 0 12px 0; font-size: 12px">
- <span style="color: lightgray" v-if="currentPatient?.diagnosis">诊断:</span><span v-if="currentPatient?.diagnosis">{{ currentPatient?.diagnosis }}</span>
- <span style="margin-left: 20px; color: lightgray" v-if="form?.healthAnalysisReport?.willillStateName">欲病状态:</span
- ><span>{{ form?.healthAnalysisReport?.willillStateName }}</span>
- <span style="margin-left: 20px; color: lightgray" v-if="form?.healthAnalysisReport?.willillDegreeName">欲病程度:</span
- ><span>{{ form?.healthAnalysisReport?.willillDegreeName }}</span>
- <span style="margin-left: 20px; color: lightgray" v-if="form?.healthAnalysisReport?.willillSocialName">欲病类型:</span
- ><span>{{ form?.healthAnalysisReport?.willillSocialName }}</span>
- <span style="margin-left: 20px; color: lightgray" v-if="form?.healthAnalysisReport?.willillFunctionName">欲病表现:</span
- ><span>{{ form?.healthAnalysisReport?.willillFunctionName }}</span>
- <span style="margin-left: 20px; color: lightgray" v-if="form?.healthAnalysisReport?.constitutionGroupName">体质:</span>
- <span>{{ form?.healthAnalysisReport?.constitutionGroupName }}</span>
- </div>
- </div>
- <!-- 服务包选择 -->
- <div v-if="filteredPatients.length > 0">
- <div class="service-select-row">
- <span>服务包选择:</span>
- <template v-if="currentPatient?.status === '0'">
- <span>{{ form.conditioningWrapName }}</span>
- </template>
- <template v-else>
- <a-select v-model:value="selectedPackage" style="width: 180px" placeholder="请选择服务包" >
- <a-select-option v-for="item in lifeCwData" :key="item.id" :value="item.id" @click="selectCw(item)">
- {{ item.name }}
- </a-select-option>
- </a-select>
- </template>
- <div v-if="currentPatient?.status === '1' && suggestCwData.length > 0">
- <span style="margin-left: 16px; color: #1890ff">推荐:</span>
- <a class="suggest-cw" v-for="item in suggestCwData" :key="item.id" @click="selectCw(item)">{{ item.name }}</a>
- </div>
- </div>
- <!-- 服务包内容表格 -->
- <div class="table-section">
- <div class="table-title">服务包内容</div>
- <vxe-table :data="displayTableData" border style="margin-top: 8px" max-height="380px">
- <vxe-column width="60" title="">
- <template #default="{ rowIndex }">
- <a-button type="text" danger @click="removeTableRow(rowIndex)" :disabled="rowIndex === displayTableData.length - 1">
- <MinusCircleOutlined />
- </a-button>
- </template>
- </vxe-column>
- <vxe-column field="conditioningProgramDetail.name" title="项目名称" width="180">
- <template #default="{ row, rowIndex }">
- <template v-if="rowIndex === displayTableData.length - 1">
- <a-popover v-model:open="showProjectPopover" trigger="click" placement="bottomLeft" :overlayStyle="{ width: '350px', padding: 0 }">
- <template #content>
- <a-input v-model:value="projectSearch" placeholder="输入项目名称搜索" style="margin: 8px; width: 90%" @input="() => nextTick()" />
- <vxe-table :data="filteredProjects" border size="small" style="max-height: 240px; overflow-y: auto" @cell-click="onSelectProject">
- <vxe-column field="name" title="项目名称" />
- <vxe-column field="conditioningProgramType" title="方案类型" />
- <vxe-column field="effect" title="功效" />
- <vxe-column title="预览" width="60">
- <template #default="{ row }">
- <EyeOutlined style="font-size: 18px; color: #1890ff; cursor: pointer" @click.stop="detailPreview(row)" />
- </template>
- </vxe-column>
- </vxe-table>
- </template>
- <a-input v-model:value="row.name" placeholder="请搜索" style="width: 120px" @click="showProjectPopover = true" />
- </a-popover>
- </template>
- <template v-else>
- {{ row.conditioningProgramDetail?.name }}
- </template>
- </template>
- </vxe-column>
- <vxe-column title="预览" width="60">
- <template #default="{ row }">
- <EyeOutlined style="font-size: 18px; color: #1890ff; cursor: pointer" @click="onPreview(row)" />
- </template>
- </vxe-column>
- <vxe-column field="days" title="周期" width="120">
- <template #default="{ row }">
- <div style="display: flex; align-items: center">
- <a-input v-model:value="row.days" @change="() => calculateCount(row)" />
- <span>天</span>
- </div>
- </template>
- </vxe-column>
- <vxe-column field="frequencyType" title="频率" width="auto">
- <template #default="{ row }">
- <div v-if="row.conditioningProgramDetail?.name === '健康咨询' || row.conditioningProgramDetail?.name === '健康评估'" class="flex items-center">
- <div class="flex items-center mr-4">
- <span>每</span>
- <a-input v-model:value="row.frequencyType" style="width: 50px" @change="() => calculateCount(row)" :disabled="row.frequencyType === '不限'" />
- <span>天</span>
- <a-input v-model:value="row.frequencyMeasure" style="width: 50px" @change="() => calculateCount(row)" :disabled="row.frequencyType === '不限'" />
- <span>{{ row.conditioningProgramDetail?.cpFixedPricingRule?.convertUnit ? row.conditioningProgramDetail?.cpFixedPricingRule?.convertUnit : '次' }}</span>
- </div>
- <div>
- <a-checkbox-group
- v-model:value="row.frequencyTypeing"
- @change="
- (value) => {
- row.frequencyTypeing = value.includes('不限') ? ['不限'] : [];
- row.frequencyType = value.includes('不限') ? '不限' : '';
- calculateCount(row);
- }
- "
- >
- <a-checkbox value="不限">不限</a-checkbox>
- </a-checkbox-group>
- </div>
- </div>
- <div class="flex items-center" v-else>
- <span>每</span>
- <a-input v-model:value="row.frequencyType" style="width: 50px" @change="() => calculateCount(row)" />
- <span>天</span>
- <a-input v-model:value="row.frequencyMeasure" style="width: 50px" @change="() => calculateCount(row)" />
- <span>{{ row.conditioningProgramDetail?.cpFixedPricingRule?.convertUnit ? row.conditioningProgramDetail?.cpFixedPricingRule?.convertUnit : '次' }}</span>
- </div>
- </template>
- </vxe-column>
- <vxe-column field="row?.conditioningProgramDetail?.conditioningProgramType" title="方案类型" width="120">
- <template #default="{ row }">
- {{ row?.conditioningProgramDetail?.conditioningProgramType || '-' }}
- </template>
- </vxe-column>
- <vxe-column field="row.totalMeasure" title="数量" width="60">
- <template #default="{ row }">
- {{ row.totalMeasure || 0 }}
- </template>
- </vxe-column>
- <vxe-column field="row?.conditioningProgramDetail?.cpFixedPricingRule?.unit" title="单位" width="60">
- <template #default="{ row }">
- {{ row?.conditioningProgramDetail?.cpFixedPricingRule?.pricingUnit ? row?.conditioningProgramDetail?.cpFixedPricingRule?.pricingUnit : '次' }}
- </template>
- </vxe-column>
- <vxe-column field="row.conditioningProgramDetail.cpFixedPricingRule.unitPrice" title="单价(元)" width="100">
- <template #default="{ row }">
- {{ row?.conditioningProgramDetail?.pricingType === '0' ? row.conditioningProgramDetail?.cpFixedPricingRule?.unitPrice : row?.unitPrice }}
- </template>
- </vxe-column>
- <vxe-column field="row.totalPrice" title="总价(元)" width="auto" @change="() => calculateCount(row)">
- <template #default="{ row }">
- <span>{{ row.totalPrice || 0 }}</span>
- </template>
- </vxe-column>
- <vxe-column field="start" title="开始时间" width="190">
- <template #default="{ row }">
- <div style="display: flex; align-items: center">
- <span>调养开始第</span>
- <a-input v-model:value="row.initialDay" style="width: 80px" />
- <span>天</span>
- </div>
- </template>
- </vxe-column>
- <vxe-column field="conditioningProgramDetail.isOffline" title="线下项目" width="100">
- <template #default="{ row }">
- <span v-if="row.conditioningProgramDetail?.isOffline">
- {{ row.conditioningProgramDetail.isOffline === 'Y' ? '是' : '否' }}
- </span>
- <span v-else>-</span>
- </template>
- </vxe-column>
- <vxe-column field="conditioningProgramDetail.pricingType" title="穴位/经络/部位" width="160">
- <template #default="{ row }">
- <!-- <a-input v-model:value="row.desc" style="width: 120px" :disabled="currentPatient?.status === '0' ? true : false" /> -->
- <span>
- <a @click="editPart(row)" style="color: #1890ff; cursor: pointer" v-if="row.conditioningProgramDetail.pricingType === '1'">编辑</a>
- </span>
- </template>
- </vxe-column>
- <vxe-column field="remark" title="说明" width="180">
- <template #default="{ row }">
- <!-- <a-input v-model:value="row.remark" style="width: 120px" :disabled="currentPatient?.status === '0' ? true : false" /> -->
- <a-textarea
- v-model:value="row.remark"
- style="max-width: 180px; width: 100%; height: 50px"
- :rows="2"
- show-count
- :maxLength="200"
- :disabled="currentPatient?.status === '0' ? true : false"
- />
- </template>
- </vxe-column>
- </vxe-table>
- </div>
- <div style="display: flex; justify-content: flex-end; margin-top: 16px">
- <span style="font-weight: bold">合计:{{ totalPrice }}元</span>
- </div>
- <!-- 调养日期 -->
- <div class="delivery-row">
- <span>开始调养日期:</span>
- <span v-if="currentPatient?.status === '0' && btnType !== '转方案'">
- {{ form.estimatedStartDate }}
- </span>
- <span v-else>
- <a-date-picker
- :value="form.estimatedStartDate ? dayjs(form.estimatedStartDate) : null"
- @change="handleDateChange"
- placeholder="请选择日期"
- :disabledDate="(current) => current && current < dayjs().startOf('day')"
- />
- </span>
- </div>
- <!-- 配送信息 -->
- <div class="delivery-row" v-if="isShowDelivery">
- <div v-if="(btnType !== '转方案' && currentPatient?.status === '0')">
- <a-checkbox v-model:checked="deliveryChecked" disabled>配送</a-checkbox>
- <span>地址:</span>
- <span>{{ form.provinceName }}{{ form.cityName }}{{ form.areaName }}{{ form.detailAddress }}</span>
- <span style="margin-left: 16px" v-if="form.phone">电话:</span>
- <span>{{ form.phone }}</span>
- </div>
- <!-- <div v-if="!form.progress || currentPatient?.status === '1'"> -->
- <div v-else>
- <a-checkbox v-model:checked="deliveryChecked">配送</a-checkbox>
- <template v-if="deliveryChecked">
- <span>地址:</span>
- <a-select v-model:value="selectedProvince" :options="provinceOptions" placeholder="请选择省" style="width: 100px; margin-right: 4px" @change="handleProvinceChange" />
- <a-select
- v-model:value="selectedCity"
- :options="cityOptions"
- placeholder="请选择市"
- style="width: 100px; margin-right: 4px"
- :disabled="!selectedProvince"
- @change="handleCityChange"
- />
- <a-select
- v-model:value="selectedArea"
- :options="areaOptions"
- placeholder="请选择区"
- style="width: 100px; margin-right: 4px"
- :disabled="!selectedCity"
- @change="handleAreaChange"
- />
- <a-input v-model:value="form.detailAddress" placeholder="详细地址" style="width: 120px; margin-right: 4px" />
- <span>电话:</span>
- <a-input v-model:value="form.phone" placeholder="请输入" style="width: 120px" />
- </template>
- </div>
- </div>
- <!-- 操作按钮 -->
- <div class="footer-btns" v-if="currentPatient?.status === '1'">
- <a-button @click="handleCancel">取消</a-button>
- <a-button type="primary" style="margin-left: 24px" @click="handleSubmit">确认</a-button>
- </div>
- </div>
- <a-result class="area" v-else style="background-color: #fff" status="warning" title="暂无数据" />
- </div>
- <!-- 右侧调养记录 -->
- <div class="right-panel flex flex-col overflow-hidden">
- <section style="flex: 0 0 auto; max-height: 270px; overflow-y: auto">
- <div style="margin-top: -6px; padding-right: 8px">
- <label>标签:</label>
- <a-tag v-for="tag in patientTags" :key="tag.id" :color="tag.color">{{ tag.name }}</a-tag>
- <a-button type="link" @click="openPatientTagEdit($event)">
- <template #icon>
- <EditOutlined />
- </template>
- </a-button>
- </div>
- </section>
- <section style="margin: 8px">
- <a-button type="primary" block @click="openHistoryPreviewHandle()">健康档案</a-button>
- </section>
- <div class="record-title">报告记录</div>
- <section class="overflow-auto" style="flex: 4 4 auto; min-height: 240px">
- <div class="record-list">
- <div class="record-item flex justify-between" v-for="item in patientHealthRecords" :key="item.id" @click="openPatientHealthRecord(item, 'analysis')">
- {{ item.date }}
- <a @click.stop="openPatientHealthRecord(item, 'scheme')">方案</a>
- </div>
- <div v-if="!patientRecord.length" style="padding-bottom: 8px; text-align: center; margin-top: 40px">暂无数据</div>
- </div>
- </section>
- <div class="record-title">调养记录</div>
- <section class="overflow-auto" style="flex: 4 4 auto; min-height: 240px">
- <div class="record-list">
- <div class="record-item" v-for="item in patientRecord" :key="item.id" @click="openRecord(item)">{{ item.estimatedStartDate }}~{{ item.estimatedEndDate }}</div>
- <div v-if="!patientRecord.length" style="padding-bottom: 8px; text-align: center; margin-top: 40px">暂无数据</div>
- </div>
- </section>
- </div>
- </div>
- </template>
- <style scoped lang="scss">
- .issue-service-page {
- display: flex;
- flex-direction: row;
- width: 100vw;
- /* min-height: 100vh; */
- height: var(--page-main-container);
- overflow: hidden;
- background: #fff;
- }
- .left-panel {
- flex: none;
- width: 180px;
- border-right: 1px solid #eee;
- padding: 16px 8px 0 16px;
- background: #fafbfc;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- min-height: 0;
- }
- .patient-list {
- flex: 1 1 0;
- overflow: auto;
- min-height: 0;
- // 让内容撑满剩余空间
- }
- .patient-item {
- padding: 10px 0;
- font-size: 14px;
- color: #333;
- }
- .patient-item.active {
- background: #e6f7ff;
- color: #1890ff;
- }
- .main-panel {
- flex: 1 1 0;
- min-width: 0;
- padding: 16px 32px 0 32px;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- overflow: auto;
- }
- .patient-info {
- font-size: 14px;
- color: #333;
- }
- .service-select-row {
- margin-bottom: 16px;
- display: flex;
- align-items: center;
- gap: 8px;
- font-size: 14px;
- }
- .table-section {
- margin-bottom: 16px;
- }
- .table-title {
- font-weight: bold;
- margin-bottom: 8px;
- }
- .table-total {
- text-align: right;
- margin-top: 8px;
- font-weight: bold;
- }
- .delivery-row {
- display: flex;
- align-items: center;
- gap: 4px;
- margin-bottom: 24px;
- margin-top: 16px;
- }
- .footer-btns {
- display: flex;
- justify-content: flex-end;
- gap: 24px;
- }
- .right-panel {
- flex: none;
- width: 190px;
- border-left: 1px solid #eee;
- //padding: 16px 8px;
- background: #fafbfc;
- /* min-height: 100vh; */
- box-sizing: border-box;
- > section {
- margin: 16px 0;
- padding-left: 8px;
- .ant-tag {
- margin-top: 6px;
- }
- }
- }
- .record-title {
- font-weight: bold;
- text-align: center;
- }
- .record-list {
- display: flex;
- flex-direction: column;
- gap: 8px;
- padding-right: 8px;
- }
- .record-item {
- background: #fff;
- border: 1px solid #eee;
- border-radius: 4px;
- padding: 8px;
- text-align: center;
- font-size: 12px;
- font-weight: bold;
- cursor: pointer;
- &:hover {
- color: #1890ff;
- }
- }
- .suggest-cw {
- color: #1890ff;
- cursor: pointer;
- margin-right: 10px;
- }
- .scroll-content {
- /* max-height: 600px; */
- overflow-y: auto;
- /* 可选:让滚动条更美观 */
- scrollbar-width: thin;
- scrollbar-color: #aaa #f5f5f5;
- }
- </style>
|