Ver Fonte

Merge branch 'master' of http://192.168.100.32:3000/fanfan/xt-front

fan há 2 anos atrás
pai
commit
5ec17ec058

+ 133 - 0
src/api/biz/visit/checkApi.ts

@@ -0,0 +1,133 @@
+import { defHttp } from '/@/utils/http/axios';
+import { setParams } from '/@/utils/index';
+
+enum Api {
+  archivesPatrolWardQueryPage = '/archives/patrolWard/query/page',
+  archivesPatrolWardDetail = '/archives/patrolWard/detail',
+  archivesPatrolWardAdd = '/archives/patrolWard/add',
+  archivesPatrolWardEdit = '/archives/patrolWard/edit',
+  archivesPatrolWardRemove = '/archives/patrolWard/removeByIds',
+  archivesPatrolWardQueryCheckRoomRecord = '/archives/patrolWard/queryCheckRoomRecord',
+  archivesPatrolWardQueryPersonNumber = '/archives/patrolWard/queryPersonNumber',
+  archivesPatrolWardFollowOrNo = '/archives/patrolWard/followOrNo',
+}
+
+/**
+ *
+ * @author  fan
+ * @date  2023/06/30 17:39
+ * @description: 根据条件查询查房列表,权限 - biz:patrolward:query
+ * @method: POST
+ * @param:
+ *       {String}  updatorName     updator_name
+ * @return:
+ *       {String}  patientBasicId  基础病历id
+ *       {String}  scheduledId  排床id
+ *       {Date}  patrolTime  查房时间
+ *       {String}  content  查房内容
+ *       {String}  updatorName  updator_name
+ */
+
+export const archivesPatrolWardQueryCheckRoomRecord = (params?: object) => {
+  return defHttp.post({
+    url: Api.archivesPatrolWardQueryCheckRoomRecord,
+    params: setParams(params),
+  });
+};
+/**
+ *
+ * @author fan
+ * @date  2023/06/30 17:39
+ * @description: 根据条件查询查房列表,权限 - biz:patrolward:query
+ * @method: POST
+ * @param:
+ *       {String}  updatorName     updator_name
+ * @return:
+ *       {String}  patientBasicId  基础病历id
+ *       {String}  scheduledId  排床id
+ *       {Date}  patrolTime  查房时间
+ *       {String}  content  查房内容
+ *       {String}  updatorName  updator_name
+ */
+
+export const archivesPatrolWardQueryPage = (params?: object) => {
+  return defHttp.post({ url: Api.archivesPatrolWardQueryPage, params: setParams(params) });
+};
+/**
+ *
+ * @author fan
+ * @date  2023/06/30 17:39
+ * @description: 根据id查询查房详细信息,权限 - biz:patrolward:query
+ * @method: GET
+ * @param:  id 查房主键id
+ * @return:
+ *       {String}  patientBasicId  基础病历id
+ *       {String}  scheduledId  排床id
+ *       {Date}  patrolTime  查房时间
+ *       {String}  content  查房内容
+ *       {String}  updatorName  updator_name
+ */
+export const archivesPatrolWardDetail = (id: string) => {
+  return defHttp.get({ url: Api.archivesPatrolWardDetail + '/' + id });
+};
+
+/**
+ *
+ * @author fan
+ * @date  2023/06/30 17:39
+ * @description: 添加查房,权限 - biz:patrolward:add
+ * @method: POST
+ * @param:
+ *       {String}  patientBasicId  基础病历id
+ *       {String}  scheduledId  排床id
+ *       {Date}  patrolTime  查房时间
+ *       {String}  content  查房内容
+ * @return:
+ *       0 添加失败
+ *       1 添加成功
+ */
+export const archivesPatrolWardAdd = (params?: object) => {
+  return defHttp.post({ url: Api.archivesPatrolWardAdd, params: params });
+};
+
+/**
+ *
+ * @author fan
+ * @date  2023/06/30 17:39
+ * @description: 通过主键id编辑查房,权限 - biz:patrolward:edit
+ * @method: POST
+ * @param:
+ *       {String}  patientBasicId  基础病历id
+ *       {String}  scheduledId  排床id
+ *       {Date}  patrolTime  查房时间
+ *       {String}  content  查房内容
+ * @return:
+ *       0 编辑失败
+ *       1 编辑成功
+ */
+export const archivesPatrolWardEdit = (params?: object) => {
+  return defHttp.post({ url: Api.archivesPatrolWardEdit, params: params });
+};
+
+/**
+ * @description: 删除,权限 - biz:patrolward:remove
+ * @method: POST
+ */
+export const archivesPatrolWardRemove = (params: Array<string | number>) => {
+  return defHttp.post({ url: Api.archivesPatrolWardRemove, params: params });
+};
+
+/**
+ * @description: 查房页面各条件人员数量,权限 - biz:patrolward:querypersonnumber
+ * @method: get
+ */
+export const archivesPatrolWardQueryPersonNumber = () => {
+  return defHttp.get({ url: Api.archivesPatrolWardQueryPersonNumber });
+};
+/**
+ * @description: 关注或取消关注,权限 - biz:patrolward:followOrNo
+ * @method: get
+ */
+export const archivesPatrolWardFollowOrNo = () => {
+  return defHttp.post({ url: Api.archivesPatrolWardFollowOrNo });
+};

+ 22 - 0
src/api/biz/visit/prepareApi.ts

@@ -0,0 +1,22 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  readyPrepare = '/visit/prePrepare/query/personList',
+  prePrepareSailing = '/visit/prePrepare/query/sailings',
+  wardInfo = '/biz/sys/wardInfo/query/list',
+}
+
+/**
+ * @description: 获取透前准备页面人员信息
+ * @method: POET
+ */
+export const readyPrepare = (params?: object) => {
+  return defHttp.post({ url: Api.readyPrepare, params: params });
+};
+
+export const wardInfo = (params?: object) => {
+  return defHttp.post({ url: Api.wardInfo, params: params });
+};
+
+export const prePrepareSailing = (params?: object) => {
+  return defHttp.get({ url: Api.prePrepareSailing, params: params });
+};

+ 41 - 0
src/api/biz/visit/transferApi.ts

@@ -0,0 +1,41 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  typeNumber = '/visit/handoverShifts/queryPersonNumber',
+  transfetList = '/visit/handoverShifts/query/page',
+  transferDel = '/visit/handoverShifts/removeByIds',
+  transferAdd = '/visit/handoverShifts/add',
+  transferEdit = '/visit/handoverShifts/edit',
+  transferById = '/visit/handoverShifts/detail',
+}
+
+/**
+ * @description: 获取透前准备页面人员信息
+ * @method: POET
+ */
+export const getTypeNumber = () => {
+  return defHttp.get({ url: Api.typeNumber });
+};
+
+export const getTransgerList = (params?: object) => {
+  return defHttp.post({ url: Api.transfetList, params: params });
+};
+
+export const transferDel = (params?: object) => {
+  return defHttp.post({ url: Api.transferDel, params: params });
+};
+
+export const transferExport = (params?: object) => {
+  return defHttp.post({ url: Api.transferDel, params: params });
+};
+
+export const transferAdd = (params?: object) => {
+  return defHttp.post({ url: Api.transferAdd, params: params });
+};
+
+export const transferEdit = (params?: object) => {
+  return defHttp.post({ url: Api.transferEdit, params: params });
+};
+
+export const transferById = (id: string) => {
+  return defHttp.get({ url: Api.transferById + '/' + id });
+};

+ 5 - 3
src/views/biz/management/complication/index.vue

@@ -18,7 +18,7 @@
                 <template #overlay>
                   <a-menu @click="handleMenuClick">
                     <a-menu-item key="1">添加并发症</a-menu-item>
-                    <a-menu-item key="2">批量导入</a-menu-item>
+                    <!-- <a-menu-item key="2">批量导入</a-menu-item> -->
                     <a-menu-item key="3">批量导出</a-menu-item>
                   </a-menu>
                 </template>
@@ -43,12 +43,12 @@
           <Col :span="18">
             <Card :title="detailName">
               <template #extra>
-                <Button shape="circle" v-auth="['bizSys:wardInfo:add']" @click="handleEditInfo"
+                <Button shape="circle" v-auth="['bizSys:complication:edit']" @click="handleEditInfo"
                   ><Icon icon="icon-xt-details_edit_default|iconfont" :size="14"
                 /></Button>
                 <Button
                   shape="circle"
