Quellcode durchsuchen

feat: 透析病历

fan vor 2 Jahren
Ursprung
Commit
1e155f4622
28 geänderte Dateien mit 4690 neuen und 12 gelöschten Zeilen
  1. 15 0
      src/views/biz/archives/detail/data.ts
  2. 73 0
      src/views/biz/archives/detail/index.vue
  3. 637 0
      src/views/biz/archives/diagnosisHistory/FormModal.vue
  4. 374 0
      src/views/biz/archives/diagnosisHistory/data.ts
  5. 394 0
      src/views/biz/archives/diagnosisHistory/index.vue
  6. 93 0
      src/views/biz/archives/formula/FormDrawer.vue
  7. 133 0
      src/views/biz/archives/formula/data.ts
  8. 183 0
      src/views/biz/archives/formula/index.vue
  9. 0 7
      src/views/biz/archives/index.vue
  10. 75 0
      src/views/biz/archives/index/FormDrawerSift.vue
  11. 61 5
      src/views/biz/archives/index/data.ts
  12. 311 0
      src/views/biz/archives/index/index.vue
  13. 90 0
      src/views/biz/archives/patientBasic/FormModal.vue
  14. 200 0
      src/views/biz/archives/patientBasic/data.ts
  15. 122 0
      src/views/biz/archives/patientBasic/index.vue
  16. 79 0
      src/views/biz/archives/patientReturn/FormModal.vue
  17. 68 0
      src/views/biz/archives/patientReturn/data.ts
  18. 190 0
      src/views/biz/archives/patientReturn/index.vue
  19. 83 0
      src/views/biz/archives/patrolward/FormModal.vue
  20. 77 0
      src/views/biz/archives/patrolward/data.ts
  21. 236 0
      src/views/biz/archives/patrolward/index.vue
  22. 93 0
      src/views/biz/archives/vascularAccess/FormDrawer.vue
  23. 99 0
      src/views/biz/archives/vascularAccess/FormModal.vue
  24. 86 0
      src/views/biz/archives/vascularAccess/FormModalReturn.vue
  25. 0 0
      src/views/biz/archives/vascularAccess/ViewDrawer.vue
  26. 226 0
      src/views/biz/archives/vascularAccess/ViewDrawerComplication.vue
  27. 333 0
      src/views/biz/archives/vascularAccess/data.ts
  28. 359 0
      src/views/biz/archives/vascularAccess/index.vue

+ 15 - 0
src/views/biz/archives/detail/data.ts

@@ -0,0 +1,15 @@
+export const BasicTab = [
+  { key: 'patientBasic', value: 0, title: '基础病历' },
+  { key: 'vascularAccess', value: 1, title: '血管通路' },
+  { key: 'formula', value: 2, title: '透析处方' },
+  { key: 'patientBasic2', value: 3, title: '透析记录' },
+  { key: 'patientBasic3', value: 4, title: '医嘱记录' },
+  { key: 'patrolward', value: 5, title: '查房记录' },
+  { key: 'patientBasic5', value: 6, title: '诊断记录' },
+  { key: 'patientBasic6', value: 7, title: '化验项' },
+  { key: 'patientBasic7', value: 8, title: '医疗文书' },
+  { key: 'patientReturn', value: 9, title: '转归' },
+  { key: 'patientBasic9', value: 10, title: '个人评估' },
+];
+
+export const BasicTabActive = BasicTab[0].key;

+ 73 - 0
src/views/biz/archives/detail/index.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="m-4">
+    <div class="mb-4">
+      <XTTitle :title="title" :go-back="true" />
+    </div>
+    <div class="py-2 bg-white">
+      <a-tabs v-model:activeKey="activeKey" :destroyInactiveTabPane="true">
+        <a-tab-pane v-for="item in tabData" :key="item.key" :tab="item.title">
+          <!-- 基础病历 0 -->
+          <div v-if="item.value == 0 && info.id">
+            <PatientBasic :info="info" />
+          </div>
+          <!-- 血管通路 1 -->
+          <div v-if="item.value == 1 && info.id">
+            <VascularAccess :info="info" />
+          </div>
+          <!-- 透析处方 2 -->
+          <div v-if="item.value == 2 && info.id">
+            <Formula :info="info" />
+          </div>
+          <!-- 查房记录 5 -->
+          <div v-if="item.value == 5 && info.id">
+            <Patrolward :info="info" />
+          </div>
+          <!-- 诊断记录 6 -->
+          <div v-if="item.value == 6 && info.id">
+            <DiagnosisHistory :info="info" />
+          </div>
+          <!-- 转归 9 -->
+          <div v-if="item.value == 9 && info.id">
+            <PatientReturn :info="info" />
+          </div>
+        </a-tab-pane>
+      </a-tabs>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { BasicTab, BasicTabActive } from './data';
+  import PatientBasic from '../patientBasic/index.vue';
+  import VascularAccess from '../vascularAccess/index.vue';
+  import Formula from '../formula/index.vue';
+  import Patrolward from '../patrolward/index.vue';
+  import DiagnosisHistory from '../diagnosisHistory/index.vue';
+  import PatientReturn from '../patientReturn/index.vue';
+  import { useRoute } from 'vue-router';
+  import { XTTitle } from '/@/components/XTTitle/index';
+
+  const route = useRoute();
+  console.log('🚀 ~ file: index.vue:25 ~ route:', route);
+  const info = ref({
+    id: String(route.query?.id),
+    accessId: String(route.query?.accessId),
+    name: route.query?.name,
+    gender: route.query?.gender,
+    age: route.query?.age,
+  });
+  const activeKey = ref(BasicTabActive);
+  const tabData = ref(BasicTab);
+  const title = `病历详情 — ${info.value.name}(${info.value.gender}${info.value.age})`;
+</script>
+
+<style lang="less" scoped>
+  ::v-deep(.ant-tabs-tab) {
+    padding: 12px;
+  }
+
+  ::v-deep(.ant-tabs-tabpane) {
+    padding: 12px;
+  }
+</style>

+ 637 - 0
src/views/biz/archives/diagnosisHistory/FormModal.vue

@@ -0,0 +1,637 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <div class="!pl-8 !pt-4">
+      <BasicForm @register="registerForm" layout="vertical" />
+      <div v-if="type != DiagnosisEnum.firstDialysis_field">
+        <div class="mb-4">
+          <a-button type="primary" shape="round" @click="handleAdd">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            添加
+          </a-button>
+        </div>
+        <!-- 过敏原添加 -->
+        <div v-if="type == DiagnosisEnum.allergic_field">
+          <BasicTable @register="registerTable" @edit-change="callEditChange">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.key === 'action'">
+                <TableAction
+                  :actions="[
+                    {
+                      auth: 'archives:diagnosisHistory:edit',
+                      icon: 'icon-xt-details_delete_default|iconfont',
+                      tooltip: '删除',
+                      popConfirm: {
+                        title: '是否取消删除',
+                        placement: 'left',
+                        confirm: handleDel.bind(null, record, column),
+                      },
+                    },
+                  ]"
+                />
+              </template>
+            </template>
+          </BasicTable>
+        </div>
+        <div v-if="type == DiagnosisEnum.operation_field">
+          <BasicTable @register="registerTableOperation" @edit-change="callEditChange">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.key === 'action'">
+                <TableAction
+                  :actions="[
+                    {
+                      auth: 'archives:diagnosisHistory:edit',
+                      icon: 'icon-xt-details_delete_default|iconfont',
+                      tooltip: '删除',
+                      popConfirm: {
+                        title: '是否取消删除',
+                        placement: 'left',
+                        confirm: handleDel.bind(null, record, column),
+                      },
+                    },
+                  ]"
+                />
+              </template>
+            </template>
+          </BasicTable>
+        </div>
+        <div v-if="type == DiagnosisEnum.contagious_field">
+          <BasicTable @register="registerTableContagious" @edit-change="callEditChange">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.key === 'action'">
+                <TableAction
+                  :actions="[
+                    {
+                      auth: 'archives:diagnosisHistory:edit',
+                      icon: 'icon-xt-details_delete_default|iconfont',
+                      tooltip: '删除',
+                      popConfirm: {
+                        title: '是否取消删除',
+                        placement: 'left',
+                        confirm: handleDel.bind(null, record, column),
+                      },
+                    },
+                  ]"
+                />
+              </template>
+            </template>
+          </BasicTable>
+        </div>
+        <div
+          v-if="
+            type == DiagnosisEnum.complications_field ||
+            type == DiagnosisEnum.clinic_field ||
+            type == DiagnosisEnum.pathological_field ||
+            type == DiagnosisEnum.ckd_field
+          "
+        >
+          <BasicTable @register="registerTableMulti" @edit-change="callEditChange">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.key === 'action'">
+                <TableAction
+                  :actions="[
+                    {
+                      auth: 'archives:diagnosisHistory:edit',
+                      icon: 'icon-xt-details_delete_default|iconfont',
+                      tooltip: '删除',
+                      popConfirm: {
+                        title: '是否取消删除',
+                        placement: 'left',
+                        confirm: handleDel.bind(null, record, column),
+                      },
+                    },
+                  ]"
+                />
+              </template>
+            </template>
+          </BasicTable>
+        </div>
+        <div v-if="type == DiagnosisEnum.elseRemark_field">
+          <BasicTable @register="registerTableOther" @edit-change="callEditChange">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.key === 'action'">
+                <TableAction
+                  :actions="[
+                    {
+                      auth: 'archives:diagnosisHistory:edit',
+                      icon: 'icon-xt-details_delete_default|iconfont',
+                      tooltip: '删除',
+                      popConfirm: {
+                        title: '是否取消删除',
+                        placement: 'left',
+                        confirm: handleDel.bind(null, record, column),
+                      },
+                    },
+                  ]"
+                />
+              </template>
+            </template>
+          </BasicTable>
+        </div>
+      </div>
+      <!-- {{ type }} -->
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, unref, nextTick, reactive } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { PlusOutlined } from '@ant-design/icons-vue';
+
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import {
+    dataFormSchema,
+    columnsAllergic,
+    DiagnosisEnum,
+    columnsMulti,
+    columnsContagious,
+    columnsOther,
+    columnsOperation,
+  } from './data';
+  import {
+    archivesDiagnosisHistorySingleAddOrEdit,
+    archivesDiagnosisHistoryMultiAddOrEdit,
+  } from '/@/api/biz/archives/diagnosisHistoryApi';
+  import { nanoid } from 'nanoid';
+  import { listDictModelBatch } from '@/api/common';
+  import dayjs from 'dayjs';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = ref('编辑');
+  const width = '45%';
+  const isSingle = ref(true);
+  const type = ref('');
+  const typeOptions = ref([]);
+  const rowId = ref('');
+  const patientBasicId = ref('');
+  // 表格数据
+  // 过敏史
+  const tableDataAllergic = ref([]);
+  // 传染病史
+  const tableDataContagious = ref([]);
+  const tableDataOperation = ref([]);
+
+  const tableDataMulti = ref([]);
+  const tableDataOther = ref([]);
+
+  const { createMessage } = useMessage();
+  const bizDictOptions = reactive<any>({});
+  const bizDictData = ref([
+    { key: 'dh_allergic', dictCode: 'dh_allergic' },
+    { key: 'allergic_food', dictCode: 'allergic_food' },
+    { key: 'allergic_touch', dictCode: 'allergic_touch' },
+    { key: 'allergic_air', dictCode: 'allergic_air' },
+    { key: 'allergic_inject', dictCode: 'allergic_inject' },
+    { key: 'dh_contagious_status', dictCode: 'dh_contagious_status' },
+    { key: 'pb_epidemic', dictCode: 'pb_epidemic' },
+    { key: 'dh_complications', dictCode: 'dh_complications' },
+    { key: 'complications_breath', dictCode: 'complications_breath' },
+    { key: 'complications_blood', dictCode: 'complications_blood' },
+    { key: 'complications_incretion', dictCode: 'complications_incretion' },
+    { key: 'dh_clinic', dictCode: 'dh_clinic' },
+    { key: 'clinic_breath', dictCode: 'clinic_breath' },
+    { key: 'clinic_heart', dictCode: 'clinic_heart' },
+    { key: 'clinic_blood', dictCode: 'clinic_blood' },
+    { key: 'clinic_hbgr', dictCode: 'clinic_hbgr' },
+    { key: 'dh_pathological', dictCode: 'dh_pathological' },
+    { key: 'pathological_breath', dictCode: 'pathological_breath' },
+    { key: 'pathological_heart', dictCode: 'pathological_heart' },
+    { key: 'pathological_blood', dictCode: 'pathological_blood' },
+    { key: 'pathological_hbgr', dictCode: 'pathological_hbgr' },
+    { key: 'dh_ckd', dictCode: 'dh_ckd' },
+    { key: 'ckd_breath', dictCode: 'ckd_breath' },
+    { key: 'ckd_heart', dictCode: 'ckd_heart' },
+    { key: 'ckd_blood', dictCode: 'ckd_blood' },
+    { key: 'ckd_hbgr', dictCode: 'ckd_hbgr' },
+  ]);
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    console.log('🚀 ~ file: FormModal.vue:52 ~ data:', data);
+    const res = await listDictModelBatch(bizDictData.value.map(ele => ele.dictCode));
+    for (const i in res) {
+      const filter = bizDictData.value.filter(ele => ele.dictCode == i)[0];
+      bizDictOptions[filter.key] = res[i];
+    }
+    tableDataAllergic.value = [];
+    type.value = data.type || '';
+    typeOptions.value = data.dictOptions;
+    rowId.value = data?.id || '';
+    patientBasicId.value = data?.patientBasicId || '';
+    isSingle.value = data.isSingle;
+    getTitle.value = `${data.id ? '编辑' : '新增'} — ${data.title}`;
+    // 其他诊断
+    if (type.value == DiagnosisEnum.elseRemark_field) {
+      tableDataOther.value =
+        (data.multiContent.length &&
+          data.multiContent.map(ele => {
+            return {
+              remark: ele.remark,
+              id: nanoid(5),
+            };
+          })) ||
+        [];
+      console.log('🚀 ~ file: FormModal.vue:179 ~ tableDataOther.value:', tableDataOther.value);
+      await nextTick();
+      await setTableDataOther(tableDataOther.value);
+    }
+    // 过敏原
+    if (type.value == DiagnosisEnum.allergic_field) {
+      tableDataAllergic.value =
+        (data.content.length &&
+          data.content.map(ele => {
+            const nameOptions = bizDictOptions[ele.contentType];
+            return {
+              contentType: ele.contentType,
+              typeOptions: typeOptions.value,
+              name: ele.multiName,
+              nameOptions,
+              id: nanoid(5),
+            };
+          })) ||
+        [];
+      await nextTick();
+      await setTableDataAllergic(tableDataAllergic.value);
+    }
+    // 手术史
+    if (type.value == DiagnosisEnum.operation_field) {
+      tableDataOperation.value =
+        (data.content.length &&
+          data.content.map(ele => {
+            return {
+              singleName: ele.singleName,
+              recordTime: dayjs(ele.recordTime).format('YYYY-MM-DD'),
+              remark: ele.remark,
+              id: nanoid(5),
+            };
+          })) ||
+        [];
+      await nextTick();
+      await setTableDataOperation(tableDataOperation.value);
+    }
+    // 传染病史
+    if (type.value == DiagnosisEnum.contagious_field) {
+      console.log('🚀 ~ file: FormModal.vue:178 ~ data.data:', data);
+      tableDataContagious.value =
+        (data.content.length &&
+          data.content.map(ele => {
+            return {
+              contentType: ele.contentType,
+              typeOptions: typeOptions.value,
+              startTime: ele.startTime ? dayjs(ele.startTime).format('YYYY-MM-DD') : '',
+              endTime: ele.endTime ? dayjs(ele.endTime).format('YYYY-MM-DD') : '',
+              status: ele.status,
+              remark: ele.remark,
+              id: nanoid(5),
+            };
+          })) ||
+        [];
+      await nextTick();
+      await setTableDataContagious(tableDataContagious.value);
+    }
+    // 合并症类型、临床诊断类型、病理类型、CKD类型
+    if (
+      type.value == DiagnosisEnum.complications_field ||
+      type.value == DiagnosisEnum.clinic_field ||
+      type.value == DiagnosisEnum.pathological_field ||
+      type.value == DiagnosisEnum.ckd_field
+    ) {
+      console.log('🚀 ~ file: FormModal.vue:178 ~ data.data:', data);
+      tableDataMulti.value =
+        (data.multiContent.length &&
+          data.multiContent.map(ele => {
+            const nameOptions = bizDictOptions[ele.type];
+            return {
+              contentType: ele.type,
+              typeOptions: typeOptions.value,
+              name: ele.multiName || [],
+              nameOptions,
+              id: nanoid(5),
+            };
+          })) ||
+        [];
+      await nextTick();
+      await setTableDataMulti(tableDataMulti.value);
+    }
+    let resData = {} as any;
+    // resData = {
+    //   recordTime: data.isSingle ? data.content?.recordTime : '',
+    //   name: data.isSingle ? useUser.getUserInfo?.nickname,
+    //   type: type.value,
+    // };
+    resData = {
+      recordTime: dayjs(data.updateTime).format('YYYY-MM-DD'),
+      name: data.updatorName,
+      type: type.value,
+    };
+    // 首次透析方式
+    if (type.value == DiagnosisEnum.firstDialysis_field) {
+      resData.firstDialysisType = data.content[0].contentType;
+      resData.firstDialysisDate = dayjs().format('YYYY-MM-DD');
+      resData.recordTime = dayjs(data.content[0].recordTime).format('YYYY-MM-DD');
+    }
+    await setFieldsValue({
+      ...resData,
+    });
+  });
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+    baseColProps: {
+      span: 12,
+    },
+    wrapperCol: {
+      span: 22,
+    },
+  });
+  const useTbaleObj = {
+    rowKey: 'id',
+    showIndexColumn: false,
+    bordered: true,
+    striped: false,
+    pagination: false,
+    maxHeight: 200,
+    actionColumn: {
+      width: 40,
+      title: '操作',
+      dataIndex: 'action',
+    },
+  };
+  // 过敏史 allergic
+  const [
+    registerTable,
+    { setTableData: setTableDataAllergic, getDataSource: getDataSourceAllergic },
+  ] = useTable({
+    ...useTbaleObj,
+    dataSource: tableDataAllergic.value,
+    columns: columnsAllergic,
+  });
+  // 传染病 contagious
+  const [
+    registerTableContagious,
+    { setTableData: setTableDataContagious, getDataSource: getDataSourceContagious },
+  ] = useTable({
+    ...useTbaleObj,
+    dataSource: tableDataContagious.value,
+    columns: columnsContagious,
+  });
+  const [
+    registerTableOperation,
+    { setTableData: setTableDataOperation, getDataSource: getDataSourceOperation },
+  ] = useTable({
+    ...useTbaleObj,
+    dataSource: tableDataOperation.value,
+    columns: columnsOperation,
+  });
+  // 治疗前合并症 contagious
+  const [
+    registerTableMulti,
+    { setTableData: setTableDataMulti, getDataSource: getDataSourceMulti },
+  ] = useTable({
+    ...useTbaleObj,
+    dataSource: tableDataMulti.value || [],
+    columns: columnsMulti,
+  });
+  // 其他
+  const [
+    registerTableOther,
+    { setTableData: setTableDataOther, getDataSource: getDataSourceOther },
+  ] = useTable({
+    ...useTbaleObj,
+    dataSource: tableDataOther.value,
+    columns: columnsOther,
+  });
+
+  async function handleAdd() {
+    if (type.value == DiagnosisEnum.allergic_field) {
+      tableDataAllergic.value.push({
+        contentType: '',
+        typeOptions: typeOptions.value,
+        name: [],
+        nameOptions: [],
+        id: nanoid(5),
+      });
+      console.log(
+        '🚀 ~ file: FormModal.vue:135 ~ handleAdd ~ tableDataAllergic.value:',
+        tableDataAllergic.value,
+      );
+      await nextTick();
+      await setTableDataAllergic(tableDataAllergic.value);
+    }
+    if (type.value == DiagnosisEnum.operation_field) {
+      tableDataOperation.value.push({
+        singleName: '',
+        recordTime: dayjs().format('YYYY-MM-DD'),
+        remark: '',
+        id: nanoid(5),
+      });
+      console.log(
+        '🚀 ~ file: FormModal.vue:135 ~ handleAdd ~ tableDataOperation.value:',
+        tableDataOperation.value,
+      );
+      await nextTick();
+      await setTableDataOperation(tableDataOperation.value);
+    }
+    if (type.value == DiagnosisEnum.contagious_field) {
+      tableDataContagious.value.push({
+        contentType: '',
+        startTime: '',
+        endTime: '',
+        status: 'contagious_status_alive',
+        remark: '',
+        id: nanoid(5),
+      });
+      console.log(
+        '🚀 ~ file: FormModal.vue:135 ~ handleAdd ~ tableDataAllergic.value:',
+        tableDataContagious.value,
+      );
+      await nextTick();
+      await setTableDataContagious(tableDataContagious.value);
+    }
+    if (
+      type.value == DiagnosisEnum.complications_field ||
+      type.value == DiagnosisEnum.clinic_field ||
+      type.value == DiagnosisEnum.pathological_field ||
+      type.value == DiagnosisEnum.ckd_field
+    ) {
+      tableDataMulti.value.push({
+        contentType: '',
+        typeOptions: typeOptions.value,
+        name: [],
+        nameOptions: [],
+        id: nanoid(5),
+      });
+      console.log(
+        '🚀 ~ file: FormModal.vue:135 ~ handleAdd ~ tableDataAllergic.value:',
+        tableDataMulti.value,
+      );
+      await nextTick();
+      await setTableDataMulti(tableDataMulti.value);
+    }
+    if (type.value == DiagnosisEnum.elseRemark_field) {
+      tableDataOther.value.push({
+        remark: '',
+        id: nanoid(5),
+      });
+      console.log(
+        '🚀 ~ file: FormModal.vue:135 ~ handleAdd ~ tableDataAllergic.value:',
+        tableDataOther.value,
+      );
+      await nextTick();
+      await setTableDataOther(tableDataOther.value);
+    }
+  }
+
+  async function handleDel(record) {
+    console.log('🚀 ~ file: FormModal.vue:488 ~ handleDel ~ record:', record);
+    let data = [];
+    let index = null;
+    if (type.value == DiagnosisEnum.operation_field) {
+      data = getDataSourceOperation();
+      index = data.findIndex(item => item.id === record.id);
+      tableDataOperation.value.splice(index, 1);
+      setTableDataOperation(tableDataOperation.value);
+    }
+    if (type.value == DiagnosisEnum.allergic_field) {
+      data = getDataSourceAllergic();
+      index = data.findIndex(item => item.id === record.id);
+      tableDataAllergic.value.splice(index, 1);
+      setTableDataAllergic(tableDataAllergic.value);
+    }
+    if (type.value == DiagnosisEnum.contagious_field) {
+      data = getDataSourceContagious();
+      index = data.findIndex(item => item.id === record.id);
+      tableDataContagious.value.splice(index, 1);
+      setTableDataContagious(tableDataContagious.value);
+    }
+    if (type.value == DiagnosisEnum.elseRemark_field) {
+      data = getDataSourceOther();
+      index = data.findIndex(item => item.id === record.id);
+      tableDataOther.value.splice(index, 1);
+      setTableDataOther(tableDataOther.value);
+    }
+    if (!isSingle.value && type.value != DiagnosisEnum.elseRemark_field) {
+      data = getDataSourceMulti();
+      index = data.findIndex(item => item.id === record.id);
+      tableDataMulti.value.splice(index, 1);
+      setTableDataMulti(tableDataMulti.value);
+    }
+  }
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      const sendData = {
+        content: [],
+        multiContent: [],
+        id: isSingle.value ? '' : rowId.value,
+        patientBasicId: patientBasicId.value,
+        type: type.value,
+      };
+      setModalProps({ confirmLoading: true });
+      // 首次透析
+      if (type.value == DiagnosisEnum.firstDialysis_field) {
+        sendData.content = [
+          {
+            recordTime: values.firstDialysisDate,
+            contentType: values.firstDialysisType,
+          },
+        ];
+      }
+      // 手术史
+      if (type.value == DiagnosisEnum.operation_field) {
+        sendData.content = tableDataOperation.value.map(ele => {
+          return {
+            singleName: ele.singleName,
+            recordTime: ele.recordTime,
+            remark: ele.remark,
+          };
+        });
+      }
+      // 过敏史
+      if (type.value == DiagnosisEnum.allergic_field) {
+        sendData.content = tableDataAllergic.value.map(ele => {
+          return {
+            contentType: ele.contentType,
+            multiName: ele.name,
+          };
+        });
+      }
+      // 传染病史
+      if (type.value == DiagnosisEnum.contagious_field) {
+        sendData.content = tableDataContagious.value.map(ele => {
+          return {
+            contentType: ele.contentType,
+            status: ele.status,
+            startTime: ele.startTime,
+            endTime: ele.endTime,
+            remark: ele.remark,
+          };
+        });
+      }
+      // 合并症类型、临床诊断类型、病理类型、CKD类型 编辑(添加)
+      if (
+        type.value == DiagnosisEnum.complications_field ||
+        type.value == DiagnosisEnum.clinic_field ||
+        type.value == DiagnosisEnum.pathological_field ||
+        type.value == DiagnosisEnum.ckd_field
+      ) {
+        sendData.multiContent = tableDataMulti.value.map(ele => {
+          return {
+            type: ele.contentType,
+            multiName: ele.name,
+            remark: '',
+          };
+        });
+      }
+      // 其他编辑(添加)
+      if (type.value == DiagnosisEnum.elseRemark_field) {
+        sendData.multiContent = tableDataOther.value.map(ele => {
+          return {
+            type: '',
+            multiName: [],
+            remark: ele.remark,
+          };
+        });
+      }
+      console.log('🚀 ~ file: FormModal.vue:528 ~ handleSubmit ~ sendData:', sendData);
+      unref(isSingle)
+        ? await archivesDiagnosisHistorySingleAddOrEdit(sendData)
+        : await archivesDiagnosisHistoryMultiAddOrEdit(sendData);
+      createMessage.success(`${rowId.value || isSingle.value ? '编辑' : '新增'}成功!`);
+      closeModal();
+      emit('success');
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+  // 回调
+  async function callEditChange({ record }) {
+    console.log('🚀 ~ file: FormModal.vue:164 ~ callAllergicChange ~ data:', record);
+  }
+</script>
+<style lang="less" scoped>
+  ::v-deep(.ant-btn-link) {
+    color: rgb(61 65 85 / 100%);
+  }
+
+  ::v-deep(.ant-table-body) {
+    height: 200px !important;
+  }
+</style>

+ 374 - 0
src/views/biz/archives/diagnosisHistory/data.ts

@@ -0,0 +1,374 @@
+import { listDictModel } from '@/api/common';
+import { FormSchema } from '@/components/Form';
+import { BasicColumn } from '@/components/Table';
+
+// 枚举类型
+export enum DiagnosisEnum {
+  firstDialysis = '首次透析',
+  firstDialysis_field = 'firstDialysis',
+  operation = '手术史',
+  operation_field = 'operation',
+  allergic = '过敏史',
+  allergic_field = 'allergic',
+  contagious = '传染病史',
+  contagious_field = 'contagious',
+  complications = '治疗前合并症',
+  complications_field = 'complications',
+  clinic = '临床诊断',
+  clinic_field = 'clinic',
+  pathological = '病理诊断',
+  pathological_field = 'pathological',
+  ckd = 'CKD/AKI',
+  ckd_field = 'ckd',
+  elseRemark = '其他',
+  elseRemark_field = 'elseRemark',
+}
+
+// 表单新增编辑
+export const dataFormSchema: FormSchema[] = [
+  {
+    label: 'type',
+    field: 'type',
+    component: 'PlainText',
+    show: false,
+  },
+  {
+    label: '记录日期',
+    field: 'recordTime',
+    component: 'PlainText',
+    // required: true,
+    // component: 'DatePicker',
+    // componentProps: {
+    //   format: 'YYYY-MM-DD',
+    //   placeholder: '请输入记录日期',
+    //   getPopupContainer: () => document.body,
+    //   valueFormat: 'YYYY-MM-DD',
+    //   disabled: true,
+    // },
+  },
+  {
+    label: '记录人',
+    field: 'name',
+    component: 'PlainText',
+  },
+  {
+    label: '首次透析时间',
+    field: 'firstDialysisDate',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      placeholder: '请输入记录日期',
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD',
+    },
+    ifShow: ({ values }) => {
+      return values.type == DiagnosisEnum.firstDialysis_field;
+    },
+  },
+  {
+    label: '首次透析方式',
+    field: 'firstDialysisType',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'dt',
+      },
+      getPopupContainer: () => document.body,
+    },
+    ifShow: ({ values }) => {
+      return values.type == DiagnosisEnum.firstDialysis_field;
+    },
+  },
+  // {
+  //   label: '备注',
+  //   field: 'remark',
+  //   required: true,
+  //   component: 'InputTextArea',
+  //   componentProps: {
+  //     placeholder: '请输入备注',
+  //   },
+  //   colProps: {
+  //     span: 24,
+  //   },
+  //   ifShow: ({ values }) => {
+  //     return values.type == DiagnosisEnum.elseRemark_field;
+  //   },
+  // },
+];
+
+export const columnsAllergic: BasicColumn[] = [
+  {
+    title: '过敏名称数组',
+    dataIndex: 'nameOptions',
+    ifShow: false,
+  },
+  {
+    title: '诊断类型字典默认值',
+    dataIndex: 'typeOptions',
+    ifShow: false,
+  },
+  {
+    title: '过敏原',
+    dataIndex: 'contentType',
+    width: 150,
+    edit: true,
+    editable: true,
+    editComponent: 'Select',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: ({ record }) => {
+      return {
+        // api: listDictModel,
+        // params: {
+        //   // dictCode: 'dh_allergic',
+        //   dictCode: record.typeDict,
+        // },
+        options: record.typeOptions,
+        showSearch: true,
+        placeholder: '请选择',
+        getPopupContainer: () => document.body,
+        onChange: async data => {
+          console.log('🚀 ~ onChange file: data.ts:99 ~ data:', data);
+          const res = await listDictModel({ dictCode: data });
+          // console.log('🚀 ~ file: data.ts:99 ~ res:', res);
+          record.nameOptions = res;
+          record.name = [];
+        },
+      };
+    },
+  },
+  {
+    title: '名称',
+    dataIndex: 'name',
+    width: 220,
+    edit: true,
+    editable: true,
+    // editComponent: 'ApiSelect',
+    // // 默认必填校验
+    // editRule: true,
+    // editComponentProps: ({ record }) => {
+    //   // console.log('🚀 ~ file: data.ts:103 ~ record:', record);
+    //   return {
+    //     api: listDictModel,
+    //     params: {
+    //       dictCode: record.contentType,
+    //     },
+    //     mode: 'multiple',
+    //     placeholder: '请选择',
+    //   };
+    // },
+    editComponent: 'Select',
+    editComponentProps({ record }) {
+      return {
+        options: record.nameOptions,
+        mode: 'multiple',
+        showSearch: true,
+      };
+    },
+  },
+];
+export const columnsOperation: BasicColumn[] = [
+  {
+    title: '手术时间',
+    dataIndex: 'recordTime',
+    width: 120,
+    edit: true,
+    editable: true,
+    editComponent: 'DatePicker',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      placeholder: '请选择时间',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    title: '名称',
+    dataIndex: 'singleName',
+    width: 100,
+    edit: true,
+    editable: true,
+    editComponent: 'Input',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      placeholder: '请输入手术名称',
+    },
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+    width: 200,
+    edit: true,
+    editable: true,
+    editComponent: 'Input',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      placeholder: '请输入备注',
+    },
+  },
+];
+export const columnsContagious: BasicColumn[] = [
+  {
+    title: '诊断名称',
+    dataIndex: 'contentType',
+    width: 90,
+    edit: true,
+    editable: true,
+    editComponent: 'ApiSelect',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'pb_epidemic',
+      },
+      placeholder: '请选择',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    title: '开始时间',
+    dataIndex: 'startTime',
+    width: 100,
+    edit: true,
+    editable: true,
+    editComponent: 'DatePicker',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      placeholder: '请选择时间',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    title: '结束时间',
+    dataIndex: 'endTime',
+    width: 100,
+    edit: true,
+    editable: true,
+    editComponent: 'DatePicker',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      placeholder: '请选择时间',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    title: '当前状态',
+    dataIndex: 'status',
+    width: 90,
+    edit: true,
+    editable: true,
+    editComponent: 'ApiSelect',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'dh_contagious_status',
+      },
+      placeholder: '请选择',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+    width: 100,
+    edit: true,
+    editable: true,
+    editComponent: 'Input',
+  },
+];
+
+export const columnsMulti: BasicColumn[] = [
+  {
+    title: '名称数组',
+    dataIndex: 'nameOptions',
+    ifShow: false,
+  },
+  {
+    title: '诊断类型字典默认值',
+    dataIndex: 'typeDict',
+    ifShow: false,
+  },
+  {
+    title: '诊断类型',
+    dataIndex: 'contentType',
+    width: 100,
+    edit: true,
+    editable: true,
+    editComponent: 'Select',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: ({ record }) => {
+      return {
+        options: record.typeOptions,
+        placeholder: '请选择',
+        getPopupContainer: () => document.body,
+        onChange: async data => {
+          console.log('🚀 ~ onChange file: data.ts:99 ~ data:', data);
+          const res = await listDictModel({ dictCode: data });
+          // console.log('🚀 ~ file: data.ts:99 ~ res:', res);
+          record.nameOptions = res;
+          record.name = [];
+        },
+      };
+    },
+  },
+  {
+    title: '名称',
+    dataIndex: 'name',
+    width: 220,
+    edit: true,
+    editable: true,
+    editComponent: 'Select',
+    editComponentProps({ record }) {
+      return {
+        options: record.nameOptions,
+        mode: 'multiple',
+      };
+    },
+  },
+  // {
+  //   title: '备注',
+  //   dataIndex: 'remark',
+  //   width: 220,
+  //   edit: true,
+  //   editable: true,
+  //   editComponent: 'Input',
+  //   editComponentProps: {
+  //     placeholder: '请输入备注',
+  //   },
+  //   ifShow(column) {
+  //     console.log('🚀 ~ file: data.ts:299 ~ ifShow ~ column:', column);
+  //     return true;
+  //   },
+  // },
+];
+
+export const columnsOther: BasicColumn[] = [
+  {
+    title: '诊断结果',
+    dataIndex: 'remark',
+    width: 220,
+    edit: true,
+    editable: true,
+    editComponent: 'Input',
+    editComponentProps: {
+      placeholder: '请输入诊断结果',
+    },
+  },
+];