-                  v-auth="['bizSys:wardInfo:add']"
+                  v-auth="['bizSys:complication:remove']"
                   @click="handleDel(detailForm.id)"
                   style="margin-left: 20px"
                   ><Icon icon="icon-xt-details_delete_default|iconfont" :size="14"
@@ -200,6 +200,7 @@
   async function getDataList(params?: object) {
     const res = await getComplicationList({
       ...params,
+      page: { current: 1, size: 999 },
       orders: [{ field: 'sort', direction: 'DESC' }],
     });
     complicationList.value = res.data;
@@ -373,6 +374,7 @@
 
   ::v-deep(.ant-card) {
     height: 800px;
+    overflow: auto;
   }
 
   ::v-deep(.ant-table-body) {

+ 1 - 0
src/views/biz/management/complication/noteFormModal.vue

@@ -94,6 +94,7 @@
       component: 'InputNumber',
       componentProps: {
         placeholder: '请输入排序',
+        min: 0,
         onFocus: function () {
           treeDis.value = true;
         },

+ 35 - 1
src/views/biz/management/parameter/index.vue

@@ -7,6 +7,14 @@
       </Input> -->
       <BasicTable @register="registerTable">
         <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'typeName' && record.typeName.indexOf('天') != -1">
+            <div style="display: flex">
+              {{ record.typeName }}
+              <a-tooltip color="orange" :title="record.typeName + ': 不可超过360天'">
+                <div class="tips">?</div>
+              </a-tooltip>
+            </div>
+          </template>
           <template
             v-if="
               column.key === 'contents' &&
@@ -16,10 +24,21 @@
               record.key != 4
             "
           >
-            <div>
+            <div v-if="record.typeName.indexOf('天') != -1">
+              <a-input-number
+                v-if="editableData[record.key]"
+                v-model:value="editableData[record.key][column.dataIndex]"
+                :min="0"
+                :max="365"
+                placeholder="请输入参数内容"
+              />
+              <template v-else> {{ record.contents }} </template>
+            </div>
+            <div v-else>
               <a-input-number
                 v-if="editableData[record.key]"
                 v-model:value="editableData[record.key][column.dataIndex]"
+                :min="0"
                 placeholder="请输入参数内容"
               />
               <template v-else> {{ record.contents }} </template>
@@ -399,6 +418,8 @@
       editObj[editableData[key].typeIndex] = Number(
         parseFloat(editableData[key].contents).toFixed(2),
       );
+    } else {
+      editObj[editableData[key].typeIndex] = editableData[key].contents;
     }
     await editParameter(resObj.value);
     delete editableData[key];
@@ -443,4 +464,17 @@
   ::v-deep(.fan-basic-table-action.center) {
     justify-content: left !important;
   }
+
+  .tips {
+    width: 14px;
+    height: 14px;
+    margin: 5px 0 0 8px;
+    line-height: 14px;
+    text-align: center;
+    border: solid 1px #696969;
+    border-radius: 50%;
+    background-color: #696969;
+    color: #fff;
+    font-size: 3px;
+  }
 </style>

+ 3 - 0
src/views/biz/management/ward/data.ts

@@ -9,14 +9,17 @@ export const wardInfoColumns: BasicColumn[] = [
   {
     title: '病区属性',
     dataIndex: 'propertiesName',
+    width: 260,
   },
   {
     title: '状态',
     dataIndex: 'disable',
+    width: 150,
   },
   {
     title: '排序',
     dataIndex: 'sort',
+    width: 120,
   },
 ];
 

+ 9 - 3
src/views/biz/management/ward/index.vue

@@ -63,7 +63,10 @@
                   <BasicTable @register="registerTable">
                     <template #bodyCell="{ column, record }">
                       <template v-if="column.key === 'disable'">
-                        <Tag :color="formatDictColor(disableOptions, record.disable)">
+                        <Tag
+                          :color="formatDictColor(disableOptions, record.disable)"
+                          :style="'color:' + formatDictFontColor(disableOptions, record.disable)"
+                        >
                           {{ formatDictValue(disableOptions, record.disable) }}
                         </Tag>
                       </template>
@@ -215,7 +218,7 @@
   import { useModal } from '/@/components/Modal';
   import { listDictModel } from '/@/api/common';
   import { useMessage } from '/@/hooks/web/useMessage';
-  import { formatDictColor, formatDictValue } from '/@/utils';
+  import { formatDictColor, formatDictFontColor, formatDictValue } from '/@/utils';
   const { createConfirm, createMessage } = useMessage();
   const tabList = ref([
     { key: 0, tab: '病区管理', type: 'WARD' },
@@ -250,7 +253,7 @@
     },
     useSearchForm: false,
     actionColumn: {
-      width: 320,
+      width: 120,
       title: '操作',
       dataIndex: 'action',
     },
@@ -466,6 +469,9 @@
 <style lang="less" scoped>
   ::v-deep(.ant-card-head) {
     background-color: #f6f8fa !important;
+    color: #000a18;
+    font-weight: 600;
+    font-size: 18px;
   }
 
   .type-title {

+ 120 - 0
src/views/biz/visit/check/data.ts

@@ -0,0 +1,120 @@
+import dayjs from 'dayjs';
+import { DescItem } from '/@/components/Description';
+import { BasicColumn, FormSchema } from '/@/components/Table';
+
+export const BasicTab = [
+  {
+    key: '0',
+    label: '全部',
+    value: 128,
+    hasValue: true,
+    hasBracket: true,
+  },
+  {
+    key: '1',
+    label: '透中患者',
+    value: 12,
+    hasValue: true,
+    prefixColor: '#1BC1B3',
+    hasBracket: true,
+  },
+  {
+    key: '2',
+    label: '非透中患者',
+    value: 18,
+    hasValue: true,
+    prefixColor: '#D3D8DD',
+    hasBracket: true,
+  },
+  {
+    key: '3',
+    label: '新患者',
+    value: 18,
+    hasValue: true,
+    prefixColor: '#F7B500',
+    hasBracket: true,
+  },
+  {
+    key: '4',
+    label: '关注',
+    value: 18,
+    hasValue: true,
+    prefixColor: '#33CCFF',
+    hasBracket: true,
+  },
+];
+export const BasicTabActive = BasicTab[0].key;
+
+export const columns: BasicColumn[] = [
+  {
+    title: '姓名',
+    dataIndex: 'name',
+    width: 150,
+    align: 'left',
+  },
+  {
+    title: '信息',
+    dataIndex: 'gender',
+    width: 150,
+    align: 'left',
+  },
+  {
+    title: '查房时间',
+    dataIndex: 'patrolTime',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '最新查房内容',
+    dataIndex: 'content',
+    align: 'left',
+  },
+  {
+    title: '记录人',
+    dataIndex: 'recorder',
+    width: 120,
+    align: 'left',
+  },
+];
+
+// 表单新增编辑
+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',
+  },
+];

+ 76 - 0
src/views/biz/visit/check/formModal.vue

@@ -0,0 +1,76 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <BasicForm @register="registerForm" layout="vertical" />
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicModal } from '/@/components/Modal';
+  import { 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/visit/checkApi';
+
+  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,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+
+    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 });
+      !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>

+ 261 - 3
src/views/biz/visit/check/index.vue

@@ -1,7 +1,265 @@
 <template>
-  <div> 占位符 </div>
+  <div class="m-4">
+    <div>
+      <XTTitle title="查房" @click="callTitleClick" />
+      <div class="flex justify-between my-4">
+        <XTTab
+          type="check"
+          :width="160"
+          :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 === 'name'">
+            <span :class="['table-dot', 'table-dot--' + record.status]" />
+            <span>{{ record.name }}</span>
+          </template>
+          <template v-if="column.key === 'patrolTime'">
+            {{ record.patrolTime ? dayjs(record.patrolTime).format('YYYY-MM-DD') : '' }}
+          </template>
+
+          <template v-if="column.key === 'action'">
+            <TableAction
+              :dropDownActions="[
+                {
+                  auth: 'storage:config:edit',
+                  tooltip: '查房记录',
+                  label: '查房记录',
+                  ifShow: !record.master,
+                },
+                {
+                  auth: 'storage:config:edit',
+                  tooltip: '编辑',
+                  label: '编辑',
+                  onClick: handleEdit.bind(null, record),
+                },
+                {
+                  auth: 'storage:config:remove',
+                  tooltip: '取消关注',
+                  label: '取消关注',
+                  color: 'error',
+                  // popConfirm: {
+                  //   title: '是否确定要取消关注?',
+                  //   placement: 'left',
+                  //   confirm: handleDelete.bind(null, record),
+                  // },
+                },
+              ]"
+              :actions="[
+                {
+                  auth: 'sys:log:query',
+                  // icon: 'icon-eye|iconfont',
+                  tooltip: '查房',
+                  label: '查房',
+                  onClick: handleView.bind(null, record),
+                },
+              ]"
+            />
+          </template>
+        </template>
+      </BasicTable>
+    </div>
+    <FormModal @register="registerModal" @success="handleSuccess" />
+    <ViewDrawer @register="registerDrawerView" @success="handleSuccess" />
+  </div>
 </template>
 
-<script setup lang="ts"></script>
+<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 } from './data';
+  import { ref } from 'vue';
+  // import { useRouter } from 'vue-router';
+  import {
+    archivesPatrolWardQueryCheckRoomRecord,
+    archivesPatrolWardQueryPersonNumber,
+  } from '/@/api/biz/visit/checkApi';
+  // import { formatDictColor, formatDictFontColor, formatDictValue } from '/@/utils';
+  import { onMounted, reactive } from 'vue';
+  import dayjs from 'dayjs';
+  import { useModal } from '/@/components/Modal';
+  import FormModal from './formModal.vue';
+  import ViewDrawer from './viewDrawer.vue';
+  import { useDrawer } from '@/components/Drawer';
+
+  // const bizDictOptions = reactive<any>({});
+  //路由跳转
+  // const router = useRouter();
+  const activeKey = ref(BasicTabActive);
+  const tabData = ref(BasicTab);
+  const [registerModal, { openModal }] = useModal();
+  const [registerDrawerView, { openDrawer, openDrawer: openDrawerView }] = useDrawer();
+
+  onMounted(async () => {
+    const personNumber = await archivesPatrolWardQueryPersonNumber();
+    console.log('🚀 ~ file: index.vue:104 ~ onMounted ~ stats:', personNumber);
+    tabData.value = tabData.value.map(ele => {
+      if (ele.key == '0') {
+        ele.value = personNumber.all;
+      }
+      if (ele.key == '1') {
+        ele.value = personNumber.dialysisPatients;
+      }
+      if (ele.key == '2') {
+        ele.value = personNumber.noDialysisPatients;
+      }
+      if (ele.key == '3') {
+        ele.value = personNumber.newPatient;
+      }
+      if (ele.key == '4') {
+        ele.value = personNumber.attention;
+      }
+      return ele;
+    });
+    console.log('🚀 ~ file: index.vue:118 ~ onMounted ~ tabData.value:', tabData.value);
+  });
+  const [registerTable, { reload }] = useTable({
+    api: archivesPatrolWardQueryCheckRoomRecord,
+    // exportAuthList: ['sys:log:export'],
+    rowKey: 'id',
+    columns,
+    showIndexColumn: true,
+    bordered: true,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    afterFetch: handleAfterFetch,
+  });
+  // 筛选数据
+  const siftData = ref([]);
+
+  // formdata
+  const formData = [
+    {
+      name: 'tableSort',
+      componentType: 'Select',
+      placeholder: '请选择',
+      width: 120,
+      defaultValue: 'patrolTime',
+      dicts: [
+        { label: '按查房时间', value: 'patrolTime' },
+        { label: '按姓氏', value: 'patientNamePinyin' },
+      ],
+    },
+    {
+      name: 'name',
+      componentType: 'Input',
+      placeholder: '请输入患者姓名',
+      width: 200,
+      prefix: 'icon-xt-search',
+    },
+  ];
+
+  const formValue = reactive({
+    name: '',
+  }) 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,
+      orders: [{ field: 'patrolTime', direction: 'DESC' }],
+      sortType: 'st_patrol_time',
+      ...sift,
+    };
+  }
+
+  function handleAfterFetch(data) {
+    return data;
+  }
+
+  // 详情按钮事件
+  function handleView(record: Recordable) {
+    console.log(record);
+    openDrawerView(true, {
+      record,
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+    });
+  }
+
+  // 弹窗回调事件
+  async function handleSuccess({ 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') {
+      openDrawer(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,
+      });
+    }
+  }
 
-<style lang="less" scoped></style>
+  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;
+      });
+    }
+    await reload();
+  }
+</script>

+ 72 - 0
src/views/biz/visit/check/viewDrawer.vue

@@ -0,0 +1,72 @@
+<template>
+  <div>
+    <BasicDrawer
+      v-bind="$attrs"
+      destroyOnClose
+      @register="registerDrawer"
+      :title="getTitle"
+      :width="width"
+    >
+      <Description @register="registerDesc" :data="descData" />
+    </BasicDrawer>
+
+    <Button
+      v-auth="['storage:config:add']"
+      type="primary"
+      @click="handleCreate"
+      preIcon="icon-plus|iconfont"
+    >
+      新增
+    </Button>
+    <FormModal @register="registerModal" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onBeforeMount, ref } from 'vue'; // onBeforeMount,
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { Description, useDescription } from '/@/components/Description';
+  import { viewSchema } from './data';
+
+  import { archivesPatrolWardDetail } from '/@/api/biz/visit/checkApi';
+  import { listDictModel } from '/@/api/common';
+  import FormModal from './formModal.vue';
+  import { formatDictValue } from '/@/utils';
+  import { useModal } from '/@/components/Modal';
+
+  const descData = ref({});
+  const getTitle = '查看查房记录';
+  const width = '45%';
+  const [registerModal, { openModal }] = useModal();
+
+  const typeOptions = ref();
+  onBeforeMount(async () => {
+    typeOptions.value = await listDictModel({ dictCode: 'sys_login_log_type' });
+  });
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+    });
+  }
+  const [registerDrawer] = useDrawerInner(async data => {
+    console.log('::::::::::', data.record);
+    const resData = await archivesPatrolWardDetail(data.record.id);
+    descData.value = {
+      ...resData,
+      type: formatDictValue(typeOptions.value, resData.type),
+    };
+  });
+  const [registerDesc] = useDescription({
+    schema: viewSchema,
+    column: 1,
+    size: 'middle',
+    labelStyle: {
+      width: '120px',
+    },
+  });
+  // 弹窗回调事件
+  async function handleSuccess({ isUpdate, values }) {
+    console.log(isUpdate);
+    console.log(values);
+  }
+</script>

+ 19 - 2
src/views/biz/visit/ready/data.ts

@@ -2,6 +2,22 @@ import { BasicColumn } from '/@/components/Table';
 import { FormSchema } from '/@/components/Form';
 import { radioBoolean } from '/@/utils/filters';
 import { listDictModel } from '/@/api/common';