+ 394 - 0
src/views/biz/archives/diagnosisHistory/index.vue

@@ -0,0 +1,394 @@
+<template>
+  <div class="mb-4">
+    <!-- <DescCard
+      id="1"
+      icon="icon-xt-add_default"
+      title="首次透析"
+      type="touxi"
+      :data="descData"
+      :right="descRight"
+      class="mb-4"
+    /> -->
+    <div v-for="(item, index) in singleData" :key="item.type" class="mb-4">
+      <DescCard
+        :id="index"
+        icon=""
+        :title="item['title']"
+        :type="item['type']"
+        :data="item['data']"
+        :right="item['right']"
+        @edit="callEdit"
+        @delete="callDel"
+      />
+    </div>
+    <div v-for="(item, index) in multiData" :key="item.type" class="mb-4">
+      <DescMultiCard
+        :id="index"
+        icon="icon-xt-add_default"
+        :title="item['title']"
+        :type="item['type']"
+        :data="item['data']"
+        @edit="callEditMulti"
+        @delete="callDel"
+        @icon="callIcon"
+      />
+    </div>
+    <FormModal @register="registerModal" @success="callSuccess" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { DescCard, DescMultiCard } from '/@/components/XTCard/index';
+  import { useModal } from '/@/components/Modal';
+  import FormModal from './FormModal.vue';
+  import { onMounted, reactive, ref } from 'vue';
+  import { formatDictColor, formatDictFontColor, formatDictValue } from '/@/utils';
+  import dayjs from 'dayjs';
+  import {
+    archivesDiagnosisHistoryQueryListSingle,
+    archivesDiagnosisHistoryQueryListMulti,
+    archivesDiagnosisHistoryMultiRemoveByIds,
+  } from '@/api/biz/archives/diagnosisHistoryApi';
+  import { listDictModelBatch } from '@/api/common';
+  import { DiagnosisEnum } from './data';
+  import { nanoid } from 'nanoid';
+  import { useUserStore } from '@/store/modules/user';
+  import { useMessage } from '@/hooks/web/useMessage';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    } as any,
+  });
+  const bizDictOptions = reactive({
+    gender: [],
+    dt: [],
+    dh_allergic: [],
+    allergic_food: [],
+    allergic_touch: [],
+    allergic_air: [],
+    allergic_inject: [],
+    dh_contagious_status: [],
+    pb_epidemic: [],
+    dh_complications: [],
+    complications_breath: [],
+    complications_blood: [],
+    complications_incretion: [],
+  });
+  const bizDictData = ref([
+    { key: 'gender', dictCode: 'pb_sex' },
+    { key: 'dt', dictCode: 'dt' },
+    { key: 'dh_allergic', dictCode: 'dh_allergic' },
+    { key: 'allergic_food', dictCode: 'allergic_food' },
+    { key: 'allergic_touch', dictCode: 'allergic_touch' },
+    { key: 'allergic_air', dictCode: 'allergic_air' },
+    { key: 'allergic_inject', dictCode: 'allergic_inject' },
+    { key: 'dh_contagious_status', dictCode: 'dh_contagious_status' },
+    { key: 'pb_epidemic', dictCode: 'pb_epidemic' },
+    { key: 'dh_complications', dictCode: 'dh_complications' },
+    { key: 'complications_breath', dictCode: 'complications_breath' },
+    { key: 'complications_blood', dictCode: 'complications_blood' },
+    { key: 'complications_incretion', dictCode: 'complications_incretion' },
+    { key: 'dh_clinic', dictCode: 'dh_clinic' },
+    { key: 'clinic_breath', dictCode: 'clinic_breath' },
+    { key: 'clinic_heart', dictCode: 'clinic_heart' },
+    { key: 'clinic_blood', dictCode: 'clinic_blood' },
+    { key: 'clinic_hbgr', dictCode: 'clinic_hbgr' },
+    { key: 'dh_pathological', dictCode: 'dh_pathological' },
+    { key: 'pathological_breath', dictCode: 'pathological_breath' },
+    { key: 'pathological_heart', dictCode: 'pathological_heart' },
+    { key: 'pathological_blood', dictCode: 'pathological_blood' },
+    { key: 'pathological_hbgr', dictCode: 'pathological_hbgr' },
+    { key: 'dh_ckd', dictCode: 'dh_ckd' },
+    { key: 'ckd_breath', dictCode: 'ckd_breath' },
+    { key: 'ckd_heart', dictCode: 'ckd_heart' },
+    { key: 'ckd_blood', dictCode: 'ckd_blood' },
+    { key: 'ckd_hbgr', dictCode: 'ckd_hbgr' },
+  ]);
+  const [registerModal, { openModal }] = useModal();
+  const useUser = useUserStore();
+  const { createConfirm, createMessage } = useMessage();
+  onMounted(async () => {
+    const res = await listDictModelBatch(bizDictData.value.map(ele => ele.dictCode));
+    for (const i in res) {
+      const filter = bizDictData.value.filter(ele => ele.dictCode == i)[0];
+      bizDictOptions[filter.key] = res[i];
+    }
+    await getData();
+  });
+  const singleData = ref({});
+  const multiData = ref({});
+  const originSingleData = ref({});
+  const originMultiData = ref({});
+
+  // const descData = [
+  //   {
+  //     label: '诊断名称/病史/状态',
+  //     value: '乙肝 / 2022-01-19 ~',
+  //     tags: [{ id: '12', label: '活动中', type: 'error' }],
+  //   },
+  //   {
+  //     label: '备注',
+  //     value: '药物治疗或其他治疗',
+  //   },
+  //   {
+  //     label: '测试',
+  //     value: '药物治疗或其他治疗',
+  //   },
+  //   {
+  //     label: '诊断名称/病史/状态备份',
+  //     value: '结核 / 2022-02-10 ~ 20',
+  //     tags: [
+  //       { id: '1', label: '未活动', type: 'muted' },
+  //       { id: '2', label: '未活动1', type: 'primary' },
+  //     ],
+  //   },
+  // ];
+  // const descRight = {
+  //   show: true,
+  //   date: '2023-04-23',
+  //   doctor: '张医生',
+  //   edit: true,
+  //   delete: true,
+  // };
+  // 获取数据
+  async function getData() {
+    const res = await archivesDiagnosisHistoryQueryListSingle(props.info?.id);
+    originSingleData.value = res;
+    for (const i in res) {
+      // console.log('res ', i, res[i]);
+      const detail = [];
+      // if (res[i] == null) {
+      //   singleData.value[i] = {
+      //     title: DiagnosisEnum[i],
+      //     data: [],
+      //     right: {
+      //       show: false,
+      //       date: '',
+      //       doctor: '',
+      //     },
+      //     type: i,
+      //   };
+      //   continue;
+      // }
+      res[i] &&
+        res[i].content.forEach(ele => {
+          if (i == DiagnosisEnum.firstDialysis_field) {
+            detail.push({
+              label: DiagnosisEnum.firstDialysis + '日期/方式',
+              value: dayjs(ele.recordTime).format('YYYY-MM-DD'),
+              tags: [
+                {
+                  id: nanoid(),
+                  label: formatDictValue(bizDictOptions.dt, ele.contentType),
+                  type: 'success',
+                  // fontColor: formatDictFontColor(bizDictOptions.dt, ele.contentType),
+                  // bgColor: formatDictColor(bizDictOptions.dt, ele.contentType),
+                },
+              ],
+            });
+          }
+          if (i == DiagnosisEnum.operation_field) {
+            detail.push({
+              label: DiagnosisEnum.operation + '日期/名称',
+              value: dayjs(ele.recordTime).format('YYYY-MM-DD'),
+              tags: [
+                {
+                  id: nanoid(),
+                  label: ele.singleName,
+                  type: 'muted',
+                  // fontColor: formatDictFontColor(bizDictOptions.dt, ele.contentType),
+                  // bgColor: formatDictColor(bizDictOptions.dt, ele.contentType),
+                },
+              ],
+            });
+            detail.push({
+              label: '备注',
+              value: ele.remark || '——',
+            });
+          }
+          if (i == DiagnosisEnum.allergic_field) {
+            detail.push({
+              label: '过敏原/名称',
+              value: formatDictValue(bizDictOptions.dh_allergic, ele.contentType),
+              tags: ele.multiName.map(mEle => {
+                return {
+                  id: nanoid(),
+                  label: formatDictValue(bizDictOptions[ele.contentType], mEle),
+                  // type: 'warning',
+                  fontColor: formatDictFontColor(bizDictOptions.dh_allergic, ele.contentType),
+                  bgColor: formatDictColor(bizDictOptions.dh_allergic, ele.contentType),
+                };
+              }),
+            });
+          }
+          if (i == DiagnosisEnum.contagious_field) {
+            detail.push({
+              label: '诊断名称/病史/状态',
+              value: `${formatDictValue(bizDictOptions.pb_epidemic, ele.contentType)}/${dayjs(
+                ele.startTime,
+              ).format('YYYY-MM-DD')} ~ ${
+                ele.endTime ? dayjs(ele.endTime).format('YYYY-MM-DD') : ''
+              }`,
+              tags: [
+                {
+                  id: nanoid(),
+                  label: formatDictValue(bizDictOptions.dh_contagious_status, ele.status),
+                  // type: ele.status == 'contagious_status_stop' ? 'muted' : 'error',
+                  fontColor: formatDictFontColor(bizDictOptions.dh_contagious_status, ele.status),
+                  bgColor: formatDictColor(bizDictOptions.dh_contagious_status, ele.status),
+                },
+              ],
+            });
+            detail.push({
+              label: '备注',
+              value: ele.remark || '——',
+            });
+          }
+        });
+      singleData.value[i] = {
+        title: DiagnosisEnum[i],
+        data: res[i] ? detail : [],
+        right: {
+          show: res[i] ? true : false,
+          date: res[i] ? dayjs(res[i].updateTime).format('YYYY-MM-DD') : '',
+          doctor: res[i] ? res[i]?.updatorName : '',
+          edit: true,
+        },
+        type: i,
+      };
+    }
+    console.log('🚀 ~ file: index.vue:63 ~ getData ~ singleData.value:', singleData.value);
+    // 下面五个
+    const resMulti = await archivesDiagnosisHistoryQueryListMulti(props.info?.id);
+    originMultiData.value = resMulti;
+    for (const i in resMulti) {
+      // console.log('resMulti ', i, resMulti[i]);
+      if (i == DiagnosisEnum.complications_field) {
+        setMultiData(i, resMulti[i], 'dh_complications');
+      } else if (i == DiagnosisEnum.clinic_field) {
+        setMultiData(i, resMulti[i], 'dh_clinic');
+      } else if (i == DiagnosisEnum.pathological_field) {
+        setMultiData(i, resMulti[i], 'dh_pathological');
+      } else if (i == DiagnosisEnum.ckd_field) {
+        setMultiData(i, resMulti[i], 'dh_ckd');
+      } else {
+        setMultiData(i, resMulti[i], '', '诊断结果');
+      }
+    }
+    // console.log('🚀 ~ file: index.vue:243 ~ getData ~ multiData.value:', multiData.value);
+  }
+  // 设置 过敏原,合并症类型、临床诊断类型、病理类型、CKD类型、其他编辑 数据
+  function setMultiData(i, record, dict?: string, str?: string) {
+    const detailMulti = [];
+    record &&
+      record.forEach(ele => {
+        detailMulti.push({
+          data: ele.multiContent.map(mEle => {
+            return {
+              label: str || '诊断类型/名称',
+              value: mEle.type ? formatDictValue(bizDictOptions[dict], mEle.type) : mEle.remark,
+              span: mEle.type ? 8 : 8,
+              tags:
+                mEle.type &&
+                mEle.multiName.map(mmEle => {
+                  return {
+                    id: nanoid(),
+                    label: formatDictValue(bizDictOptions[mEle.type], mmEle),
+                    // type: 'warning',
+                    fontColor: formatDictFontColor(bizDictOptions[dict], mEle.type),
+                    bgColor: formatDictColor(bizDictOptions[dict], mEle.type),
+                  };
+                }),
+            };
+          }),
+          right: {
+            show: true,
+            date: dayjs(ele.updateTime).format('YYYY-MM-DD'),
+            doctor: ele.updatorName,
+            edit: useUser.getUserInfo.nickname == ele.updatorName ? true : false,
+            delete: useUser.getUserInfo.nickname == ele.updatorName ? true : false,
+          },
+          id: ele.id,
+        });
+      });
+    multiData.value[i] = {
+      title: DiagnosisEnum[i],
+      data: record == null ? [] : detailMulti,
+      type: DiagnosisEnum[i + '_field'],
+    };
+  }
+
+  // 回调
+  // single
+  function callEdit(data) {
+    console.log('🚀 ~ file: index.vue:181 ~ callEdit ~ data:', data);
+    // openModal(true, singleData.value[data.type]);
+    openModal(true, {
+      ...originSingleData.value[data.type],
+      title: singleData.value[data.type]?.title,
+      dictOptions:
+        bizDictOptions[
+          data.type == DiagnosisEnum.contagious_field ? 'dh_contagious_status' : `dh_${data.type}`
+        ],
+      patientBasicId: props.info?.id,
+      isSingle: true,
+    });
+  }
+  function callEditMulti(data) {
+    console.log('🚀 ~ file: index.vue:181 ~ callEdit ~ data:', data);
+    const filterData =
+      originMultiData.value[data.type].filter(ele => {
+        return ele.id == data.data.id;
+      })[0] || [];
+    openModal(true, {
+      ...filterData,
+      title: multiData.value[data.type]?.title,
+      id: data.data.id,
+      dictOptions:
+        bizDictOptions[
+          data.type == DiagnosisEnum.contagious_field ? 'dh_contagious_status' : `dh_${data.type}`
+        ],
+      patientBasicId: props.info?.id,
+      isSingle: false,
+    });
+  }
+  // multi
+  async function callDel(data) {
+    console.log('🚀 ~ file: index.vue:181 ~ callDel ~ data:', data);
+    const id = data.data.id || '';
+    createConfirm({
+      content: '你确定要删除?',
+      iconType: 'warning',
+      onOk: async () => {
+        await archivesDiagnosisHistoryMultiRemoveByIds([id]);
+        createMessage.success('删除成功');
+        await getData();
+      },
+    });
+  }
+  // multi add
+  function callIcon(data) {
+    console.log('🚀 ~ file: index.vue:295 ~ callIcon ~ data:', data);
+    openModal(true, {
+      type: data.type,
+      updateTime: dayjs().format('YYYY-MM-DD'),
+      updatorName: useUser.getUserInfo.nickname,
+      multiContent: [],
+      title: multiData.value[data.type]?.title,
+      id: '',
+      dictOptions:
+        bizDictOptions[
+          data.type == DiagnosisEnum.contagious_field ? 'dh_contagious_status' : `dh_${data.type}`
+        ],
+      patientBasicId: props.info?.id,
+      isSingle: false,
+    });
+  }
+  async function callSuccess() {
+    await getData();
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 93 - 0
src/views/biz/archives/formula/FormDrawer.vue

@@ -0,0 +1,93 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerDrawer"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <BasicForm @register="registerForm" layout="vertical" class="!px-6 !pt-4" />
+  </BasicDrawer>
+</template>
+<script lang="ts" setup>
+  import { ref, reactive, unref } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchemaVascularAccess } from './data';
+  import {
+    archivesVascularAccessDetail,
+    archivesVascularAccessEdit,
+    archivesVascularAccessAdd,
+  } from '/@/api/biz/archives/vascularAccessApi';
+  import { listDictModel } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = ref(`血管通路`);
+  const width = '70%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const patientBasicId = ref();
+  const bizDictOptions = reactive({
+    accessType: [],
+  });
+  const accessType = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    schemas: dataFormSchemaVascularAccess,
+    showActionButtonGroup: false,
+    baseColProps: {
+      span: 6,
+    },
+  });
+  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async data => {
+    console.log('🚀 ~ file: FormDrawer.vue:49 ~ data:', data);
+    await resetFields();
+    setDrawerProps({ confirmLoading: false });
+    bizDictOptions.accessType = await listDictModel({ dictCode: 'va_type' });
+    isUpdate.value = !!data?.isUpdate;
+    patientBasicId.value = data.record.id;
+    if (unref(isUpdate)) {
+      const resData = await archivesVascularAccessDetail(patientBasicId.value);
+      rowId.value = resData.id;
+      resData.accessType = resData.type;
+      accessType.value = formatDictValue(bizDictOptions.accessType, resData.type);
+      await setFieldsValue({
+        ...resData,
+      });
+    } else {
+      accessType.value = formatDictValue(bizDictOptions.accessType, data.record.accessType);
+      await setFieldsValue({
+        accessType: data.record.accessType,
+      });
+    }
+    getTitle.value = `${isUpdate.value ? '编辑' : '新建'}${accessType.value}   ( ${
+      data.record.name
+    } | ${data.record.gender} | ${data.record.age}岁 )`;
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setDrawerProps({ confirmLoading: true });
+      values.patientBasicId = patientBasicId.value;
+      !unref(isUpdate)
+        ? await archivesVascularAccessAdd({ ...values })
+        : await archivesVascularAccessEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeDrawer();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setDrawerProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 133 - 0
src/views/biz/archives/formula/data.ts

@@ -0,0 +1,133 @@
+import { FormSchema } from '@/components/Form';
+import { listDictModel } from '/@/api/common';
+
+// 通路转归 Form 表单
+export const dataFormSchemaVascularAccess: FormSchema[] = [
+  {
+    label: '血管通路',
+    field: 'puncture',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_type',
+      },
+    },
+  },
+  {
+    label: '透析模式',
+    field: 'puncture',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'dt',
+      },
+    },
+  },
+  {
+    label: '制定日期',
+    field: 'setUpTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      placeholder: '请输入制定日期',
+    },
+  },
+  {
+    field: 'PlainTitle',
+    component: 'PlainTitle',
+    defaultValue: '体征',
+    colProps: {
+      span: 24,
+    },
+  },
+  {
+    label: '干体重 (kg)',
+    field: 'tizhong',
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入干体重',
+    },
+  },
+  {
+    label: 'CH值 (kg)',
+    field: 'tizhong',
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入干体重',
+    },
+  },
+  {
+    label: 'V值 (kg)',
+    field: 'tizhong',
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入干体重',
+    },
+  },
+  {
+    field: 'PlainTitle',
+    component: 'PlainTitle',
+    defaultValue: '抗凝',
+    colProps: {
+      span: 24,
+    },
+  },
+  {
+    label: '抗凝方式',
+    field: 'tizhong',
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入抗凝方式',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'idCard',
+    label: '首剂',
+    component: 'ApiInputDict',
+    componentProps: ({ formModel }) => {
+      return {
+        placeholder: '请输入异常内容',
+        api: listDictModel,
+        params: {
+          dictCode: 'sys_dict_type',
+          dictSort: false,
+        },
+        onChange: e => {
+          console.log('🚀 ~ file: data.ts:81 ~ e:', e);
+          return (formModel['idCard'] = e);
+        },
+      };
+    },
+  },
+  {
+    label: '追加 (mg)',
+    field: 'tizhong',
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入追加',
+    },
+  },
+  {
+    field: 'PlainTitle',
+    component: 'PlainTitle',
+    defaultValue: '耗材',
+    colProps: {
+      span: 24,
+    },
+  },
+  {
+    field: 'PlainTitle',
+    component: 'PlainTitle',
+    defaultValue: '透析参数',
+    colProps: {
+      span: 24,
+    },
+  },
+];

+ 183 - 0
src/views/biz/archives/formula/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div>
+    <div class="mx-6 mb-2">
+      <TimeLine :data="timeLineData" @hover="callHover" @icon="callIcon">
+        <template #head>
+          <transition class="animate__animated animate__slideInLeft">
+            <div class="timeline-outer" v-if="timeOuter">
+              <div class="timeline-outer_item" @click="handleAdd"> 新建透析处方模板 </div>
+            </div>
+          </transition>
+        </template>
+      </TimeLine>
+    </div>
+    <FormDrawer @register="registerDrawer" @success="callSuccess" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, reactive, onUnmounted } from 'vue';
+  import TimeLine from '/@/components/XTTimeLine/src/TimeLine.vue';
+  import { onMounted } from 'vue';
+  import dayjs from 'dayjs';
+  import { listDictModelBatch } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+  //
+  import FormDrawer from './FormDrawer.vue';
+
+  import { useDrawer } from '@/components/Drawer';
+  import { archivesFormulaTemplateQueryList } from '@/api/biz/archives/formulaTemplateApi';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    },
+  });
+  const bizDictOptions = reactive<any>({});
+  const timeLineData = ref([]);
+  const timeOuter = ref(false);
+  onMounted(async () => {
+    const res = await listDictModelBatch(bizDictData.value.map(ele => ele.dictCode));
+    for (const i in res) {
+      const filter = bizDictData.value.filter(ele => ele.dictCode == i)[0];
+      bizDictOptions[filter.key] = res[i];
+    }
+    console.log('🚀 ~ file: index.vue:47 ~ onMounted ~ bizDictOptions:', bizDictOptions);
+    // console.log('🚀 ~ file: index.vue:96 ~ onMounted ~ res:', res);
+    await getData();
+  });
+  const [registerDrawer, { openDrawer }] = useDrawer();
+  const bizDictData = ref([
+    // 透析模式
+    { key: 'dt', dictCode: 'dt' },
+    // 通路类型
+    { key: 'type', dictCode: 'va_type' },
+  ]);
+
+  async function getData() {
+    const res = await archivesFormulaTemplateQueryList(props.info?.id);
+    console.log('🚀 ~ file: index.vue:105 ~ getData ~ res:', res);
+    timeLineData.value = res.map(ele => {
+      const innerData = {
+        title: '基本信息',
+        icon: 'icon-xt-edit_default',
+        type: 'basic',
+        data: [
+          { field: 'vascularAccess', label: '血管通路', value: '', dict: true },
+          { field: 'setUpHospital', label: '干体重 (kg)', value: '' },
+          { field: 'setUpHospital', label: 'CH值 (kg)', value: '' },
+          { field: 'setUpHospital', label: 'V值 (kg)', value: '' },
+          { field: 'setUpHospital', label: '首剂 (mg)', value: '' },
+          { field: 'setUpHospital', label: '追加 (mg)', value: '' },
+          { field: 'setUpHospital', label: '抗凝方式', value: '', span: 12 },
+          { field: 'setUpHospital', label: '耗材使用', value: '', span: 24 },
+          { field: 'setUpHospital', label: '透析时长 (h)', value: '' },
+          { field: 'setUpHospital', label: '流量 (ml/min)', value: '' },
+          { field: 'setUpHospital', label: '湿度 (℃)', value: '' },
+          { field: 'setUpHospital', label: '血流量 (ml/min)', value: '' },
+          { field: 'setUpHospital', label: 'K (mmolL)', value: '' },
+          { field: 'setUpHospital', label: 'Na (mmolL)', value: '' },
+          { field: 'setUpHospital', label: 'Ca (mmolL)', value: '' },
+          { field: 'setUpHospital', label: '碳酸氢根 (mmolL)', value: '' },
+          { field: 'setUpHospital', label: 'Mg (mmolL)', value: '' },
+          { field: 'setUpHospital', label: 'Cl (mmolL)', value: '' },
+          { field: 'setUpHospital', label: '葡萄糖 (mmolL)', value: '' },
+        ],
+      };
+      innerData.title = formatDictValue(bizDictOptions.dt, ele.dialysisType);
+      console.log('🚀 ~ file: index.vue:114 ~ getData ~ innerData:', innerData);
+      innerData.type = ele.dialysisType;
+      innerData.data = innerData.data.map(eleC => {
+        if (eleC.dict) {
+          if (eleC.field == 'vascularAccess') {
+            eleC.value = formatDictValue(bizDictOptions['type'], ele[eleC.field]);
+          } else {
+            eleC.value = formatDictValue(bizDictOptions[eleC.field], ele[eleC.field]);
+          }
+        } else {
+          eleC.value = ele[eleC.field];
+        }
+        // if (ele.type == 'va_avf' || ele.type == 'va_avg') {
+        //   // 置管引导
+        //   eleC.hidden = eleC.field == 'catheterGuidance' ? true : false;
+        // }
+        return eleC;
+      });
+      // console.log('🚀 ~ file: index.vue:168 ~ getData ~ cnt:', innerData);
+      return {
+        id: ele.id,
+        dot: ele.updatorName,
+        date: dayjs(ele.enactedTime).format('YYYY-MM-DD'),
+        cnt: innerData,
+      };
+    });
+    console.log('🚀 ~ file: index.vue:168 ~ getData ~ timeLineData.value :', timeLineData.value);
+  }
+  function handleAdd(data) {
+    console.log('🚀 ~ file: index.vue:205 ~ handleAdd ~ data:', data);
+    openDrawer(true, {
+      record: { ...props.info, accessType: data },
+    });
+  }
+  onUnmounted(() => {
+    timeOuter.value = false;
+  });
+  // 回调
+  function callSuccess(data) {
+    console.log('🚀 ~ file: index.vue:212 ~ callSuccess ~ data:', data);
+  }
+  function callHover() {
+    timeOuter.value = true;
+  }
+  function callIcon(data) {
+    openDrawer(true, {
+      isUpdate: true,
+      record: { ...props.info, ...data },
+    });
+  }
+</script>
+
+<style lang="less" scoped>
+  .timeline-outer {
+    display: flex;
+    margin-left: 20px;
+    transition: all 0.3s ease-in-out;
+
+    &_item {
+      padding: 0 30px;
+      height: 40px;
+      line-height: 40px;
+      border-radius: 30px;
+      color: #fff;
+      background: #0075ff;
+      margin-right: 20px;
+      cursor: pointer;
+      letter-spacing: 2px;
+    }
+  }
+
+  ::v-deep(.ant-picker) {
+    width: 100%;
+  }
+
+  .ant-dropdown-link {
+    display: block;
+    margin-right: 16px;
+    background: #fff;
+    border-radius: 50%;
+    width: 32px;
+    height: 32px;
+    text-align: center;
+    line-height: 32px;
+    cursor: pointer;
+
+    &:last-child {
+      margin-right: 0;
+    }
+
+    &:hover {
+      color: #0075ff;
+    }
+  }
+</style>

+ 0 - 7
src/views/biz/archives/index.vue

@@ -1,7 +0,0 @@
-<template>
-  <div> 占位符 </div>
-</template>
-
-<script setup lang="ts"></script>
-
-<style lang="less" scoped></style>

+ 75 - 0
src/views/biz/archives/index/FormDrawerSift.vue

@@ -0,0 +1,75 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerDrawer"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+    cancelText=""
+    okText="筛选"
+    :showCancelBtn="false"
+  >
+    <BasicForm @register="registerForm" layout="vertical" class="!px-6 !pt-2" />
+    <template #insertFooter>
+      <a-button @click="handleCancel" class="mr-2"> 重置 </a-button>
+    </template>
+  </BasicDrawer>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { siftFormSchema } from './data';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = ref(`筛选条件`);
+  const width = '25%';
+  const [registerForm, { setFieldsValue, resetFields, validate, getFieldsValue }] = useForm({
+    schemas: siftFormSchema,
+    showActionButtonGroup: false,
+    baseColProps: {
+      span: 24,
+    },
+  });
+  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async data => {
+    await resetFields();
+    setDrawerProps({ confirmLoading: false });
+    console.log('🚀 ~ file: FormDrawer.vue:49 ~ data:', data);
+    const resData = {} as any;
+    (data.record.length &&
+      data.record.map(ele => {
+        console.log('🚀 ~ file: FormDrawerSift.vue:46 ~ ele:', ele);
+        resData[ele.field] = ele.value;
+      })) ||
+      [];
+    console.log('🚀 ~ file: FormDrawerSift.vue:44 ~ resData:', resData);
+    await setFieldsValue({ ...resData });
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setDrawerProps({ confirmLoading: true });
+      closeDrawer();
+      emit('success', values);
+    } finally {
+      setDrawerProps({ confirmLoading: false });
+    }
+  }
+
+  async function handleCancel() {
+    console.log('关闭', await getFieldsValue());
+    await resetFields();
+  }
+</script>
+<style lang="less" scoped>
+  ::v-deep(.ant-picker) {
+    border-top: 0;
+    border-left: 0;
+    border-right: 0;
+  }
+</style>

+ 61 - 5
src/views/biz/archives/index/data.ts

@@ -1,5 +1,6 @@
 import { BasicColumn } from '@/components/Table';
 import { FormSchema } from '/@/components/Form';