+import { ColorEnum } from '@/enums/colorEnum';
+
+export enum TitleEnum {
+  finish = '已完成',
+  finishColor = ColorEnum.MUTED,
+  finishBg = ColorEnum.MUTED_BG,
+  printable = '可打印',
+  printableColor = ColorEnum.SUCCESS,
+  printableBg = ColorEnum.SUCCESS_BG,
+  unWeighed = '未称量',
+  unWeighedColor = ColorEnum.PRIMARY,
+  unWeighedBg = ColorEnum.PRIMARY_BG,
+  unconfirmed = '待确认',
+  unconfirmedColor = ColorEnum.WARNING,
+  unconfirmedBg = ColorEnum.WARNING_BG,
+}
 
 export const dataFormSchema: FormSchema[] = [
   {
@@ -13,7 +29,7 @@ export const dataFormSchema: FormSchema[] = [
     },
   },
   {
-    field: 'PlainText1111',
+    field: 'PlainText',
     component: 'RadioDescGroup',
     label: '1、干体重在过去3~6个月总的变化',
     componentProps: {
@@ -78,7 +94,7 @@ export const dataFormSchema: FormSchema[] = [
   {
     field: 'configKey',
     label: '参数键名',
-    labelColor: '#818694',
+    labelColor: '#81869 4',
     subLabel: '测试',
     subLabelColor: '#FF5D39',
     component: 'Input',
@@ -175,6 +191,7 @@ export const dataFormSchema: FormSchema[] = [
     },
   },
 ];
+
 export const columns: BasicColumn[] = [
   {
     title: '名称',

+ 150 - 0
src/views/biz/visit/ready/importView.vue

@@ -0,0 +1,150 @@
+<template>
+  <BasicModal
+    @register="registerModal"
+    width="500px"
+    title="导入数据"
+    @ok="handleSubmit"
+    @cancel="handleCancel"
+  >
+    <Card style="background-color: rgb(240 240 240)" v-if="statsUpload == 'done'">
+      <h2 style="text-align: center">数据导入中...</h2>
+      <h5 style="text-align: center">请不要离开此页面</h5>
+      <Progress :percent="percentProgress" :showInfo="false" status="active" />
+    </Card>
+    <Card style="background-color: rgb(240 240 240)" v-else-if="statsUpload == 'success'">
+      <h2 style="text-align: center">成功</h2>
+      <h2 style="text-align: center"><a @click="goOnImport">继续导入</a></h2>
+    </Card>
+    <Card style="background-color: rgb(240 240 240)" v-else-if="statsUpload == 'fail'">
+      <h2 style="text-align: center">失败</h2>
+      <h5 style="text-align: center"><a @click="getExportExcel">下载失败文件</a></h5>
+      <div style="text-align: center">
+        <span> 失败: {{ importExcelInfo.fail }}</span>
+        <span> 成功: {{ importExcelInfo.success }}</span>
+        <span> 总数: {{ importExcelInfo.total }}</span>
+      </div>
+    </Card>
+    <Card style="background-color: rgb(240 240 240)" v-else>
+      <h2 style="text-align: center">选择需要导入的文件</h2>
+      <h5 style="text-align: center"
+        >若您是第一次上传文件,可先下载<a @click="downloadFile">文件模板</a>,文件大小不超过5M</h5
+      >
+      <div class="flex flex-col justify-center">
+        <Upload
+          class="button-style"
+          name="file"
+          :beforeUpload="handleBeforeUpload"
+          :maxCount="1"
+          :action="uploadApi"
+          @change="onChange"
+        >
+          <Button type="primary"> 点击上传 </Button>
+        </Upload>
+      </div>
+      <!-- //v-auth="['dialysis:patientbasic:import']" -->
+    </Card>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { onBeforeMount, onUnmounted, reactive, ref } from 'vue';
+  import { Card } from 'ant-design-vue';
+  import { Progress } from 'ant-design-vue';
+  import { Upload } from 'ant-design-vue';
+  import { Button } from '/@/components/Button';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { excelSheetDetail, getDownloadUrl, tempDownload } from '/@/api/common/index';
+  const globSetting = useGlobSetting();
+  import { listDictModel } from '/@/api/common';
+  import { downloadByBase64, downloadByUrl } from '/@/utils/file/download';
+  const uploadApi = globSetting.apiUrl + '/archives/patientBasic/import/batch';
+  const fileUpload = ref();
+  const typeOptions = ref();
+  const statsUpload = ref('default');
+  const timer = ref(null);
+  const exportFileId = ref('');
+  const percentProgress = ref(0);
+  const importExcelInfo = reactive({
+    success: 0,
+    fail: 0,
+    total: 0,
+  });
+
+  onBeforeMount(async () => {
+    typeOptions.value = await listDictModel({ dictCode: 'sys_login_log_type' });
+    fileUpload.value = '';
+  });
+  // closeModal
+  const [registerModal, { setModalProps }] = useModalInner(async data => {
+    console.log('🚀 ~ file: code.vue:21 ~ data:', data);
+    setModalProps({ confirmLoading: false });
+  });
+  async function handleBeforeUpload(file) {
+    console.log('file', file);
+    fileUpload.value = file;
+  }
+  async function handleSubmit() {
+    console.log('11111');
+  }
+  function handleCancel() {
+    statsUpload.value = 'default';
+  }
+  function goOnImport() {
+    statsUpload.value = 'default';
+  }
+  async function onChange(file) {
+    if (file.file.status === 'done') {
+      statsUpload.value = 'done';
+      const id = file.file.response.data;
+      if (id) {
+        timer.value = setInterval(async function () {
+          const importStats = await excelSheetDetail(id);
+          console.log('importStats', importStats);
+          importExcelInfo.fail = importStats.failCount;
+          importExcelInfo.success = importStats.successCount;
+          importExcelInfo.total = importStats.totalCount;
+          // 进度条
+          percentProgress.value =
+            ((importStats.failCount + importStats.successCount) / importStats.totalCount) * 100;
+          console.log('进度条:', percentProgress.value);
+          const isFinish =
+            importStats.failCount + importStats.successCount == importStats.totalCount;
+          if (isFinish) {
+            clearInterval(timer.value);
+            setTimeout(() => {
+              if (importStats.failCount) {
+                statsUpload.value = 'fail';
+                exportFileId.value = importStats.exportFileId;
+                percentProgress.value = 0;
+              } else {
+                statsUpload.value = 'success';
+                percentProgress.value = 0;
+              }
+            }, 3000);
+          }
+        }, 1000);
+      }
+    }
+  }
+
+  async function getExportExcel() {
+    const res = await getDownloadUrl(exportFileId.value);
+    console.log('res', res);
+    downloadByUrl({ url: res });
+  }
+  async function downloadFile() {
+    const res = await tempDownload([]);
+    downloadByBase64(res.base64, res.fileName + '.xlsx');
+  }
+  onUnmounted(() => {
+    timer.value ? clearInterval(timer.value) : null;
+  });
+</script>
+<style lang="less" scoped>
+  .button-style {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    align-items: center;
+  }
+</style>

+ 197 - 491
src/views/biz/visit/ready/index.vue

@@ -4,14 +4,7 @@
     <a-button type="primary" @click="plusFn">
       {{ countRef }}
     </a-button> -->
-    <XTTitle title="透析病历" :right-data="titleData" />
-
-    <div class="mt-6" />
-    <XTTab type="illness11" :selected="tabSelected" :data="tabData1" @item-click="callTab" />
-    <div class="mt-6" />
-    <div class="mt-6" />
-    <XTTab type="illness11" :selected="tabSelected" :data="tabData2" @item-click="callTab" />
-    <div class="mt-6" />
+    <XTTitle title="透析病历" :right-data="titleData" @click="cellTitle" />
     <div class="flex justify-between my-4">
       <XTTab
         type="illness"
@@ -20,67 +13,12 @@
         :data="tabData"
         @item-click="callTab"
       />
-      <XTForm :form-data="formData" />
-    </div>
-    <div class="flex">
-      <Sift :data="siftData" />
-    </div>
-    <div class="m-6">
-      <BasicForm @register="registerForm" @field-value-change="filedChange" />
+      <XTForm :form-data="formData" @change="cellFrom" />
     </div>
     <div>
-      <BasicTable @register="registerTable">
-        <template #headerTop>
-          <span>headerTop</span>
-        </template>
-        <template #toolbar>
-          <span>toolbar</span>
-        </template>
-      </BasicTable>
-    </div>
-    <div class="mx-6 my-2">
-      <ChartsCard
-        title="透前血压趋势"
-        :has-safe="true"
-        :colors="chartData.colors"
-        :safe-range="chartData.safeRange"
-      />
-    </div>
-    <div class="mx-6 my-2">
-      <DescCard
-        id="1"
-        icon="icon-xt-add_default"
-        title="透析测量"
-        type="touxi"
-        :data="descData"
-        :right="descRight"
-      />
-    </div>
-    <div class="mx-6 my-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" @click="handleAdd">AVF</div>
-              <div class="timeline-outer_item">AVH</div>
-              <div class="timeline-outer_item">TCG</div>
-            </div>
-          </transition>
-        </template>
-      </TimeLine>
-    </div>
-    <div class="mx-6 my-2">
-      <List type="default" :data="listData" selected="0" />
-    </div>
-    <div class="mx-6 my-2">
-      <List type="attachment" :data="listData1" selected="0" :width="320" />
-    </div>
-    <div class="flex justify-between">
-      <XTCard class="m-2" :data="cardData1" @item-click="cellCard" />
-    </div>
-    <div class="flex justify-between">
-      <XTCard class="m-2" :data="cardData" @item-click="cellCard" />
+      <XTCard class="flex justify-around my-2" :data="dataCard" @item-click="cellCard" />
     </div>
+    <importView @register="registerModal" />
   </div>
 </template>
 
@@ -90,20 +28,28 @@
   import { XTCard } from '/@/components/XTCard/index';
   import { XTForm } from '/@/components/XTForm/index';
   import { XTTitle } from '/@/components/XTTitle/index';
-  import { ColorEnum } from '/@/enums/colorEnum';
-  import { BasicForm, useForm } from '/@/components/Form';
-  import { dataFormSchema, columns } from './data';
-  import { BasicTable, useTable } from '/@/components/Table';
   import { onMounted } from 'vue';
-  import ChartsCard from '/@/components/XTCard/src/ChartsCard.vue';
-  import DescCard from '/@/components/XTCard/src/DescCard.vue';
-  import TimeLine from '/@/components/XTTimeLine/src/TimeLine.vue';
-  import List from '/@/components/XTList/src/List.vue';
-  import { Sift } from '/@/components/XTList/index';
-  // import { TransitionPresets, useTransition } from '@vueuse/core';
+  import { readyPrepare, prePrepareSailing, wardInfo } from '/@/api/biz/visit/prepareApi';
+  import { TitleEnum } from './data';
+  import { nanoid } from 'nanoid';
+  import { useModal } from '/@/components/Modal';
+  import importView from './importView.vue';
+  const dataCard = ref([]) as any; // 卡片数据
+  const tabSelected = ref('0'); // 默认病区 // 病区id
+  const SailingList = ref([]) as any; // 班次列表
+  const tabData = ref([]);
+  const Status = ref(''); // 状态
+  const Name = ref('');
+  const Sailing = ref('1'); //班次
+  // const wardId = ref('');
+  // const abcdefg = [
+  //   { key: '0', label: '全部' },
+  //   { key: '1', label: 'A区' },
+  //   { key: '2', label: 'B区' },
+  // ];
+  // 默认状态
 
-  const tabSelected = ref('0');
-  const dataSource = ref([]);
+  const [registerModal, { openModal }] = useModal();
   const titleData = [
     {
       type: 'import',
@@ -119,457 +65,217 @@
       btnText: '患者建档',
     },
   ];
-  const siftData = [
-    {
-      field: 'gender',
-      label: '性别',
-      value: '男',
-    },
-    {
-      field: 'age',
-      label: '年龄区间',
-      value: '25岁-50岁',
-    },
-  ];
-  const chartData = {
-    colors: [
-      {
-        color: 'rgba(0, 117, 255, 1)',
-        label: '化验值',
-        dot: 'rgba(0, 117, 255, 1)',
-      },
-    ],
-    safeRange: [
-      {
-        // name: '60分到80分',
-        yAxis: 0,
-      },
-      {
-        yAxis: 20,
-      },
-    ],
-  };
-  const listData = [
-    {
-      id: '0',
-      title: '红细胞笔迹测定',
-      startTime: '2023-11-11 12:00',
-      endTime: '2023-12-11 12:00',
-      status: 'default',
-    },
-    {
-      id: '2',
-      title: '红细胞笔迹测定2',
-      startTime: '2023-11-11 12:00',
-      endTime: '2023-12-11 12:00',
-      status: 'default',
-    },
-  ];
-  const listData1 = [
-    {
-      id: '0',
-      title: '红细胞笔迹测定',
-      startTime: '2023-11-11 12:00',
-      endTime: '2023-12-11 12:00',
-      status: 'default',
-    },
-    {
-      id: '2',
-      title: '红细胞笔迹测定2',
-      startTime: '2023-11-11 12:00',
-      endTime: '2023-12-11 12:00',
-      status: 'default',
-    },
-  ];
 
-  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,
-  };
-
-  const timeLineData = [
-    {
-      id: '1',
-      dot: '王医生',
-      date: '2023-05-20',
-      cnt: {
-        title: '测试' + Math.round(Math.random() * 1000),
-        id: '123',
-        type: '123',
-        data: descData,
-      },
-    },
-    {
-      id: '12',
-      dot: '范医生',
-      date: '2023-03-20',
-      cnt: {
-        title: '测试' + Math.round(Math.random() * 1000),
-        id: '123',
-        type: '123',
-        data: descData,
-      },
-    },
-  ];
-  const timeOuter = ref(false);
   onMounted(async () => {
-    for (let i = 0; i < 10; i++) {
+    const PrepareData = await readyPrepare({ sailingSort: '1' });
+    const SailingData = await prePrepareSailing();
+    const wardData = await wardInfo();
+    for (const i of SailingData) {
       const obj = {
-        createTime: '2023-05-23 10:09:48',
-        updateTime: '2023-05-23 19:00:32',
-        id: '1660830352886149125' + Math.round(Math.random() * 10000),
-        name: '驼人',
-        packageId: '1655202440997244930',
-        packageName: '测试套餐',
-        username: 'tuoren',
-        type: 'custom',
-        contractUser: 'Lf',
-        contactMobile: '18339543638',
-        remark: null,
-        disable: 0,
+        label: i.name,
+        value: i.sort,
       };
-      dataSource.value.push(obj);
+      SailingList.value.push(obj);
     }
-
-    await setFieldsValue({
-      configName333: {
-        bool: 1,
-        remark: '321321dsada',
-        dictValues: ['DIC_BIZ', '-1'],
-      },
-      configValue: [100, 150],
-      idCard: {
-        input: '测试',
-        dictValue: 'DICT_SYS',
-      },
-      apiCheck: ['pump_single'],
-    });
-  });
-  // const count = ref(0);
-  // const countRef = useTransition(count, {
-  //   duration: 1000,
-  //   transition: TransitionPresets.easeInCirc,
-  // });
-  // function plusFn() {
-  //   count.value = Math.round(Math.random() * 100);
-  // }
-  // const [registerTable, { reload, getCacheColumns, setColumns }] = useTable({
-  const [registerTable] = useTable({
-    id: 'sys_config',
-    title: '参数列表2121',
-    columns,
-    dataSource: dataSource,
-    useSearchForm: false,
-    titleStyle: {
-      fontSize: '14px',
-      color: '#000a18',
-      cursor: 'default',
-    },
-    titleLined: true,
-    bordered: false,
-    showIndexColumn: false,
-    pagination: false,
-    // maxHeight: 350,
-    canResize: false,
-  });
-
-  const [registerForm, { setFieldsValue }] = useForm({
-    layout: 'vertical',
-    labelWidth: '100%',
-    baseColProps: {
-      span: 8,
-    },
-    baseRowGutter: [16, 8],
-    schemas: dataFormSchema,
-    showActionButtonGroup: false,
-    actionColOptions: {
-      span: 8,
-    },
-    showResetButton: false,
-    submitButtonOptions: {
-      text: '添加',
-    },
+    for (const i of wardData) {
+      if (!i.disable) {
+        const obj = {
+          key: i.id,
+          label: i.name,
+        };
+        tabData.value.push(obj);
+      }
+    }
+    console.log('PrepareData', PrepareData);
+    for (const i in PrepareData) {
+      // console.log('i', i, PrepareData[i]);
+      if (PrepareData[i] == null) continue;
+      const obj = {
+        groupKey: PrepareData[i] ? i : nanoid(),
+        groupTit: TitleEnum[i],
+        groupMode: 'simple',
+        groupValue: [
+          {
+            value: PrepareData[i] ? PrepareData[i].length : 0,
+            color: TitleEnum[i + 'Color'],
+            background: TitleEnum[i + 'Bg'],
+          },
+        ],
+        groupValueShow: true,
+        groupData: PrepareData[i].map(ele => {
+          return {
+            id: ele.patientBasicId,
+            type: '1',
+            borderLeftColor: TitleEnum[i + 'Color'],
+            ward: ele.inpatientWard,
+            bed: ele.bed,
+            cure: 'HDF/AVF',
+            name: ele.name,
+            age: ele.age,
+            gender: ele.gender,
+            infoShow: false,
+            way: [
+              { label: '透前称量', type: '2' },
+              { label: '确认配方', type: '3' },
+            ],
+          };
+        }),
+      };
+      dataCard.value.push(obj);
+    }
+    console.log(' dataCard.value', dataCard.value);
   });
 
-  function filedChange(key, value) {
-    console.log('🚀 ~ file: index.vue:47 ~ filedChange ~ value:', value);
-    console.log('🚀 ~ file: index.vue:47 ~ filedChange ~ key:', key);
-  }
-  const tabData = [
-    {
-      key: '0',
-      label: '全部',
-      value: 128,
-      hasValue: true,
-      hasBracket: true,
-    },
-    {
-      key: '1',
-      label: 'A区',
-      value: 12,
-      hasValue: true,
-      prefixColor: '#1BC1B3',
-      valueColor: 'red',
-      hasBracket: true,
-    },
-    {
-      key: '2',
-      label: 'B区',
-      value: 18,
-      hasValue: true,
-      prefixColor: '#854AFF',
-      hasBracket: true,
-    },
-  ];
-  const tabData2 = [
-    {
-      key: '0',
-      label: 'A1',
-    },
-    {
-      key: '1',
-      label: 'A2',
-      disabled: true,
-    },
-    {
-      key: '2',
-      label: 'B3',
-    },
-  ];
-  const tabData1 = [
-    {
-      key: '0',
-      label: '全部',
-    },
-    {
-      key: '1',
-      label: 'A区',
-    },
-    {
-      key: '2',
-      label: 'B区',
-    },
-  ];
   // formdata
   const formData = [
     {
-      name: 'text',
+      name: 'prepareStatus',
       label: '全部',
       componentType: 'Select',
       placeholder: '请选择',
-      width: 80,
-      defaultValue: '1',
+      width: 100,
+      defaultValue: '',
       dicts: [
-        { label: '全部', value: '1' },
-        { label: '未称量', value: '2', prefixColor: '#1BC1B3' },
-        { label: '待确认', value: '3', prefixColor: '#854AFF' },
-        { label: '可打印', value: '4', prefixColor: '#1BC1B3' },
-        { label: '完成', value: '5', prefixColor: '#854AFF' },
+        { label: '全部', value: '' },
+        { label: '未称量', value: 'pds_unWeighed', prefixColor: TitleEnum.unWeighedColor },
+        { label: '待确认', value: 'pds_unconfirmed', prefixColor: TitleEnum.unconfirmedColor },
+        { label: '可打印', value: 'pds_printable', prefixColor: TitleEnum.printableColor },
+        { label: '已完成', value: 'pds_finish', prefixColor: TitleEnum.finishColor },
       ],
     },
     {
-      name: 'text22',
+      name: 'sailingSort',
       componentType: 'Select',
       placeholder: '请选择',
       width: 120,
-      defaultValue: '1',
+      defaultValue: '1班',
       label: '班次',
-      dicts: [
-        { label: '第一班', value: '1' },
-        { label: '第二班', value: '2' },
-        { label: '第三班', value: '3' },
-      ],
+      dicts: SailingList.value,
     },
     {
-      name: 'text1',
+      name: 'name',
       componentType: 'Input',
       placeholder: '请输入',
       width: 200,
     },
-    {
-      name: 'text233',
-      componentType: 'DatePicker',
-      placeholder: '请输入',
-      format: 'YYYY-MM-DD',
-      valueFormat: 'YYYY-MM-DD',
-    },
-    {
-      name: 'text233',
-      componentType: 'RangePicker',
-      placeholder: '请输入',
-      format: 'YYYY-MM-DD',
-      valueFormat: 'YYYY-MM-DD',
-    },
-    {
-      name: 'filter',
-      componentType: 'IconBtn',
-      count: 4,
-    },
-  ];
-  // card 标签组
-  const cardData = [
-    {
-      groupKey: '123',
-      groupTit: '待确认/测试/待核对',
-      groupMode: 'default',
-      groupValue: [
-        { value: 10, color: ColorEnum.BLUE, background: ColorEnum.BLUE_BG },
-        { value: 1, color: ColorEnum.MUTED, background: ColorEnum.MUTED_BG },
-        { value: 4, color: ColorEnum.PRIMARY, background: ColorEnum.PRIMARY_BG },
-      ],
-      groupValueShow: true,
-      groupData: [
-        {
-          id: '1',
-          type: '1',
-          borderLeftColor: ColorEnum.BLUE,
-          ward: 'A区',
-          bed: '99',
-          cure: 'HDF/AVF',
-          name: '范了饭饭饭',
-          age: 20,
-          gender: '1',
-          infoShow: true,
-          info: [
-            { label: '时间', value: '4:00', span: 12 },
-            { label: '超滤量', value: '2.1', suffix: 'kg', span: 12 },
-            { label: '血流量', value: '300', span: 12 },
-            { label: '透析器', value: 'fx60', span: 12 },
-            { label: '抗凝剂', value: '低分子肝素1配上低分子肝素2', span: 24 },
-          ],
-          way: [
-            { label: '下机', type: '0' },
-            { label: '医嘱执行', type: '1', badge: 12 },
-            { label: '记录并发症', type: '2', badge: 2 },
-            { label: '交叉核对', type: '3' },
-          ],
-        },
-        {
-          id: '12',
-          type: '2',
-          borderLeftColor: ColorEnum.PRIMARY,
-          ward: 'C区',
-          bed: '99',
-          cure: 'HDF/AVF',
-          name: '范了饭饭饭',
-          age: 20,
-          gender: '1',
-          infoShow: true,
-          info: [
-            { label: '时间', value: '4:00', span: 12 },
-            { label: '超滤量', value: '2.1', suffix: 'kg', span: 12 },
-            { label: '血流量', value: '300', span: 12 },
-            { label: '透析器', value: 'fx60', span: 12 },
-            { label: '抗凝剂', value: '低分子肝素1配上低分子肝素2', span: 24 },
-          ],
-          way: [
-            { label: '下机', type: '0' },
-            { label: '医嘱执行', type: '1', badge: 12 },
-            { label: '记录并发症', type: '2', badge: 2 },
-            { label: '交叉核对', type: '3' },
-          ],
-        },
-        {
-          id: '3',
-          type: '3',
-          borderLeftColor: ColorEnum.MUTED,
-          ward: 'C区',
-          bed: '99',
-          cure: 'HDF/AVF',
-          name: '范了饭饭饭',
-          age: 20,
-          gender: '1',
-          infoShow: true,
-          info: [
-            { label: '时间', value: '4:00', span: 12 },
-            { label: '超滤量', value: '2.1', suffix: 'kg', span: 12 },
-            { label: '血流量', value: '300', span: 12 },
-            { label: '透析器', value: 'fx60', span: 12 },
-            { label: '抗凝剂', value: '低分子肝素1配上低分子肝素2', span: 24 },
-          ],
-          way: [
-            { label: '下机', type: '0' },
-            { label: '医嘱执行', type: '1', badge: 12 },
-            { label: '记录并发症', type: '2', badge: 2 },
-            { label: '交叉核对', type: '3' },
-          ],
-        },
-      ],
-    },
-  ];
-  const cardData1 = [
-    {
-      groupKey: '123',
-      groupTit: '待确认/测试',
-      groupMode: 'simple',
-      groupValue: [
-        { value: 10, color: ColorEnum.BLUE, background: ColorEnum.BLUE_BG },
-        { value: 1, color: ColorEnum.MUTED, background: ColorEnum.MUTED_BG },
-      ],
-      groupValueShow: true,
-      groupData: [
-        {
-          id: '1',
-          type: '1',
-          borderLeftColor: ColorEnum.BLUE,
-          ward: 'A区',
-          bed: '99',
-          cure: 'HDF/AVF',
-          name: '范了饭饭饭',
-          age: 20,
-          gender: '1',
-          infoShow: false,
-          way: [
-            { label: '透前称量', type: '2' },
-            { label: '确认配方', type: '3' },
-          ],
-        },
-      ],
-    },
   ];
 
-  function handleAdd() {
-    timeOuter.value = false;
-  }
   // 回调
-  function callTab(data) {
+  async function callTab(data) {
     console.log('🚀 ~ file: index.vue:41 ~ callTab ~ data:', data);
     tabSelected.value = data.value;
+    const formData = {
+      prepareStatus: Status.value,
+      sailingSort: Sailing.value,
+      name: Name.value,
+      inpatientWardId: tabSelected.value,
+    };
+    const PrepareData = await readyPrepare(formData);
+    console.log('PrepareData', PrepareData);
+    dataCard.value = [];
+    for (const i in PrepareData) {
+      // console.log('i', i, PrepareData[i]);
+      if (PrepareData[i] == null) continue;
+      const obj = {
+        groupKey: PrepareData[i] ? i : nanoid(),
+        groupTit: TitleEnum[i],
+        groupMode: 'simple',
+        groupValue: [
+          {
+            value: PrepareData[i] ? PrepareData[i].length : 0,
+            color: TitleEnum[i + 'Color'],
+            background: TitleEnum[i + 'Bg'],
+          },
+        ],
+        groupValueShow: true,
+        groupData: PrepareData[i].map(ele => {
+          return {
+            id: ele.patientBasicId,
+            type: '1',
+            borderLeftColor: TitleEnum[i + 'Color'],
+            ward: ele.inpatientWard,
+            bed: ele.bed,
+            cure: 'HDF/AVF',
+            name: ele.name,
+            age: ele.age,
+            gender: ele.gender,
+            infoShow: false,
+            way: [
+              { label: '透前称量', type: '2' },
+              { label: '确认配方', type: '3' },
+            ],
+          };
+        }),
+      };
+      dataCard.value.push(obj);
+    }
   }
 
   function cellCard(data) {
     console.log('🚀 ~ file: index.vue:106 ~ cellCard ~ data:', data);
   }
-
-  function callHover() {
-    timeOuter.value = true;
+  //  导入导出回调
+  function cellTitle(data) {
+    console.log('🚀 ~ file: index.vue:106 ~ cellTitle ~ data:', data);
+    if (data.type == 'import') {
+      openModal(true);
+    }
+  }
+  async function cellFrom(data) {
+    Status.value = data.prepareStatus;
+    if (data.sailingSort) {
+      Sailing.value = data.sailingSort;
+    }
+    Name.value = data.name;
+    data.inpatientWardId = tabSelected.value;
+    console.log('🚀 ~ file: index.vue:106 ~ cellFrom ~ data:', data);
+    if (!data.sailingSort) {
+      data.sailingSort = '1';
+    }
+    const PrepareData = await readyPrepare(data);
+    console.log('PrepareData', PrepareData);
+    dataCard.value = [];
+    for (const i in PrepareData) {
+      // console.log('i', i, PrepareData[i]);
+      if (PrepareData[i] == null) continue;
+      const obj = {
+        groupKey: PrepareData[i] ? i : nanoid(),
+        groupTit: TitleEnum[i],
+        groupMode: 'simple',
+        groupValue: [
+          {
+            value: PrepareData[i] ? PrepareData[i].length : 0,
+            color: TitleEnum[i + 'Color'],
+            background: TitleEnum[i + 'Bg'],
+          },
+        ],
+        groupValueShow: true,
+        groupData: PrepareData[i].map(ele => {
+          return {
+            id: ele.patientBasicId,
+            type: '1',
+            borderLeftColor: TitleEnum[i + 'Color'],
+            ward: ele.inpatientWard,
+            bed: ele.bed,
+            cure: 'HDF/AVF',
+            name: ele.name,
+            age: ele.age,
+            gender: ele.gender,
+            infoShow: false,
+            way: [
+              { label: '透前称量', type: '2' },
+              { label: '确认配方', type: '3' },
+            ],
+          };
+        }),
+      };
+      dataCard.value.push(obj);
+    }
   }
+
+  // function callHover() {
+  //   timeOuter.value = true;
+  // }
 </script>
 
 <style lang="less" scoped>

+ 124 - 0
src/views/biz/visit/transfer/data.ts

@@ -0,0 +1,124 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import {
+  archivesPatientBasicQueryPage,
+  archivesPatientBasicDetail,
+} from '@/api/biz/archives/patientBasicApi';
+import { listDictModel } from '/@/api/common';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '患者姓名',
+    dataIndex: 'name',
+  },
+  {
+    title: '信息',
+    dataIndex: 'message',
+  },
+  {
+    title: '交班类型',
+    dataIndex: 'type',
+  },
+  {
+    title: '交班重点内容',
+    dataIndex: 'content',
+  },
+  {
+    title: '记录日期',
+    dataIndex: 'shiftTime',
+  },
+  {
+    title: '记录人',
+    dataIndex: 'updatorName',
+  },
+];
+// 表单新增编辑
+export const dataFormSchema: FormSchema[] = [
+  {
+    label: '患者姓名',
+    field: 'patientBasicId',
+    required: true,
+    ifShow: ({ values }) => {
+      return values.id ? false : true;
+    },
+    component: 'ApiSelect',
+    componentProps: ({ formModel }) => {
+      return {
+        api: archivesPatientBasicQueryPage,
+        params: {
+          name: formModel.patientBasicName,
+          pageSize: 999,
+        },
+        labelField: 'name',
+        valueField: 'id',
+        resultField: 'data',
+        placeholder: '请输入患者姓名',
+        getPopupContainer: () => document.body,
+        showSearch: true,
+        filterOption: false,
+        onSearch: e => {
+          formModel.patientBasicName = e;
+        },
+        onChange: async e => {
+          const detail = await archivesPatientBasicDetail(e);
+          formModel.message = (detail.gender == 'pb_sex_man' ? '男' : '女') + detail.age;
+        },
+      };
+    },
+  },
+  {
+    label: '患者姓名',
+    field: 'name',
+    component: 'Input',
+    componentProps: {
+      disabled: true,
+    },
+    ifShow: ({ values }) => {
+      return values.name ? true : false;
+    },
+  },
+  {
+    label: '患者信息',
+    field: 'message',
+    component: 'Input',
+    componentProps: {
+      disabled: true,
+    },
+  },
+  {
+    label: 'ID',
+    field: 'id',
+    component: 'Input',
+    ifShow: false,
+  },
+  {
+    label: '交班日期',
+    field: 'shiftTime',
+    component: 'DatePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD',
+      placeholder: '请输入上传日期',
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD',
+    },
+  },
+  {
+    label: '交班类型',
+    field: 'type',
+    component: 'ApiSelect',
+    componentProps: {
+      placeholder: '请选择交班类型',
+      api: listDictModel,
+      params: {
+        dictCode: 'hb',
+      },
+    },
+  },
+  {
+    label: '交班内容',
+    field: 'content',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '请输入交班内容',
+    },
+  },
+];

+ 79 - 0
src/views/biz/visit/transfer/formModal.vue

@@ -0,0 +1,79 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    @ok="handleSubmit"
+    @cancel="handleCancel"
+  >
+    <BasicForm @register="registerForm">
+      <template #messageSlot="{ model, field }">
+        <div>{{ model[field] }}</div>
+      </template>
+    </BasicForm>
+  </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 { transferAdd, transferById, transferEdit } from '/@/api/biz/visit/transferApi';
+
+  const emit = defineEmits(['success', 'cancel', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增记录' : '编辑记录'));
+  const isUpdate = ref(false);
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { resetFields, validate, setFieldsValue }] = useForm({
+    layout: 'vertical',
+    showResetButton: true,
+    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;
+
+    if (unref(isUpdate)) {
+      rowId.value = data.record.id;
+      const resData = await transferById(data.record.id);
+      console.log('resData::::', resData);
+      resData.message = (resData.gender == 'pb_sex_man' ? '男' : '女') + resData.age;
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      !unref(isUpdate)
+        ? await transferAdd({ ...values })
+        : await transferEdit({ ...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, canFullscreen: false });
+    }
+  }
+
+  async function handleCancel() {
+    closeModal();
+    emit('cancel');
+  }
+</script>

+ 321 - 3
src/views/biz/visit/transfer/index.vue

@@ -1,7 +1,325 @@
 <template>
-  <div> 占位符 </div>
+  <div class="m-4">
+    <div>
+      <XTTitle title="交班记录" :right-data="titleData" @click="callTitleClick" />
+      <div class="flex items-center justify-between my-4">
+        <XTTab
+          type="illness"
+          :width="180"
+          :selected="tabSelected"
+          :data="typeOptions"
+          @item-click="callTab"
+        />
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'type'">
+          <span :class="['table-dot', 'table-dot--' + record.type]" />
+          <span> {{ formatDictValue(responseTypeOptions, record.type) }}</span>
+        </template>
+        <template v-if="column.key === 'shiftTime'">
+          {{ dayjs(record.shiftTime).format('YYYY-MM-DD') }}
+        </template>
+        <template v-if="column.key === 'message'">
+          <Icon
+            v-if="record.gender == 'pb_sex_man'"
+            icon="icon-xt-male_sm|iconfont"
+            color="#0057FF"
+          />
+          <Icon
+            v-if="record.gender == 'pb_sex_woman'"
+            icon="icon-xt-female_sm|iconfont"
+            color="#FF0066"
+          />({{ record.age }})</template
+        >
+        <template v-if="column.key === 'updatorName'">
+          <div class="colUpdateAvatar">
+            <img class="colImg" :src="record.updateAvatar" />
+            {{ record.updatorName }}
+          </div>
+        </template>
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                auth: 'visit:handoverShifts:edit',
+                icon: 'icon-xt-details_edit_default|iconfont',
+                tooltip: '编辑',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                auth: 'visit:handoverShifts:edit',
+                icon: 'icon-xt-details_delete_default|iconfont',
+                tooltip: '删除',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <FormModal @register="registerModal" @success="callSuccess" @cancel="handleCancel" />
+  </div>
 </template>
+<script lang="ts" setup>
+  import { onBeforeMount, ref } from 'vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/TableCard';
+  import { Icon } from '/@/components/Icon';
+  import dayjs from 'dayjs';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormModal from './formModal.vue';
+  import { columns } from './data';
 
-<script setup lang="ts"></script>
+  import {
+    getTypeNumber,
+    getTransgerList,
+    transferDel,
+    transferExport,
+  } from '/@/api/biz/visit/transferApi';
+  import { listDictModel } from '/@/api/common';
+  import { formatDictValue } from '/@/utils';
+  import { useModal } from '/@/components/Modal';
+  import { XTTitle } from '/@/components/XTTitle/index';
+  import { XTTab } from '/@/components/XTTab/index';
+  import { XTForm } from '/@/components/XTForm/index';
 
-<style lang="less" scoped></style>
+  // 标题数据
+  const titleData = [
+    {
+      type: 'print',
+      icon: 'icon-xt-print_default',
+    },
+    {
+      type: 'import',
+      icon: 'icon-xt-import_default',
+    },
+    {
+      type: 'add',
+      btnIcon: 'icon-xt-add_default',
+      btnText: '新增记录',
+    },
+  ];
+  // formdata
+  const formData = [
+    {
+      name: 'shiftDate',
+      componentType: 'DatePicker',
+      format: 'YYYY-MM-DD',
+      placeholder: '请选择日期',
+      width: 220,
+    },
+  ];
+  // tab 切换选中
+  const tabSelected = ref();
+  // 操作名称
+  const shiftDate = ref('');
+
+  const typeOptions = ref();
+  const responseTypeOptions = ref();
+  onBeforeMount(async () => {
+    responseTypeOptions.value = await listDictModel({ dictCode: 'hb' });
+    getTab();
+  });
+
+  const { createConfirm, createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
+
+  const tableSort = ref([
+    {
+      field: 'create_time',
+      direction: 'DESC',
+    },
+  ]) as any;
+
+  const [registerTable, { reload, getSelectRowKeys, clearSelectedRowKeys }] = useTable({
+    api: getTransgerList,
+    batchDelApi: transferDel,
+    batchExportApi: transferExport,
+    delAuthList: ['visit:handoverShifts:remove'],
+    rowKey: 'id',
+    columns,
+    showIndexColumn: true,
+    rowSelection: { type: 'checkbox' },
+    bordered: true,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+  // 详情按钮事件
+  function handleEdit(record) {
+    openModal(true, {
+      record,
+      isUpdate: true,
+    });
+  }
+
+  // 新增按钮事件
+  function callTitleClick(data) {
+    if (data.type == 'add') {
+      openModal(true, {
+        isUpdate: false,
+        record: data,
+      });
+    } else if (data.type == 'print') {
+      console.log('打印中...');
+    }
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    if (record) {
+      await transferDel([record.id]);
+      createMessage.success('删除成功!');
+      clearSelectedRowKeys();
+      await reload();
+      await getTab();
+    } else {
+      createConfirm({
+        content: '你确定要删除?',
+        iconType: 'warning',
+        onOk: async () => {
+          const keys = getSelectRowKeys();
+          await transferDel(keys);
+          createMessage.success('删除成功!');
+          await reload();
+          await getTab();
+        },
+      });
+    }
+  }
+  // 表格点击字段排序
+  function handleSortFn(sortInfo) {
+    if (sortInfo?.order && sortInfo?.columnKey) {
+      // 默认单列排序
+      tableSort.value = [
+        {
+          field: sortInfo.columnKey,
+          direction: sortInfo.order.replace(/(\w+)(end)/g, '$1').toUpperCase(),
+        },
+      ];
+    }
+  }
+
+  // 表格请求之前,对参数进行处理, 添加默认 排序
+  async function handleBeforeFetch(params) {
+    return {
+      ...params,
+      orders: tableSort.value,
+      shiftType: tabSelected.value,
+      shiftTime: shiftDate.value == '' ? undefined : shiftDate.value,
+    };
+  }
+
+  async function getTab() {
+    typeOptions.value = await listDictModel({ dictCode: 'hb' });
+    const typeNums = await getTypeNumber(); // 获取各类型数量
+    const typeList = [];
+    typeOptions.value.forEach(ele => {
+      // 变量各类型放置对应数量
+      let typeData = {};
+      Object.keys(typeNums).forEach(numKey => {
+        if (ele.value == numKey) {
+          typeData = {
+            key: ele.value,
+            label: ele.label,
+            value: typeNums[numKey],
+            hasValue: true,
+            prefixColor: ele.prefixColor,
+            hasBracket: true,
+          };
+          typeList.push(typeData);
+        }
+      });
+    });
+    typeList.splice(0, 0, {
+      key: '',
+      label: '全部',
+      value: typeNums.total,
+      hasValue: true,
+      hasBracket: true,
+    });
+    typeOptions.value = typeList;
+    tabSelected.value = typeOptions.value[0].key;
+  }
+
+  //取消按钮事件
+  async function handleCancel() {
+    clearSelectedRowKeys();
+    await reload();
+    await getTab();
+  }
+
+  // 弹窗回调事件
+  async function callSuccess({ isUpdate, values }) {
+    console.log(isUpdate);
+    console.log(values);
+    await reload();
+    await getTab();
+  }
+
+  // 组件回调
+  async function callTab(data) {
+    tabSelected.value = data.value;
+    await reload();
+  }
+  async function callForm(data) {
+    shiftDate.value = data.shiftDate == null ? '' : data.shiftDate;
+    await reload();
+  }
+</script>
+<style lang="less" scoped>
+  .table-dot {
+    display: inline-block;
+    width: 10px;
+    height: 10px;
+    margin-right: 6px;
+    border-radius: 50%;
+
+    &--hb_new_complication {
+      background-color: #f7b500;
+    }
+
+    &--hb_new_patient {
+      background-color: #1bc1b3;
+    }
+
+    &--hb_stop_dialysis {
+      background-color: #d3d8dd;
+    }
+
+    &--hb_else {
+      background-color: #854aff;
+    }
+
+    &--hb_none_heparin {
+      background-color: #2d5aff;
+    }
+  }
+
+  ::v-deep(.ant-btn-link) {
+    color: #8a99ac;
+  }
+
+  .colUpdateAvatar {
+    display: flex;
+    justify-content: center;
+    text-align: center;
+    line-height: 28px;
+  }
+
+  .colImg {
+    width: 28px;
+    height: 28px;
+    margin-right: 5px;
+  }
+</style>

+ 17 - 0
src/views/sys/sysDict/category/data.ts

@@ -24,6 +24,11 @@ export const columns: BasicColumn[] = [
     dataIndex: 'bgColor',
     width: 120,
   },
+  {
+    title: '前缀颜色',
+    dataIndex: 'prefixColor',
+    width: 120,
+  },
   {
     title: '排序',
     dataIndex: 'sort',
@@ -143,6 +148,18 @@ export const dataFormSchema: FormSchema[] = [
       };
     },
   },
+  {
+    field: 'prefixColor',
+    label: '前缀颜色',
+    component: 'FormColorPicker',
+    componentProps: ({ formModel }) => {
+      return {
+        onChange: e => {
+          formModel.prefixColor = e;
+        },
+      };
+    },
+  },
   {
     field: 'sort',
     label: '排序',

+ 5 - 0
src/views/sys/sysDict/category/index.vue

@@ -28,6 +28,11 @@
               {{ record.bgColor }}
             </Tag>
           </template>
+          <template v-if="column.key === 'prefixColor'">
+            <Tag :color="record.prefixColor || '#000'">
+              {{ record.prefixColor }}
+            </Tag>
+          </template>
           <template v-if="column.key === 'action'">
             <TableAction
               :actions="[