+import { listDictModel } from '@/api/common';
 
 export const BasicTab = [
   {
@@ -39,21 +40,76 @@ export const BasicTab = [
 export const BasicTabActive = BasicTab[0].key;
 
 // 抽屉搜索条件
-export const searchFormSchema: FormSchema[] = [
+export const siftFormSchema: FormSchema[] = [
   {
     label: '性别',
-    field: 'name',
-    component: 'Input',
+    field: 'gender',
+    component: 'ApiRadioGroup',
     componentProps: {
-      placeholder: '请输入性别',
+      api: listDictModel,
+      params: {
+        dictCode: 'pb_sex',
+      },
     },
   },
   {
     label: '年龄',
     field: 'code',
-    component: 'Input',
+    component: 'Slider',
     componentProps: {
       placeholder: '请输入年龄',
+      max: 99,
+      min: 1,
+      range: true,
+      tooltipPlacement: 'bottom',
+      tooltipVisible: true,
+      tipFormatter: (value: number) => {
+        return `${value}岁`;
+      },
+      marks: {
+        1: '1岁',
+        99: '99岁',
+      },
+    },
+  },
+  {
+    label: '首次透析日期',
+    field: 'firstDialysisTime',
+    component: 'RangePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    label: '手术史',
+    field: 'operationTime',
+    component: 'RangePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    label: '传染病开始日期范围',
+    field: 'contagiousStartTime',
+    component: 'RangePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    label: '传染病结束日期范围',
+    field: 'contagiousEndTime',
+    component: 'RangePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      getPopupContainer: () => document.body,
     },
   },
 ];

+ 311 - 0
src/views/biz/archives/index/index.vue

@@ -0,0 +1,311 @@
+<template>
+  <div class="m-4">
+    <div>
+      <XTTitle title="透析病历" :right-data="titleData" @click="callTitleClick" />
+      <div class="flex justify-between my-4">
+        <XTTab
+          type="illness"
+          :width="136"
+          :selected="activeKey"
+          :data="tabData"
+          @item-click="callTab"
+        />
+        <XTForm :form-data="formData" @change="callFormChange" @click="callFormClick" />
+      </div>
+      <div class="flex mb-2" v-if="siftData.length">
+        <Sift :data="siftData" @close="callClose" />
+      </div>
+      <BasicTable @register="registerTable">
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'birthday'">
+            {{ dayjs(record.birthday).format('YYYY-MM-DD') }}
+          </template>
+          <template v-if="column.key === 'firstDialysisTime'">
+            {{ dayjs(record.firstDialysisTime).format('YYYY-MM-DD') || '' }}
+          </template>
+          <template v-if="column.key === 'gender'">
+            {{ formatDictValue(bizDictOptions.gender, record.gender) }}
+          </template>
+          <template v-if="column.key === 'type'">
+            {{ formatDictValue(bizDictOptions.type, record.type) }}
+          </template>
+          <template v-if="column.key === 'infectiousDiseases'">
+            <div>
+              {{ formatDictValue(bizDictOptions.infectiousDiseases, record.infectiousDiseases) }}
+            </div>
+          </template>
+          <template v-if="column.key === 'vascularAccess'">
+            {{ formatDictValue(bizDictOptions.vascularAccess, record.vascularAccess) }}
+          </template>
+          <template v-if="column.key === 'patientReturn'">
+            {{ formatDictValue(bizDictOptions.patientReturn, record.patientReturn) }}
+          </template>
+          <template v-if="column.key === 'action'">
+            <TableAction
+              :actions="[
+                {
+                  auth: 'sys:log:query',
+                  icon: 'icon-eye|iconfont',
+                  tooltip: '详情',
+                  label: '详情',
+                  onClick: handleView.bind(null, record),
+                },
+              ]"
+            />
+          </template>
+        </template>
+      </BasicTable>
+    </div>
+    <FormModal @register="registerModal" @success="callSuccess" />
+    <FormDrawerSift @register="registerDrawer" @success="callSift" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { XTTitle } from '/@/components/XTTitle/index';
+  import { XTTab } from '/@/components/XTTab/index';
+  import { XTForm } from '/@/components/XTForm/index';
+  import { Sift } from '/@/components/XTList/index';
+  import { BasicTable, useTable, TableAction } from '/@/components/TableCard';
+  import { BasicTab, BasicTabActive, columns, siftFormSchema } from './data';
+  import { ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import {
+    archivesPatientBasicQueryPage,
+    archivesPatientBasicStats,
+  } from '/@/api/biz/archives/patientBasicApi';
+  import { listDictModelBatch } from '@/api/common';
+  import { formatDictValue } from '/@/utils';
+  import { onMounted, reactive } from 'vue';
+  import dayjs from 'dayjs';
+  import { useModal } from '/@/components/Modal';
+  import FormModal from '../patientBasic/FormModal.vue';
+  // 筛选条件
+  import FormDrawerSift from './FormDrawerSift.vue';
+  import { useDrawer } from '@/components/Drawer';
+
+  const bizDictOptions = reactive<any>({});
+  const bizDictData = ref([
+    // 血管材料
+    { key: 'gender', dictCode: 'pb_sex' },
+    // 转归类型
+    { key: 'patientReturn', dictCode: 'pb_return' },
+    // 传染病
+    { key: 'infectiousDiseases', dictCode: 'pb_epidemic' },
+    // 患者类型
+    { key: 'type', dictCode: 'pb_type' },
+    // 通路类型
+    { key: 'vascularAccess', dictCode: 'va_type' },
+    // 转归原因
+    { key: 'return', dictCode: 'va_return' },
+  ]);
+  // 路由跳转
+  const router = useRouter();
+  const activeKey = ref(BasicTabActive);
+  const tabData = ref(BasicTab);
+  const [registerModal, { openModal }] = useModal();
+  const [registerDrawer, { openDrawer }] = useDrawer();
+
+  onMounted(async () => {
+    const res = await listDictModelBatch(bizDictData.value.map(ele => ele.dictCode));
+    for (const i in res) {
+      const filter = bizDictData.value.filter(ele => ele.dictCode == i)[0];
+      bizDictOptions[filter.key] = res[i];
+    }
+    const stats = await archivesPatientBasicStats();
+    console.log('🚀 ~ file: index.vue:104 ~ onMounted ~ stats:', stats);
+    tabData.value = tabData.value.map(ele => {
+      if (ele.key == '0') {
+        ele.value = stats.all;
+      }
+      if (ele.key == '1') {
+        ele.value = stats.newPatient;
+      }
+      if (ele.key == '2') {
+        ele.value = stats.noneFormulate;
+      }
+      if (ele.key == '3') {
+        ele.value = stats.positive;
+      }
+      return ele;
+    });
+    console.log('🚀 ~ file: index.vue:118 ~ onMounted ~ tabData.value:', tabData.value);
+  });
+  const [registerTable, { reload }] = useTable({
+    api: archivesPatientBasicQueryPage,
+    exportAuthList: ['sys:log:export'],
+    rowKey: 'id',
+    columns,
+    showIndexColumn: true,
+    bordered: true,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    afterFetch: handleAfterFetch,
+  });
+  // 筛选数据
+  const siftData = ref([]);
+  // 标题数据
+  const titleData = [
+    {
+      type: 'import',
+      icon: 'icon-xt-import_default',
+    },
+    {
+      type: 'export',
+      icon: 'icon-xt-export_default',
+    },
+    {
+      type: 'add',
+      btnIcon: 'icon-xt-add_default',
+      btnText: '患者建档',
+    },
+  ];
+
+  const formData = ref([
+    {
+      name: 'name',
+      componentType: 'Input',
+      placeholder: '请输入患者姓名',
+      prefix: 'icon-xt-search',
+      width: 240,
+    },
+    {
+      name: 'filter',
+      componentType: 'IconBtn',
+      count: 0,
+    },
+  ]);
+
+  const formValue = reactive({
+    name: '',
+  }) as any;
+
+  // const tableSort = ref([
+  //   {
+  //     field: 'create_time',
+  //     direction: 'DESC',
+  //   },
+  // ]) as any;
+  // 表格请求之前,对参数进行处理, 添加默认 排序
+  function handleBeforeFetch(params) {
+    // return { ...params, orders: tableSort.value };
+    const sift = {};
+    siftData.value.forEach(ele => {
+      sift[ele.field] = ele.isDict ? ele.dict : ele.value;
+    });
+    return {
+      ...params,
+      queryType: activeKey.value == '0' ? '0' : activeKey.value,
+      name: formValue.name,
+      ...sift,
+    };
+  }
+
+  function handleAfterFetch(data) {
+    return data;
+  }
+
+  // 详情按钮事件
+  function handleView(record: Recordable) {
+    router.push({
+      path: '/bizArchives/detail',
+      query: {
+        id: record.id,
+        accessId: record.accessId,
+        name: record.name,
+        gender: formatDictValue(bizDictOptions.gender, record.gender),
+        age: dayjs().diff(record.birthday, 'year'),
+      },
+    });
+  }
+
+  // 弹窗回调事件
+  async function callSuccess({ isUpdate, values }) {
+    console.log(isUpdate);
+    console.log(values);
+    await reload();
+  }
+  // 回调
+  async function callTab(data) {
+    activeKey.value = data.value;
+    await reload();
+  }
+
+  function callTitleClick(data) {
+    if (data.type == 'add') {
+      openModal(true, {
+        isUpdate: false,
+        record: data,
+      });
+    }
+  }
+
+  async function callFormChange(data) {
+    formValue.name = data.name ? data.name : '';
+    await reload();
+  }
+
+  async function callFormClick(data) {
+    if (data.name == 'filter') {
+      const record = [];
+      siftData.value.forEach(ele => {
+        const obj = {
+          field: ele.field,
+          value: ele.value,
+        } as any;
+        if (ele.isDict) {
+          obj.value = ele.dict;
+        }
+        record.push(obj);
+      });
+      openDrawer(true, {
+        record,
+      });
+    }
+  }
+  // 筛选条件回调
+  async function callSift(data) {
+    siftData.value = [];
+    for (const i in data) {
+      if (data[i]) {
+        siftFormSchema.forEach(ele => {
+          // console.log('🚀 ~ file: index.vue:280 ~ obj ~ ele:', ele);
+          if (ele.field == i) {
+            siftData.value.push({
+              field: ele.field,
+              label: ele.label,
+              value: ele.component.includes('Api')
+                ? formatDictValue(bizDictOptions.gender, data[i])
+                : data[i],
+              isDict: ele.component.includes('Api'),
+              dict: ele.component.includes('Api') ? data[i] : '',
+            });
+          }
+          formData.value[formData.value.length - 1]['count'] = siftData.value.length;
+        });
+      }
+    }
+    await reload();
+  }
+
+  async function callClose(data) {
+    if (data.type == 'clear') {
+      console.log('清空全部');
+      siftData.value = [];
+    }
+    if (data.type == 'close') {
+      console.log('删除部分条件');
+      siftData.value = siftData.value.filter(ele => {
+        return ele.field != data.item?.field;
+      });
+    }
+    formData.value[formData.value.length - 1]['count'] = siftData.value.length;
+    await reload();
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 90 - 0
src/views/biz/archives/patientBasic/FormModal.vue

@@ -0,0 +1,90 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <div class="!pl-8 !pt-4">
+      <BasicForm @register="registerForm" layout="vertical" />
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchema } from './data';
+
+  import {
+    archivesPatientBasicAdd,
+    archivesPatientBasicEdit,
+    archivesPatientBasicDetail,
+  } from '/@/api/biz/archives/patientBasicApi';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增基础病历' : '编辑基础病历'));
+  const width = '45%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+    baseColProps: {
+      span: 12,
+    },
+    wrapperCol: {
+      span: 22,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+
+    if (unref(isUpdate)) {
+      const resData = await archivesPatientBasicDetail(data.record.id);
+      rowId.value = resData.id;
+      resData.cardNo = {
+        input: resData.cardNo,
+        dictValue: resData.cardType,
+      };
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      const cardInfo = values.cardNo;
+      values.cardNo = cardInfo?.input;
+      values.cardType = cardInfo?.dictValue;
+      !unref(isUpdate)
+        ? await archivesPatientBasicAdd({ ...values })
+        : await archivesPatientBasicEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 200 - 0
src/views/biz/archives/patientBasic/data.ts

@@ -0,0 +1,200 @@
+import { listDictModel } from '@/api/common';
+import { FormSchema } from '@/components/Form';
+
+export const BasicData = [
+  {
+    title: '基本信息',
+    icon: 'icon-xt-edit_default',
+    type: 'basic',
+    data: [
+      { field: 'name', label: '姓名', value: '' },
+      { field: 'gender', label: '性别', value: '', dict: true },
+      { field: 'age', label: '年龄', value: '' },
+      { field: 'height', label: '身高(cm)', value: '' },
+      { field: 'uniqueNo', label: '编号', value: '' },
+      { field: 'createTime', label: '建档时间', value: '' },
+      { field: 'cardNo', label: '证件号码', value: '', dictName: 'cardType', dictField: 'label' },
+      { field: 'birthday', label: '出生日期', value: '' },
+      { field: 'type', label: '类型', value: '', dict: true },
+      {
+        field: 'infectiousDiseases',
+        label: '传染病',
+        value: '',
+        tags: [],
+        dict: true,
+        hiddenValue: 'true',
+      },
+      { field: 'bloodType', label: '血型', value: '', dict: true },
+      { field: 'address', label: '详细地址', value: '' },
+      { field: 'mobile', label: '联系电话', value: '' },
+      { field: 'familyName', label: '家属姓名', value: '' },
+      { field: 'familyMobile', label: '家属电话', value: '' },
+    ],
+  },
+  {
+    title: '血管通路',
+    type: 'vc',
+    data: [
+      { field: 'type', label: '当前血管通路', value: '', dict: true },
+      { field: 'setUpTime', label: '建立时间', value: '' },
+    ],
+  },
+  {
+    title: '诊断记录',
+    type: 'record',
+    data: [
+      {
+        field: 'first',
+        label: '首次透析',
+        value: '2022-01-19 ~',
+        tags: [{ id: '12', label: '活动中', type: 'error' }],
+      },
+      {
+        field: 'first',
+        label: '临床诊断',
+        value: '临床诊断结果',
+        tags: [{ id: '12', label: '活动中', type: 'error' }],
+      },
+    ],
+  },
+];
+
+// 表单新增编辑
+export const dataFormSchema: FormSchema[] = [
+  {
+    label: '患者姓名',
+    field: 'name',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入患者姓名',
+    },
+  },
+  {
+    label: '性别',
+    field: 'gender',
+    required: true,
+    component: 'ApiRadioGroup',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'pb_sex',
+      },
+    },
+  },
+  {
+    label: '证件号码',
+    field: 'cardNo',
+    required: true,
+    component: 'ApiInputDict',
+    componentProps: ({ formModel }) => {
+      return {
+        placeholder: '请输入证件号码',
+        api: listDictModel,
+        params: {
+          dictCode: 'pb_card',
+          dictSort: true,
+        },
+        onChange: e => {
+          return (formModel['cardNo'] = e);
+        },
+      };
+    },
+  },
+  {
+    label: '出生日期',
+    field: 'birthday',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      placeholder: '请输入出生日期',
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    label: '身高(cm)',
+    field: 'height',
+    required: true,
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入身高',
+      min: 0,
+      max: 999,
+    },
+  },
+  {
+    label: '详细地址',
+    field: 'address',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入详细地址',
+    },
+  },
+  {
+    label: '联系电话',
+    field: 'mobile',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入联系电话',
+    },
+  },
+  {
+    label: '类型',
+    field: 'type',
+    required: true,
+    component: 'ApiRadioGroup',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'pb_type',
+      },
+    },
+  },
+  {
+    label: '血型',
+    field: 'bloodType',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'pb_blood',
+      },
+    },
+  },
+  {
+    label: '传染病',
+    field: 'infectiousDiseases',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      mode: 'multiple',
+      params: {
+        dictCode: 'pb_epidemic',
+      },
+    },
+  },
+  {
+    label: '家属姓名',
+    field: 'familyName',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入家属姓名',
+    },
+  },
+  {
+    label: '家属电话',
+    field: 'familyMoblie',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入家属电话',
+    },
+  },
+];

+ 122 - 0
src/views/biz/archives/patientBasic/index.vue

@@ -0,0 +1,122 @@
+<template>
+  <div>
+    <div class="mx-2 mb-6" v-for="item in basicData" :key="item.type">
+      <DescCard
+        :icon="item.icon"
+        :title="item.title"
+        :type="item.type"
+        :data="item.data"
+        @icon="callIcon"
+        :id="info.id"
+      />
+    </div>
+    <FormModal @register="registerModal" @success="callSuccess" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, reactive } from 'vue';
+  import DescCard from '/@/components/XTCard/src/DescCard.vue';
+  import { BasicData } from './data';
+  import { onMounted } from 'vue';
+  import { useModal } from '/@/components/Modal';
+  import FormModal from './FormModal.vue';
+  import { archivesPatientBasicDetail } from '/@/api/biz/archives/patientBasicApi';
+  import { archivesVascularAccessQueryCurrent } from '/@/api/biz/archives/vascularAccessApi';
+  import { listDictModelBatch } from '/@/api/common';
+  import { formatDictTags, formatDictValue } from '/@/utils';
+  import dayjs from 'dayjs';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    } as any,
+  });
+  const bizDictOptions = reactive({
+    gender: [],
+    cardType: [],
+    bloodType: [],
+    type: [],
+    infectiousDiseases: [],
+    accessType: [],
+  });
+  const bizDictData = ref([
+    { key: 'gender', dictCode: 'pb_sex' },
+    { key: 'cardType', dictCode: 'pb_card' },
+    { key: 'bloodType', dictCode: 'pb_blood' },
+    { key: 'type', dictCode: 'pb_type' },
+    { key: 'infectiousDiseases', dictCode: 'pb_epidemic' },
+    { key: 'accessType', dictCode: 'va_type' },
+  ]);
+  onMounted(async () => {
+    const res = await listDictModelBatch(bizDictData.value.map(ele => ele.dictCode));
+    for (const i in res) {
+      const filter = bizDictData.value.filter(ele => ele.dictCode == i)[0];
+      bizDictOptions[filter.key] = res[i];
+    }
+    await getData();
+  });
+  const [registerModal, { openModal }] = useModal();
+  const basicData = ref(BasicData);
+  // 获取数据
+  async function getData() {
+    const res = await archivesPatientBasicDetail(props.info?.id);
+    basicData.value[0].data = basicData.value[0].data.map(ele => {
+      if (ele.dict) {
+        ele.value = formatDictValue(bizDictOptions[ele.field], res[ele.field]);
+      } else {
+        ele.value = res[ele.field];
+      }
+      if (ele.dictField) {
+        ele[ele.dictField] = formatDictValue(bizDictOptions[ele.dictName], res[ele.dictName]);
+      }
+      if (Array.isArray(ele.tags)) {
+        if (res[ele.field]?.length) {
+          const value = res[ele.field].map(v => {
+            return {
+              value: v,
+            };
+          });
+          ele.tags = formatDictTags(bizDictOptions[ele.field], value);
+          ele.value = ele.hiddenValue ? '' : res[ele.field];
+        }
+      }
+
+      if (ele.field == 'age') {
+        ele.value = dayjs().diff(res['birthday'], 'year');
+      }
+      if (ele.field == 'birthday') {
+        ele.value = dayjs(res['birthday']).format('YYYY-MM-DD');
+      }
+      return ele;
+    });
+
+    const vascularAccessRes = await archivesVascularAccessQueryCurrent(props.info?.id);
+    basicData.value[1].data =
+      (vascularAccessRes &&
+        basicData.value[1].data.map(ele => {
+          if (ele.field == 'type') {
+            ele.value = formatDictValue(bizDictOptions['accessType'], vascularAccessRes[ele.field]);
+          } else {
+            ele.value = vascularAccessRes[ele.field];
+          }
+          return ele;
+        })) ||
+      [];
+  }
+  // 回调
+  function callIcon(data) {
+    console.log('🚀 ~ file: index.vue:16 ~ callIcon ~ data:', data);
+    openModal(true, {
+      isUpdate: true,
+      record: data,
+    });
+  }
+
+  function callSuccess(data) {
+    console.log('🚀 ~ file: index.vue:39 ~ handleSuccess ~ data:', data);
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 79 - 0
src/views/biz/archives/patientReturn/FormModal.vue

@@ -0,0 +1,79 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <div class="!px-4 !pt-4">
+      <BasicForm @register="registerForm" layout="vertical" />
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchema } from './data';
+
+  import {
+    archivesPatientReturnAdd,
+    archivesPatientReturnEdit,
+    archivesPatientReturnDetail,
+  } from '/@/api/biz/archives/patientReturnApi';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增转归记录' : '编辑转归记录'));
+  const width = '35%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const patientBasicId = ref('');
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    patientBasicId.value = data.record?.patientBasicId;
+    if (unref(isUpdate)) {
+      const resData = await archivesPatientReturnDetail(data.record.id);
+      rowId.value = resData.id;
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      values.patientBasicId = patientBasicId.value;
+      setModalProps({ confirmLoading: true });
+      !unref(isUpdate)
+        ? await archivesPatientReturnAdd({ ...values })
+        : await archivesPatientReturnEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 68 - 0
src/views/biz/archives/patientReturn/data.ts

@@ -0,0 +1,68 @@
+import { listDictModel } from '/@/api/common';
+import { BasicColumn, FormSchema } from '/@/components/Table';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '日期',
+    dataIndex: 'returnTime',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '转归类型',
+    dataIndex: 'type',
+    align: 'left',
+    width: 200,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+    align: 'left',
+  },
+  {
+    title: '记录人',
+    dataIndex: 'updatorName',
+    align: 'left',
+    width: 120,
+  },
+];
+
+// 表单新增编辑
+export const dataFormSchema: FormSchema[] = [
+  {
+    label: '转归日期',
+    field: 'returnTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: () => {
+      return {
+        placeholder: '请输入转归日期',
+        getPopupContainer: () => document.body,
+        valueFormat: 'YYYY-MM-DD',
+      };
+    },
+  },
+  {
+    label: '转归类型',
+    field: 'type',
+    required: true,
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'pb_return',
+        dictSort: true,
+      },
+      getPopupContainer: () => document.body,
+      placeholder: '请输入转归类型',
+    },
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入备注',
+    },
+  },
+];

+ 190 - 0
src/views/biz/archives/patientReturn/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div>
+    <div class="flex">
+      <div>
+        <a-button type="primary" size="large" class="btn-add" @click="handleCreate">
+          添加转归
+        </a-button>
+      </div>
+    </div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'type'">
+          {{ formatDictValue(bizDictOptions.type, record.type) }}
+        </template>
+        <template v-if="column.key === 'returnTime'">
+          {{ dayjs(record.returnTime).format('YYYY-MM-DD') }}
+        </template>
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                auth: 'archives:patientReturn:edit',
+                icon: 'icon-xt-details_edit_default|iconfont',
+                tooltip: '编辑',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: 'archives:patientReturn:remove',
+                icon: 'icon-xt-details_delete_default|iconfont',
+                tooltip: '删除',
+                popConfirm: {
+                  title: '是否取消删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record, column),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <FormModal @register="registerModal" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { reactive, ref } from 'vue';
+
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormModal from './FormModal.vue';
+  import { columns } from './data';
+
+  import {
+    archivesPatientReturnQueryPage,
+    archivesPatientReturnRemove,
+  } from '/@/api/biz/archives/patientReturnApi';
+  import { listDictModel } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+  import { useModal } from '/@/components/Modal';
+  import { onBeforeMount } from 'vue';
+  import dayjs from 'dayjs';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    } as any,
+  });
+  const bizDictOptions = reactive({
+    type: [],
+  });
+  onBeforeMount(async () => {
+    bizDictOptions.type = await listDictModel({ dictCode: 'pb_return' });
+  });
+  const { createConfirm, createMessage } = useMessage();
+  // const [registerModal, { openModal }] = useModal();
+  const [registerModal, { openModal }] = useModal();
+
+  const tableSort = ref([
+    {
+      field: 'create_time',
+      direction: 'ASC',
+    },
+  ]) as any;
+
+  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+    api: archivesPatientReturnQueryPage,
+    rowKey: 'id',
+    columns,
+    showIndexColumn: false,
+    useSearchForm: false,
+    bordered: false,
+    striped: false,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    pagination: false,
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+      record: { patientBasicId: props.info.id },
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record: { patientBasicId: props.info.id, ...record },
+      isUpdate: true,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    if (record) {
+      await archivesPatientReturnRemove([record.id]);
+      createMessage.success('删除成功!');
+      await reload();
+    } else {
+      createConfirm({
+        content: '你确定要删除?',
+        iconType: 'warning',
+        onOk: async () => {
+          const keys = getSelectRowKeys();
+          await archivesPatientReturnRemove(keys);
+          createMessage.success('删除成功!');
+          await reload();
+        },
+      });
+    }
+  }
+  // 表格点击字段排序
+  function handleSortFn(sortInfo) {
+    if (sortInfo?.order && sortInfo?.columnKey) {
+      // 默认单列排序
+      tableSort.value = [
+        {
+          field: sortInfo.columnKey,
+          direction: sortInfo.order.replace(/(\w+)(end)/g, '$1').toUpperCase(),
+        },
+      ];
+    }
+  }
+
+  // 表格请求之前,对参数进行处理, 添加默认 排序
+  function handleBeforeFetch(params) {
+    return { ...params, orders: tableSort.value, patientBasicId: props.info?.id };
+  }
+
+  // 弹窗回调事件
+  async function handleSuccess({ isUpdate, values }) {
+    console.log(isUpdate);
+    console.log(values);
+    await reload();
+  }
+</script>
+
+<style lang="less" scoped>
+  ::v-deep(.ant-btn-link) {
+    color: rgb(61 65 85 / 100%);
+  }
+
+  ::v-deep(.ant-table-tbody > tr > td) {
+    border-right: 0 !important;
+    border-left: 0 !important;
+    border-bottom: 1px solid #f0f0f0;
+  }
+
+  ::v-deep(.ant-table-wrapper table) {
+    border: 0;
+  }
+
+  ::v-deep(.ant-table-cell-fix-right-first::after, .ant-table-cell-fix-right-last::after) {
+    content: '';
+    top: auto;
+    width: 0;
+  }
+
+  .btn-add {
+    width: 120px;
+    border-radius: 4px;
+  }
+</style>

+ 83 - 0
src/views/biz/archives/patrolward/FormModal.vue

@@ -0,0 +1,83 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <div class="!px-4 !pt-4">
+      <BasicForm @register="registerForm" layout="vertical" />
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchema } from './data';
+
+  import {
+    archivesPatrolWardAdd,
+    archivesPatrolWardEdit,
+    archivesPatrolWardDetail,
+  } from '/@/api/biz/archives/patrolWardApi';
+  import dayjs from 'dayjs';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增查房' : '编辑查房'));
+  const width = '30%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const patientBasicId = ref('');
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    patientBasicId.value = data.record?.patientBasicId;
+    await setFieldsValue({
+      patrolTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+    });
+    if (unref(isUpdate)) {
+      const resData = await archivesPatrolWardDetail(data.record.id);
+      rowId.value = resData.id;
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      values.patientBasicId = patientBasicId.value;
+      !unref(isUpdate)
+        ? await archivesPatrolWardAdd({ ...values })
+        : await archivesPatrolWardEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 77 - 0
src/views/biz/archives/patrolward/data.ts

@@ -0,0 +1,77 @@
+import dayjs from 'dayjs';
+import { DescItem } from '/@/components/Description';
+import { BasicColumn, FormSchema } from '/@/components/Table';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '查房时间',
+    dataIndex: 'patrolTime',
+    width: 200,
+  },
+  {
+    title: '查房内容',
+    dataIndex: 'content',
+    align: 'left',
+  },
+  {
+    title: '记录人',
+    dataIndex: 'updatorName',
+    width: 120,
+  },
+];
+
+// 表单列定义
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: 'updator_name',
+    field: 'updatorName',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入updator_name',
+    },
+  },
+];
+// 表单新增编辑
+export const dataFormSchema: FormSchema[] = [
+  {
+    label: '查房时间',
+    field: 'patrolTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: ({ formModel }) => {
+      return {
+        placeholder: '请输入查房时间',
+        getPopupContainer: () => document.body,
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
+        showTime: true,
+        disabledDate: current => {
+          return current < dayjs(formModel.accessSetUpTime).subtract(1, 'day').endOf('day');
+        },
+      };
+    },
+  },
+  {
+    label: '记录',
+    field: 'content',
+    required: true,
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入查房内容',
+    },
+  },
+];
+// 表单详情查看
+export const viewSchema: DescItem[] = [
+  {
+    label: '查房时间',
+    field: 'patrolTime',
+  },
+  {
+    label: '查房内容',
+    field: 'content',
+  },
+  {
+    label: '记录人',
+    field: 'updatorName',
+  },
+];

+ 236 - 0
src/views/biz/archives/patrolward/index.vue

@@ -0,0 +1,236 @@
+<template>
+  <div>
+    <div class="flex justify-between mb-4">
+      <div>
+        <a-button type="primary" size="large" class="btn-add" @click="handleCreate">查房</a-button>
+      </div>
+      <div>
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                auth: 'archives:patrolWard:edit',
+                icon: 'icon-xt-details_edit_default|iconfont',
+                tooltip: '编辑',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: 'archives:patrolWard:remove',
+                icon: 'icon-xt-details_delete_default|iconfont',
+                tooltip: '删除',
+                popConfirm: {
+                  title: '是否取消删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record, column),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <FormModal @register="registerModal" @success="callSuccess" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { reactive, ref } from 'vue';
+  import { XTForm } from '/@/components/XTForm/index';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+
+  // import { useModal } from '/@/components/Modal';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormModal from './FormModal.vue';
+  // import ViewDrawer from './viewDrawer.vue';
+  import { columns } from './data';
+
+  import {
+    archivesPatrolWardQueryPage,
+    archivesPatrolWardRemove,
+  } from '/@/api/biz/archives/patrolWardApi';
+  import { useModal } from '/@/components/Modal';
+  import dayjs from 'dayjs';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    },
+  });
+
+  const { createConfirm, createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
+  const tableSort = ref([
+    {
+      field: 'create_time',
+      direction: 'DESC',
+    },
+  ]) as any;
+
+  // formdata
+  const formData = [
+    {
+      name: 'patrolTime',
+      label: '查房时间',
+      componentType: 'RangePicker',
+      placeholder: '请输入',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+      defaultValue: [
+        dayjs().subtract(3, 'month').format('YYYY-MM-DD'),
+        dayjs().format('YYYY-MM-DD'),
+      ],
+    },
+    {
+      name: 'content',
+      componentType: 'Input',
+      placeholder: '请输入',
+      prefix: 'icon-xt-search',
+      width: 280,
+    },
+  ];
+
+  const formValue = reactive({
+    patrolTime: [
+      dayjs().subtract(3, 'month').format('YYYY-MM-DD'),
+      dayjs().add(1, 'day').format('YYYY-MM-DD'),
+    ],
+    content: '',
+  });
+
+  const [registerTable, { reload, getSelectRowKeys }] = useTable({
+    api: archivesPatrolWardQueryPage,
+    rowKey: 'id',
+    columns,
+    showIndexColumn: false,
+    bordered: true,
+    striped: false,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+      record: { patientBasicId: props.info.id },
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record: { patientBasicId: props.info.id, ...record },
+      isUpdate: true,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    if (record) {
+      await archivesPatrolWardRemove([record.id]);
+      createMessage.success('删除成功!');
+      await reload();
+    } else {
+      createConfirm({
+        content: '你确定要删除?',
+        iconType: 'warning',
+        onOk: async () => {
+          const keys = getSelectRowKeys();
+          await archivesPatrolWardRemove(keys);
+          createMessage.success('删除成功!');
+          await reload();
+        },
+      });
+    }
+  }
+  // 表格点击字段排序
+  function handleSortFn(sortInfo) {
+    if (sortInfo?.order && sortInfo?.columnKey) {
+      // 默认单列排序
+      tableSort.value = [
+        {
+          field: sortInfo.columnKey,
+          direction: sortInfo.order.replace(/(\w+)(end)/g, '$1').toUpperCase(),
+        },
+      ];
+    }
+  }
+
+  // 表格请求之前,对参数进行处理, 添加默认 排序
+  function handleBeforeFetch(params) {
+    console.log({ ...params, orders: tableSort.value, patientBasicId: props.info?.id });
+    return {
+      ...params,
+      orders: tableSort.value,
+      patientBasicId: props.info?.id,
+      patrolTime: [
+        formValue.patrolTime[0],
+        dayjs(formValue.patrolTime[1]).add(1, 'day').format('YYYY-MM-DD'),
+      ],
+      content: formValue.content,
+    };
+  }
+
+  // 弹窗回调事件
+  async function callSuccess() {
+    await reload();
+  }
+
+  // 查询回调
+  async function callForm(data) {
+    formValue.content = data.content || '';
+    formValue.patrolTime = data.patrolTime || '';
+    await reload();
+  }
+</script>
+<style lang="less" scoped>
+  ::v-deep(.ant-btn-link) {
+    color: rgb(61 65 85 / 100%);
+  }
+
+  ::v-deep(.ant-input) {
+    border-color: #c2ccd4;
+    border-radius: 4px;
+  }
+
+  ::v-deep(.ant-input-affix-wrapper) {
+    border-color: #c2ccd4;
+    border-radius: 4px;
+  }
+
+  ::v-deep(.ant-picker) {
+    border-color: #c2ccd4;
+    border-radius: 4px;
+  }
+
+  ::v-deep(.ant-table-tbody > tr > td) {
+    border-right: 0 !important;
+    border-left: 0 !important;
+    border-bottom: 1px solid #f0f0f0;
+  }
+
+  ::v-deep(.ant-table-wrapper table) {
+    border: 0;
+  }
+
+  ::v-deep(.ant-table-cell-fix-right-first::after, .ant-table-cell-fix-right-last::after) {
+    content: '';
+    top: auto;
+    width: 0;
+  }
+
+  .btn-add {
+    width: 120px;
+    border-radius: 4px;
+  }
+</style>

+ 93 - 0
src/views/biz/archives/vascularAccess/FormDrawer.vue

@@ -0,0 +1,93 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerDrawer"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <BasicForm @register="registerForm" layout="vertical" class="!px-6 !pt-4" />
+  </BasicDrawer>
+</template>
+<script lang="ts" setup>
+  import { ref, reactive, unref } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchemaVascularAccess } from './data';
+  import {
+    archivesVascularAccessDetail,
+    archivesVascularAccessEdit,
+    archivesVascularAccessAdd,
+  } from '/@/api/biz/archives/vascularAccessApi';
+  import { listDictModel } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = ref(`血管通路`);
+  const width = '35%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const patientBasicId = ref();
+  const bizDictOptions = reactive({
+    accessType: [],
+  });
+  const accessType = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    schemas: dataFormSchemaVascularAccess,
+    showActionButtonGroup: false,
+    baseColProps: {
+      span: 24,
+    },
+  });
+  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async data => {
+    console.log('🚀 ~ file: FormDrawer.vue:49 ~ data:', data);
+    await resetFields();
+    setDrawerProps({ confirmLoading: false });
+    bizDictOptions.accessType = await listDictModel({ dictCode: 'va_type' });
+    isUpdate.value = !!data?.isUpdate;
+    patientBasicId.value = data.record.id;
+    if (unref(isUpdate)) {
+      const resData = await archivesVascularAccessDetail(patientBasicId.value);
+      rowId.value = resData.id;
+      resData.accessType = resData.type;
+      accessType.value = formatDictValue(bizDictOptions.accessType, resData.type);
+      await setFieldsValue({
+        ...resData,
+      });
+    } else {
+      accessType.value = formatDictValue(bizDictOptions.accessType, data.record.accessType);
+      await setFieldsValue({
+        accessType: data.record.accessType,
+      });
+    }
+    getTitle.value = `${isUpdate.value ? '编辑' : '新建'}${accessType.value}   ( ${
+      data.record.name
+    } | ${data.record.gender} | ${data.record.age}岁 )`;
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setDrawerProps({ confirmLoading: true });
+      values.patientBasicId = patientBasicId.value;
+      !unref(isUpdate)
+        ? await archivesVascularAccessAdd({ ...values })
+        : await archivesVascularAccessEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeDrawer();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setDrawerProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 99 - 0
src/views/biz/archives/vascularAccess/FormModal.vue

@@ -0,0 +1,99 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <BasicForm @register="registerForm" layout="vertical" class="!px-6 !pt-4" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, reactive, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchemaVascularAccess } from './data';
+  import {
+    archivesVascularAccessDetail,
+    archivesVascularAccessEdit,
+    archivesVascularAccessAdd,
+  } from '/@/api/biz/archives/vascularAccessApi';
+  import { listDictModel } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+  import { useUserStore } from '@/store/modules/user';
+  import dayjs from 'dayjs';
+
+  const emit = defineEmits(['success', 'register']);
+  const userStore = useUserStore();
+  const getTitle = ref(`血管通路`);
+  const width = '35%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const patientBasicId = ref();
+  const bizDictOptions = reactive({
+    accessType: [],
+  });
+  const accessType = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    schemas: dataFormSchemaVascularAccess,
+    showActionButtonGroup: false,
+    baseColProps: {
+      span: 24,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    console.log('🚀 ~ file: FormModal.vue:49 ~ data:', data);
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    bizDictOptions.accessType = await listDictModel({ dictCode: 'va_type' });
+    isUpdate.value = !!data?.isUpdate;
+    patientBasicId.value = data.record.id;
+    if (unref(isUpdate)) {
+      const resData = await archivesVascularAccessDetail(patientBasicId.value);
+      rowId.value = resData.id;
+      resData.accessType = resData.type;
+      accessType.value = formatDictValue(bizDictOptions.accessType, resData.type);
+      await setFieldsValue({
+        ...resData,
+      });
+    } else {
+      accessType.value = formatDictValue(bizDictOptions.accessType, data.record.accessType);
+      await setFieldsValue({
+        setUpHospital: userStore.getTenant?.name,
+        setUpTime: dayjs(),
+        accessType: data.record.accessType,
+      });
+    }
+    getTitle.value = `${isUpdate.value ? '编辑' : '新建'}${accessType.value}   ( ${
+      data.record.name
+    } | ${data.record.gender} | ${data.record.age}岁 )`;
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      values.patientBasicId = patientBasicId.value;
+      values.type = values.accessType;
+      delete values.accessType;
+      !unref(isUpdate)
+        ? await archivesVascularAccessAdd({ ...values })
+        : await archivesVascularAccessEdit({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 86 - 0
src/views/biz/archives/vascularAccess/FormModalReturn.vue

@@ -0,0 +1,86 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <BasicForm @register="registerForm" layout="vertical" class="!px-6 !pt-4" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, reactive, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { dataFormSchemaAccessReturn } from './data';
+  import {
+    archivesAccessReturnAdd,
+    archivesAccessReturnDetail,
+  } from '/@/api/biz/archives/accessReturnApi';
+  import { listDictModel } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+  import dayjs from 'dayjs';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = ref(`通路转归`);
+  const width = '35%';
+  const isUpdate = ref(false);
+  const rowId = ref();
+  const vascularAccessId = ref();
+  const bizDictOptions = reactive({
+    accessType: [],
+  });
+
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: dataFormSchemaAccessReturn,
+    showActionButtonGroup: false,
+    baseColProps: {
+      span: 12,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    bizDictOptions.accessType = await listDictModel({ dictCode: 'va_type' });
+    isUpdate.value = !!data?.isUpdate;
+    getTitle.value = `通路转归 ( ${data.record.name} | ${data.record.gender} | ${data.record.age}岁 )`;
+    vascularAccessId.value = data.accessId;
+    console.log('🚀 ~ file: FormModalReturn.vue:56 ~  data.record:', data.record);
+    if (unref(isUpdate)) {
+      const resData = await archivesAccessReturnDetail(vascularAccessId.value);
+      rowId.value = resData.id;
+      resData.accessType = formatDictValue(bizDictOptions.accessType, resData.accessType);
+      resData.accessSetUpTime = dayjs(resData.accessSetUpTime).format('YYYY-MM-DD');
+      resData.returnTime = resData.returnTime || dayjs().format('YYYY-MM-DD');
+      resData.useTime = dayjs(resData.returnTime).diff(resData.accessSetUpTime, 'day') + '天';
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      values.vascularAccessId = vascularAccessId.value;
+      await archivesAccessReturnAdd({ ...values, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeModal();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      setModalProps({ confirmLoading: false });
+    }
+  }
+</script>

+ 0 - 0
src/views/biz/archives/vascularAccess/ViewDrawer.vue


+ 226 - 0
src/views/biz/archives/vascularAccess/ViewDrawerComplication.vue

@@ -0,0 +1,226 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerDrawer"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+    :show-ok-btn="false"
+    cancel-text="关闭"
+  >
+    <div class="!px-4 !pt-4">
+      <div class="mb-4">
+        <a-button type="primary" shape="round" @click="handleAdd">
+          <template #icon>
+            <PlusOutlined />
+          </template>
+          添加
+        </a-button>
+      </div>
+      <div>
+        <BasicTable @register="registerTable" @edit-change="onEditChange">
+          <template #bodyCell="{ column, record }">
+            <template v-if="column.key === 'action'">
+              <TableAction :actions="createActions(record, column)" />
+            </template>
+          </template>
+        </BasicTable>
+      </div>
+    </div>
+  </BasicDrawer>
+</template>
+<script lang="ts" setup>
+  import { ref, reactive, unref, nextTick } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import {
+    BasicTable,
+    useTable,
+    TableAction,
+    EditRecordRow,
+    ActionItem,
+    BasicColumn,
+  } from '/@/components/Table';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { columnsComplication } from './data';
+  import {
+    archivesAccessComplicationAdd,
+    archivesAccessComplicationEdit,
+    archivesAccessComplicationQueryList,
+    archivesAccessComplicationRemove,
+  } from '/@/api/biz/archives/accessComplicationApi';
+  import { listDictModel } from '/@/api/common';
+  import { PlusOutlined } from '@ant-design/icons-vue';
+  import { formatDictLabel, formatDictValue } from '/@/utils';
+  import dayjs from 'dayjs';
+
+  const emit = defineEmits(['success', 'register']);
+
+  const getTitle = ref(``);
+  const width = '60%';
+  const isUpdate = ref(false);
+  const vascularAccessId = ref();
+  const bizDictOptions = reactive({
+    accessType: [],
+    vaComp: [],
+  });
+  // 表格数据
+  const tableData = ref();
+
+  const { createMessage } = useMessage();
+
+  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async data => {
+    setDrawerProps({ confirmLoading: false });
+    bizDictOptions.accessType = await listDictModel({ dictCode: 'va_type' });
+    bizDictOptions.vaComp = await listDictModel({ dictCode: 'va_comp' });
+    isUpdate.value = !!data?.isUpdate;
+    getTitle.value = `通路并发症 ( ${data.record.name} | ${data.record.gender} | ${data.record.age}岁 )`;
+    vascularAccessId.value = data.accessId;
+    await getData();
+  });
+
+  const [registerTable, { setTableData, getDataSource }] = useTable({
+    dataSource: tableData.value,
+    columns: columnsComplication,
+    autoCreateKey: true,
+    showIndexColumn: false,
+    bordered: true,
+    striped: false,
+    actionColumn: {
+      width: 100,
+      title: '操作',
+      dataIndex: 'action',
+    },
+  });
+
+  function createActions(record: EditRecordRow, column: BasicColumn): ActionItem[] {
+    if (!record.editable) {
+      return [
+        {
+          auth: 'sys:log:query',
+          icon: 'icon-xt-details_edit_default|iconfont',
+          tooltip: '编辑',
+          onClick: handleEdit.bind(null, record),
+        },
+        {
+          auth: 'sys:log:query',
+          icon: 'icon-xt-details_delete_default|iconfont',
+          tooltip: '删除',
+          popConfirm: {
+            title: '是否取消删除',
+            placement: 'left',
+            confirm: handleDel.bind(null, record, column),
+          },
+        },
+      ];
+    }
+    return [
+      {
+        label: '保存',
+        onClick: handleSave.bind(null, record, column),
+      },
+      {
+        label: '取消',
+        popConfirm: {
+          title: '是否取消编辑',
+          placement: 'left',
+          confirm: handleCancel.bind(null, record, column),
+        },
+      },
+    ];
+  }
+  // 获取数据
+  async function getData() {
+    tableData.value = await archivesAccessComplicationQueryList(vascularAccessId.value);
+    tableData.value = tableData.value.map(ele => {
+      ele.name = formatDictValue(bizDictOptions.vaComp, ele.name);
+      return ele;
+    });
+    setTableData(tableData.value);
+  }
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      setDrawerProps({ confirmLoading: true });
+      emit('success', {
+        isUpdate: unref(isUpdate),
+      });
+      closeDrawer();
+    } finally {
+      setDrawerProps({ confirmLoading: false });
+    }
+  }
+
+  async function handleAdd() {
+    const isAdd = getDataSource().filter(ele => {
+      return ele.editable;
+    });
+    if (isAdd.length) {
+      createMessage.error('请编辑完成后添加!');
+      return;
+    }
+    tableData.value.unshift({
+      occurredTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+      name: '',
+      remark: '',
+    });
+    await nextTick();
+    setTableData(tableData.value);
+    console.log('getDataSource()', getDataSource());
+    const record = getDataSource()[0];
+    record.editable = true;
+    handleEdit(record);
+  }
+
+  async function handleEdit(record) {
+    record.onEdit?.(true);
+  }
+  async function handleDel(record) {
+    const data = getDataSource();
+    const index = data.findIndex(item => item.key === record.key);
+    const res = await archivesAccessComplicationRemove([record?.id]);
+    if (res) {
+      tableData.value.splice(index, 1);
+      setTableData(tableData.value);
+    } else {
+      createMessage.error('删除失败');
+    }
+  }
+  async function handleSave(record: EditRecordRow) {
+    // console.log('🚀 ~ file: ViewDrawerComplication.vue:166 ~ handleSave ~ record:', record);
+    record.onEdit?.(false, true);
+    const data = {
+      id: record?.id || '',
+      name: record?.name || '',
+      occurredTime: record?.occurredTime || '',
+      remark: record?.remark || '',
+      vascularAccessId: vascularAccessId.value,
+    };
+    if (record?.id) {
+      data.name = formatDictLabel(bizDictOptions.vaComp, record?.name) || record?.name;
+    }
+    data.id
+      ? await archivesAccessComplicationEdit(data)
+      : await archivesAccessComplicationAdd(data);
+    createMessage.success(data.id ? '编辑成功' : '新增成功!');
+    await getData();
+  }
+  function handleCancel(record: EditRecordRow) {
+    console.log('🚀 ~ file: ViewDrawerComplication.vue:179 ~ handleCancel ~ record:', record);
+    record.onEdit?.(false);
+  }
+  function onEditChange({ column, value, record }) {
+    // 本例
+    // if (column.dataIndex === 'id') {
+    //   record.editValueRefs.name4.value = `${value}`;
+    // }
+    console.log(column, value, record);
+  }
+</script>
+<style lang="less" scoped>
+  ::v-deep(.ant-btn-link) {
+    color: rgb(61 65 85 / 100%);
+  }
+</style>

+ 333 - 0
src/views/biz/archives/vascularAccess/data.ts

@@ -0,0 +1,333 @@
+import { FormSchema } from '@/components/Form';
+import { listDictModel } from '/@/api/common';
+import dayjs from 'dayjs';
+import { BasicColumn } from '@/components/Table';
+
+export const BasicCnt = {
+  title: '基本信息',
+  icon: 'icon-xt-edit_default',
+  type: 'basic',
+  data: [
+    { field: 'setUpHospital', label: '建立医院', value: '' },
+    { field: 'puncture', label: '穿刺体侧', value: '', dict: true },
+    { field: 'location', label: '手术部位', value: '' },
+    { field: 'anastomosis', label: '吻合方式', value: '', dict: true },
+    { field: 'catheterGuidance', label: '置管引导', value: '', dict: true },
+    { field: 'vascularMaterial', label: '血管材料', value: '', hidden: true, dict: true },
+    { field: 'updatorName', label: '记录人', value: '' },
+    { field: 'accessReturn', label: '转归记录', value: '', span: 12 },
+  ],
+};
+
+export const DictCommon = {
+  basic: {
+    va_type_avf: 'va_type_avf',
+    va_type_avg: 'va_type_avg',
+    va_type_ncc: 'va_type_ncc',
+    va_type_tcc: 'va_type_tcc',
+  },
+  va_type_avf: {
+    loc: 'va_avf_loc',
+    arteries: 'va_avf_arteries',
+    vein: 'va_avf_vein',
+  },
+  va_type_avg: {
+    loc: 'va_avg_loc',
+    arteries: 'va_avg_arteries',
+    vein: 'va_avg_vein',
+  },
+  va_type_ncc: {
+    loc: 'va_ncc_loc',
+    jnjm: 'ncc_loc_jnjm',
+    sg: 'ncc_loc_sg',
+  },
+  va_type_tcc: {
+    loc: 'va_tcc_loc',
+    jnjm: 'tcc_loc_jnjm',
+    jwjm: 'tcc_loc_jwjm',
+    sg: 'tcc_loc_sg',
+  },
+};
+// 通路转归 Form 表单
+export const dataFormSchemaAccessReturn: FormSchema[] = [
+  {
+    label: '类型',
+    field: 'accessType',
+    component: 'PlainText',
+    componentProps: {
+      placeholder: '请输入类型',
+    },
+  },
+  {
+    label: '建立时间',
+    field: 'accessSetUpTime',
+    component: 'PlainText',
+    componentProps: {
+      placeholder: '请输入类型',
+    },
+  },
+  {
+    label: '转归日期',
+    field: 'returnTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: ({ formModel }) => {
+      return {
+        placeholder: '请输入转归日期',
+        getPopupContainer: () => document.body,
+        valueFormat: 'YYYY-MM-DD',
+        disabledDate: current => {
+          return current < dayjs(formModel.accessSetUpTime).subtract(1, 'day').endOf('day');
+        },
+      };
+    },
+    colProps: {
+      span: 24,
+    },
+  },
+  {
+    label: '使用时间',
+    field: 'useTime',
+    component: 'PlainText',
+    componentProps: ({ formModel }) => {
+      formModel.useTime = dayjs(formModel.returnTime).diff(formModel.accessSetUpTime, 'day') + '天';
+    },
+  },
+  {
+    label: '转归原因',
+    field: 'returnCause',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_return',
+      },
+    },
+    colProps: {
+      span: 24,
+    },
+  },
+];
+
+// 通路转归 Form 表单
+export const dataFormSchemaVascularAccess: FormSchema[] = [
+  {
+    label: '类型',
+    field: 'accessType',
+    component: 'PlainText',
+    show: false,
+    componentProps: {
+      placeholder: '请输入类型',
+    },
+    defaultValue: 'va_type_avf',
+  },
+  {
+    label: '建立时间',
+    field: 'setUpTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      placeholder: '请输入建立时间',
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD',
+      format: 'YYYY-MM-DD',
+    },
+  },
+  {
+    label: '建立医院',
+    field: 'setUpHospital',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入建立医院名称',
+    },
+  },
+  {
+    label: '血管材料(AVG)',
+    field: 'vascularMaterial',
+    required: true,
+    component: 'ApiSelect',
+    ifShow: ({ values }) => {
+      return values.accessType == DictCommon.basic.va_type_avg;
+    },
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_material',
+      },
+      placeholder: '请输入血管材料',
+    },
+  },
+  {
+    label: '穿刺体侧',
+    field: 'puncture',
+    required: true,
+    component: 'ApiRadioGroup',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_puncture',
+      },
+    },
+  },
+  {
+    label: '手术部位 (位置)',
+    field: 'location',
+    required: true,
+    component: 'ApiRadioGroup',
+    componentProps: ({ formModel }) => {
+      console.log(formModel['accessType']);
+      return {
+        api: listDictModel,
+        params: {
+          dictCode: DictCommon[formModel['accessType']]['loc'],
+        },
+      };
+    },
+  },
+  {
+    label: '手术部位 (动脉)',
+    field: 'arteries',
+    component: 'ApiRadioGroup',
+    componentProps: ({ formModel }) => {
+      return {
+        api: listDictModel,
+        params: {
+          dictCode: DictCommon[formModel['accessType']]['arteries'],
+        },
+      };
+    },
+    ifShow: ({ values }) => {
+      console.log('🚀 ~ file: data.ts:191 ~ values:', values);
+
+      return (
+        values.accessType == DictCommon.basic.va_type_avf ||
+        values.accessType == DictCommon.basic.va_type_avg
+      );
+    },
+  },
+  {
+    label: '手术部位 (静脉)',
+    field: 'vein',
+    component: 'ApiRadioGroup',
+    componentProps: ({ formModel }) => {
+      return {
+        api: listDictModel,
+        params: {
+          dictCode: DictCommon[formModel['accessType']]['vein'],
+        },
+      };
+    },
+    ifShow: ({ values }) => {
+      return (
+        values.accessType == DictCommon.basic.va_type_avf ||
+        values.accessType == DictCommon.basic.va_type_avg
+      );
+    },
+  },
+
+  {
+    label: '路径',
+    field: 'path',
+    component: 'ApiRadioGroup',
+    componentProps: ({ formModel }) => {
+      return {
+        api: listDictModel,
+        params: {
+          dictCode: formModel['location'],
+        },
+      };
+    },
+    ifShow: ({ values }) => {
+      return (
+        (values.accessType == DictCommon.basic.va_type_ncc ||
+          values.accessType == DictCommon.basic.va_type_tcc) &&
+        (values['location'] == DictCommon.va_type_ncc.jnjm ||
+          values['location'] == DictCommon.va_type_ncc.sg ||
+          values['location'] == DictCommon.va_type_tcc.jnjm ||
+          values['location'] == DictCommon.va_type_tcc.sg)
+      );
+    },
+  },
+  {
+    label: '吻合方式',
+    field: 'anastomosis',
+    required: true,
+    component: 'ApiRadioGroup',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_anastomosis',
+      },
+    },
+    ifShow: ({ values }) => {
+      console.log('🚀 ~ file: data.ts:256 ~ values:', values);
+      console.log(
+        '🚀 ~ file: data.ts:254 ~ values.accessType == DictCommon.basic.va_type_avf',
+        values.accessType == DictCommon.basic.va_type_avf ||
+          values.accessType == DictCommon.basic.va_type_avg,
+      );
+      return (
+        values.accessType == DictCommon.basic.va_type_avf ||
+        values.accessType == DictCommon.basic.va_type_avg
+      );
+    },
+  },
+  {
+    label: '置管引导',
+    field: 'catheterGuidance',
+    required: true,
+    component: 'ApiRadioGroup',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_guidance',
+      },
+    },
+    ifShow: ({ values }) => {
+      return (
+        values.accessType == DictCommon.basic.va_type_ncc ||
+        values.accessType == DictCommon.basic.va_type_tcc
+      );
+    },
+  },
+];
+
+export const columnsComplication: BasicColumn[] = [
+  {
+    title: '发生时间',
+    dataIndex: 'occurredTime',
+    width: 200,
+    editRow: true,
+    editComponent: 'DatePicker',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      showTime: true,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      format: 'YYYY-MM-DD HH:mm:ss',
+      getPopupContainer: () => document.body,
+    },
+  },
+  {
+    title: '并发症名称',
+    dataIndex: 'name',
+    width: 220,
+    editRow: true,
+    editComponent: 'ApiSelect',
+    // 默认必填校验
+    editRule: true,
+    editComponentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'va_comp',
+      },
+    },
+  },
+  {
+    title: '说明',
+    dataIndex: 'remark',
+    editRow: true,
+    editComponent: 'Input',
+  },
+];

+ 359 - 0
src/views/biz/archives/vascularAccess/index.vue

@@ -0,0 +1,359 @@
+<template>
+  <div>
+    <div class="mx-6 mb-2">
+      <TimeLine :data="timeLineData" @hover="callHover">
+        <template #head>
+          <transition class="animate__animated animate__slideInLeft">
+            <div class="timeline-outer" v-if="timeOuter">
+              <div
+                class="timeline-outer_item"
+                v-for="item in bizDictOptions.type"
+                :key="item.id"
+                @click="handleAdd(item.value)"
+              >
+                {{ item.label }}
+              </div>
+            </div>
+          </transition>
+        </template>
+        <template #card="{ data }">
+          <a-button
+            v-if="data.dot == '正常'"
+            type="primary"
+            shape="round"
+            class="mr-4"
+            @click="handleAccessReturn(data.id)"
+            >通路转归</a-button
+          >
+          <a-dropdown>
+            <span class="ant-dropdown-link">
+              <i :class="['iconfont', 'icon-xt-details_more_default']" />
+            </span>
+            <template #overlay>
+              <a-menu>
+                <a-menu-item
+                  v-for="menu in dropMenu"
+                  :key="menu.value"
+                  @click="handleMenu({ id: data.id, menu: menu.value })"
+                >
+                  <span>
+                    {{ menu.label }}
+                  </span>
+                </a-menu-item>
+              </a-menu>
+            </template>
+          </a-dropdown>
+        </template>
+      </TimeLine>
+    </div>
+    <FormModal @register="registerModal" @success="callSuccess" />
+    <FormDrawer @register="registerDrawer" @success="callSuccess" />
+    <FormModalReturn @register="registerModalReturn" @success="callSuccess" />
+    <ViewDrawerComplication @register="registerDrawerComplication" @success="callSuccess" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, reactive, onUnmounted } from 'vue';
+  import TimeLine from '/@/components/XTTimeLine/src/TimeLine.vue';
+  import { archivesVascularAccessQueryList } from '/@/api/biz/archives/vascularAccessApi';
+  import { onMounted } from 'vue';
+  import dayjs from 'dayjs';
+  import { listDictModelBatch } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+  // 通路回归
+  import FormModalReturn from './FormModalReturn.vue';
+  // 新建编辑
+  import FormModal from './FormModal.vue';
+  // 通路评估
+  import FormDrawer from './FormDrawer.vue';
+  // 通路并发症
+  import ViewDrawerComplication from './ViewDrawerComplication.vue';
+
+  import { useModal } from '/@/components/Modal';
+  import { useMessage } from '@/hooks/web/useMessage';
+  import { useDrawer } from '@/components/Drawer';
+  import { DictCommon } from './data';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    },
+  });
+  const bizDictOptions = reactive<any>({});
+  const timeLineData = ref([]);
+  const timeOuter = ref(false);
+  onMounted(async () => {
+    const res = await listDictModelBatch(bizDictData.value.map(ele => ele.dictCode));
+    for (const i in res) {
+      const filter = bizDictData.value.filter(ele => ele.dictCode == i)[0];
+      bizDictOptions[filter.key] = res[i];
+    }
+    console.log('🚀 ~ file: index.vue:47 ~ onMounted ~ bizDictOptions:', bizDictOptions);
+    // console.log('🚀 ~ file: index.vue:96 ~ onMounted ~ res:', res);
+    await getData();
+  });
+  const dropMenu = [
+    { label: '通路评估', value: 'assessment' },
+    { label: '评估记录', value: 'assessmentRecord' },
+    { label: '通路并发症', value: 'assessComplication' },
+    { label: '编辑', value: 'edit' },
+    { label: '删除', value: 'del' },
+  ];
+  const [registerModalReturn, { openModal: openModalReturn }] = useModal();
+  const [registerModal, { openModal }] = useModal();
+  const [registerDrawer] = useDrawer();
+  const [registerDrawerComplication, { openDrawer: openDrawerComplication }] = useDrawer();
+  const { createMessage, createConfirm } = useMessage();
+  const bizDictData = ref([
+    // 血管材料
+    { key: 'material', dictCode: 'va_material' },
+    // 吻合方式
+    { key: 'anastomosis', dictCode: 'va_anastomosis' },
+    // 穿刺方式
+    { key: 'puncture', dictCode: 'va_puncture' },
+    // 通路类型
+    { key: 'type', dictCode: 'va_type' },
+    // 置管引导
+    { key: 'catheterGuidance', dictCode: 'va_guidance' },
+    // 转归原因
+    { key: 'return', dictCode: 'va_return' },
+    // 位置
+    { key: 'avfLoc', dictCode: 'va_avf_loc' },
+    // 动脉
+    { key: 'avfArteries', dictCode: 'va_avf_arteries' },
+    // 静脉
+    { key: 'avfVein', dictCode: 'va_avf_vein' },
+    { key: 'avgLoc', dictCode: 'va_avg_loc' },
+    { key: 'avgArteries', dictCode: 'va_avg_arteries' },
+    { key: 'avgVein', dictCode: 'va_avg_vein' },
+    { key: 'nccLoc', dictCode: 'va_ncc_loc' },
+    // 颈内静脉
+    { key: 'nccLocJnjm', dictCode: 'ncc_loc_jnjm' },
+    // 锁骨下静脉
+    { key: 'nccLocSg', dictCode: 'ncc_loc_sg' },
+    { key: 'tccLoc', dictCode: 'va_tcc_loc' },
+    { key: 'tccLocJnjm', dictCode: 'tcc_loc_jnjm' },
+    { key: 'tccLocJwjm', dictCode: 'tcc_loc_jwjm' },
+    { key: 'tccLocSg', dictCode: 'tcc_loc_sg' },
+  ]);
+
+  async function getData() {
+    const res = await archivesVascularAccessQueryList(props.info?.id);
+    console.log('🚀 ~ file: index.vue:105 ~ getData ~ res:', res);
+    timeLineData.value = res.map(ele => {
+      const innerData = {
+        title: '基本信息',
+        // icon: 'icon-xt-details_more_default',
+        icon: '',
+        type: 'basic',
+        data: [
+          { field: 'setUpHospital', label: '建立医院', value: '' },
+          { field: 'puncture', label: '穿刺体侧', value: '', dict: true },
+          { field: 'location', label: '手术部位', value: '' },
+          { field: 'anastomosis', label: '吻合方式', value: '', dict: true },
+          { field: 'catheterGuidance', label: '置管引导', value: '', dict: true },
+          { field: 'vascularMaterial', label: '血管材料', value: '', hidden: true, dict: true },
+          { field: 'updatorName', label: '记录人', value: '' },
+          { field: 'accessReturn', label: '转归记录', value: '', span: 12 },
+        ],
+      };
+      innerData.title = formatDictValue(bizDictOptions.type, ele.type);
+      console.log('🚀 ~ file: index.vue:114 ~ getData ~ innerData:', innerData);
+      innerData.type = ele.type;
+      innerData.data = innerData.data.map(eleC => {
+        if (eleC.dict) {
+          eleC.value = formatDictValue(bizDictOptions[eleC.field], ele[eleC.field]);
+        } else {
+          eleC.value = ele[eleC.field];
+        }
+        // 吻合方式
+        if (ele.type == DictCommon.basic.va_type_ncc || ele.type == DictCommon.basic.va_type_tcc) {
+          eleC.hidden = eleC.field == 'anastomosis' ? true : false;
+        }
+        // 置管引导
+        if (ele.type == DictCommon.basic.va_type_avf || ele.type == DictCommon.basic.va_type_avg) {
+          eleC.hidden = eleC.field == 'catheterGuidance' ? true : false;
+        }
+        // 手术位置
+        if (eleC.field == 'location') {
+          if (ele.type == DictCommon.basic.va_type_avf) {
+            eleC.value = `
+              ${formatDictValue(bizDictOptions['avfLoc'], ele['location'])}
+              / ${formatDictValue(bizDictOptions['avfArteries'], ele['arteries'])}
+              - ${formatDictValue(bizDictOptions['avfVein'], ele['vein'])}`;
+          }
+          if (ele.type == DictCommon.basic.va_type_avg) {
+            eleC.value = `
+              ${formatDictValue(bizDictOptions['avgLoc'], ele['location'])}
+              / ${formatDictValue(bizDictOptions['avgArteries'], ele['arteries'])}
+              - ${formatDictValue(bizDictOptions['avgVein'], ele['vein'])}`;
+          }
+          if (ele.type == DictCommon.basic.va_type_ncc) {
+            let path = '';
+            if (ele['location'] == 'ncc_loc_jnjm') {
+              path = formatDictValue(bizDictOptions['nccLocJnjm'], ele['path']);
+            } else if (ele['location'] == 'ncc_loc_sg') {
+              path = formatDictValue(bizDictOptions['nccLocSg'], ele['path']);
+            } else {
+              path = formatDictValue(bizDictOptions['nccLoc'], ele['path']);
+            }
+            eleC.value = `
+              ${formatDictValue(bizDictOptions['nccLoc'], ele['location'])}
+              / ${path}`;
+          }
+          if (ele.type == DictCommon.basic.va_type_tcc) {
+            let path = '';
+            if (ele['location'] == 'tcc_loc_jnjm') {
+              path = formatDictValue(bizDictOptions['tccLocJnjm'], ele['path']);
+            } else if (ele['location'] == 'tcc_loc_jwjm') {
+              path = formatDictValue(bizDictOptions['tccLocJwjm'], ele['path']);
+            } else if (ele['location'] == 'tcc_loc_sg') {
+              path = formatDictValue(bizDictOptions['tccLocSg'], ele['path']);
+            } else {
+              path = formatDictValue(bizDictOptions['tccLoc'], ele['path']);
+            }
+            eleC.value = `
+              ${formatDictValue(bizDictOptions['tccLoc'], ele['location'])}
+              / ${path}`;
+          }
+        }
+
+        // 血管材料
+        if (eleC.field == 'vascularMaterial') {
+          eleC.hidden = ele.type == 'va_avg' ? false : true;
+        }
+        // 转归记录
+        if (eleC.field == 'accessReturn') {
+          if (ele.returnBack) {
+            eleC.value = `于${dayjs(ele[eleC.field]['returnTime']).format(
+              'YYYY-MM-DD',
+            )}转归; 使用${dayjs().diff(
+              ele[eleC.field]['returnTime'],
+              'day',
+            )}天; ${formatDictValue(bizDictOptions.return, ele[eleC.field]['returnCause'])}; ${
+              ele['updatorName']
+            }`;
+          } else {
+            eleC.hidden = true;
+          }
+        }
+        return eleC;
+      });
+      // console.log('🚀 ~ file: index.vue:168 ~ getData ~ cnt:', innerData);
+      return {
+        id: ele.id,
+        dot: ele.returnBack ? '已转归' : '正常',
+        date: dayjs(ele.setUpTime).format('YYYY-MM-DD'),
+        cnt: innerData,
+      };
+    });
+    console.log('🚀 ~ file: index.vue:168 ~ getData ~ timeLineData.value :', timeLineData.value);
+  }
+  function handleAdd(data) {
+    console.log('🚀 ~ file: index.vue:205 ~ handleAdd ~ data:', data);
+    // timeOuter.value = false;
+    openModal(true, {
+      record: { ...props.info, accessType: data },
+    });
+  }
+  function callHover() {
+    timeOuter.value = true;
+  }
+  function handleAccessReturn(id) {
+    console.log('通路转归', id);
+    openModalReturn(true, {
+      record: props.info,
+      accessId: id,
+      isUpdate: true,
+    });
+  }
+  onUnmounted(() => {
+    timeOuter.value = false;
+  });
+  function handleMenu(data) {
+    if (data.menu == 'edit') {
+      console.log('data', data);
+      openModal(true, {
+        isUpdate: true,
+        record: { ...props.info, ...data },
+      });
+    }
+    if (data.menu == 'del') {
+      console.log('data', data);
+      createConfirm({
+        content: '确定要删除选中数据?',
+        iconType: 'warning',
+        onOk: async () => {
+          // 删除
+          await archivesVascularAccessQueryList({});
+          createMessage.success('删除成功!');
+          await getData();
+        },
+      });
+    }
+    if (data.menu == 'assessment') {
+      console.log('data', data);
+    }
+    if (data.menu == 'assessmentRecord') {
+      console.log('data', data);
+    }
+    if (data.menu == 'assessComplication') {
+      console.log('data', data);
+      openDrawerComplication(true, {
+        record: props.info,
+        accessId: data.id,
+      });
+    }
+  }
+  // 回调
+  async function callSuccess(data) {
+    console.log('🚀 ~ file: index.vue:212 ~ callSuccess ~ data:', data);
+    await getData();
+  }
+</script>
+
+<style lang="less" scoped>
+  .timeline-outer {
+    display: flex;
+    margin-left: 20px;
+    transition: all 0.3s ease-in-out;
+
+    &_item {
+      padding: 0 30px;
+      height: 40px;
+      line-height: 40px;
+      border-radius: 30px;
+      color: #fff;
+      background: #0075ff;
+      margin-right: 20px;
+      cursor: pointer;
+      letter-spacing: 2px;
+    }
+  }
+
+  ::v-deep(.ant-picker) {
+    width: 100%;
+  }
+
+  .ant-dropdown-link {
+    display: block;
+    margin-right: 16px;
+    background: #fff;
+    border-radius: 50%;
+    width: 32px;
+    height: 32px;
+    text-align: center;
+    line-height: 32px;
+    cursor: pointer;
+
+    &:last-child {
+      margin-right: 0;
+    }
+
+    &:hover {
+      color: #0075ff;
+    }
+  }
+</style>