Procházet zdrojové kódy

fix: 透析设备补充(未完成)

Tong před 2 roky
rodič
revize
41fc07b473

+ 27 - 0
src/api/biz/engineer/dialysisDeviceApi.ts

@@ -8,6 +8,12 @@ enum Api {
   engineerDialysisDeviceEdit = '/device/dialysisDevice/edit',
   engineerDialysisDeviceRemove = '/device/dialysisDevice/removeByIds',
   engineerDialysisDeviceList = '/device/dialysisDevice/query/noPage',
+
+  upkeepQueryPage = '/biz/device/dialysisMaintain/detailByDevice',
+  upkeepDetail = '/biz/device/dialysisMaintain/detail',
+  upkeepAdd = '/biz/device/dialysisMaintain/add',
+  upkeepEdit = '/biz/device/dialysisMaintain/edit',
+  upkeepRemove = '/biz/device/dialysisMaintain/removeByIds',
 }
 
 /**
@@ -72,3 +78,24 @@ export const engineerDialysisDeviceRemove = (params: Array<string | number>) =>
 export const engineerDialysisDeviceList = (params?: object) => {
   return defHttp.post({ url: Api.engineerDialysisDeviceList, params: params });
 };
+
+// 透析设备保养记录相关接口
+export const upkeepQueryPage = (params?: object) => {
+  return defHttp.post({ url: Api.upkeepQueryPage, params: params });
+};
+
+export const upkeepDetail = (id: string) => {
+  return defHttp.get({ url: Api.upkeepDetail + '/' + id });
+};
+
+export const upkeepAdd = (params?: object) => {
+  return defHttp.post({ url: Api.upkeepAdd, params: params });
+};
+
+export const upkeepEdit = (params?: object) => {
+  return defHttp.post({ url: Api.upkeepEdit, params: params });
+};
+
+export const upkeepRemove = (params: Array<string | number>) => {
+  return defHttp.post({ url: Api.upkeepRemove, params: params });
+};

+ 9 - 0
src/views/biz/engineer/dialysis/detail/data.ts

@@ -0,0 +1,9 @@
+export const BasicTab = [
+  { key: 'deviceBasic', value: 0, title: '设备信息' },
+  { key: 'runList', value: 1, title: '运行记录' },
+  { key: 'maintenanceList', value: 2, title: '维修记录' },
+  { key: 'upkeepList', value: 3, title: '保养记录' },
+  { key: 'treatmentList', value: 4, title: '消毒记录' },
+];
+
+export const BasicTabActive = BasicTab[0].key;

+ 67 - 0
src/views/biz/engineer/dialysis/detail/index.vue

@@ -0,0 +1,67 @@
+<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"
+          :class="'tab-' + item.key"
+        >
+          <!-- 基础信息 0 -->
+          <div v-if="item.value == 0 && info.id">
+            <DeviceInfo :info="info" />
+          </div>
+          <!--  运行记录1 -->
+          <div v-if="item.value == 1 && info.id"> 运行记录 </div>
+          <!-- 维修记录 2 -->
+          <div v-if="item.value == 2 && info.id">
+            <MaintenanceList :info="info" />
+          </div>
+          <!-- 保养记录 3 -->
+          <div v-if="item.value == 3 && info.id">
+            <UpkeepList :info="info" />
+          </div>
+          <!-- 消毒记录 4 -->
+          <div v-if="item.value == 4 && info.id"> 消毒记录 </div>
+        </a-tab-pane>
+      </a-tabs>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { BasicTab, BasicTabActive } from './data';
+  import { useRoute } from 'vue-router';
+  import { XTTitle } from '/@/components/XTTitle/index';
+  import DeviceInfo from '../deviceInfo/index.vue';
+  import UpkeepList from '../upkeepList/index.vue';
+  import MaintenanceList from '../maintenanceList/index.vue';
+  const route = useRoute();
+  console.log('🚀 ~ file: index.vue:25 ~ route:', route);
+  const info = ref({
+    id: String(route.query?.id),
+    name: route.query?.name,
+  });
+  const activeKey = ref(BasicTabActive);
+  const tabData = ref(BasicTab);
+  const title = `设备详情 — ${info.value.name}`;
+</script>
+
+<style lang="less" scoped>
+  ::v-deep(.ant-tabs-tab) {
+    padding: 12px;
+  }
+
+  ::v-deep(.ant-tabs-tabpane) {
+    padding: 12px;
+  }
+
+  .tab-medicalDocuments {
+    padding: 0;
+  }
+</style>

+ 29 - 0
src/views/biz/engineer/dialysis/deviceInfo/data.ts

@@ -0,0 +1,29 @@
+export const BasicData = [
+  {
+    title: '设备信息',
+    icon: 'icon-xt-edit_default',
+    type: 'basic',
+    data: [
+      { field: 'uniqueCode', label: '设备编号', value: '' },
+      { field: 'deviceType', label: '设备类型', value: '', dict: true },
+      { field: 'infectiousDiseases', label: '传染标识', value: '', dict: true },
+      { field: 'serialNumber', label: '序列号', value: '' },
+      { field: 'pumpType', label: '泵类型', value: '', dict: true },
+      { field: 'manufacturer', label: '厂家', value: '' },
+      { field: 'model', label: '型号', value: '' },
+
+      { field: '', label: '产地', value: '' },
+
+      { field: 'price', label: '采购金额(元)', value: '' },
+      { field: 'purchaseDate', label: '入账时间', value: '' },
+      { field: 'useDate', label: '使用日期', value: '' },
+      { field: 'produceDate', label: '生产日期', value: '' },
+      { field: 'warrantyPeriod', label: '保修期限(年)', value: '' },
+
+      { field: '', label: '联机设备', value: '' },
+      { field: '', label: '联机状态', value: '' },
+
+      { field: 'remark', label: '备注', value: '' },
+    ],
+  },
+];

+ 54 - 0
src/views/biz/engineer/dialysis/deviceInfo/index.vue

@@ -0,0 +1,54 @@
+<template>
+  <div>
+    <div class="mx-2 mb-6" v-for="item in basicData" :key="item.type">
+      <DescCard :title="item.title" :type="item.type" :data="item.data" :id="info.id" />
+    </div>
+  </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 { formatDictValue } from '/@/utils';
+  import { engineerDialysisDeviceDetail } from '/@/api/biz/engineer/dialysisDeviceApi';
+  import { listDictModelBatch } from '/@/api/common';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    } as any,
+  });
+  const bizDictOptions = reactive({
+    deviceType: [],
+  });
+  const bizDictData = ref([
+    { key: 'deviceType', dictCode: 'bm_det' },
+    { key: 'pumpType', dictCode: 'bm_pump' },
+    { key: 'infectiousDiseases', dictCode: 'pb_epidemic' },
+  ]);
+  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 basicData = ref(BasicData as any);
+  // 获取数据
+  async function getData() {
+    const res = await engineerDialysisDeviceDetail(props.info?.id);
+    basicData.value[0].data.forEach(ele => {
+      if (ele.dict) {
+        ele.value = formatDictValue(bizDictOptions[ele.field], res[ele.field]);
+      } else {
+        ele.value = res[ele.field];
+      }
+    });
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 143 - 0
src/views/biz/engineer/dialysis/disinfectants/data.ts

@@ -0,0 +1,143 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import { listDictModel, uploadApi } from '/@/api/common/index';
+import dayjs from 'dayjs';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '开始时间',
+    dataIndex: 'maintainTime',
+    align: 'left',
+  },
+  {
+    title: '结束时间',
+    dataIndex: 'maintainCompany',
+    align: 'left',
+  },
+  {
+    title: '内部消毒类型',
+    dataIndex: 'content',
+    align: 'left',
+  },
+  {
+    title: '内部消毒剂',
+    dataIndex: 'costYuan',
+    align: 'left',
+  },
+  {
+    title: '物表消毒剂',
+    dataIndex: 'picture',
+    align: 'left',
+  },
+  {
+    title: '细菌过滤器',
+    dataIndex: 'picture',
+    align: 'left',
+  },
+  {
+    title: '消毒液',
+    dataIndex: 'picture',
+    align: 'left',
+  },
+];
+
+export const upkeepDataFormSchema: FormSchema[] = [
+  {
+    field: 'deviceInfo',
+    component: 'PlainTitle',
+    defaultValue: '设备信息',
+  },
+  {
+    label: '设备编号',
+    field: 'uniqueCode',
+    component: 'Input',
+    slot: 'uniqueCode',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '设备厂家',
+    field: 'manufacturer',
+    component: 'Input',
+    slot: 'manufacturer',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '设备型号',
+    field: 'model',
+    component: 'Input',
+    slot: 'model',
+  },
+
+  {
+    field: 'maintenanceInfo',
+    component: 'PlainTitle',
+    defaultValue: '保养记录',
+  },
+
+  {
+    label: '保养时间',
+    field: 'maintainTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD HH:mm:ss',
+      placeholder: '请选择维修时间',
+      showTime: true,
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    defaultValue: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+  },
+  {
+    label: '保养方',
+    required: true,
+    field: 'maintainCompany',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'dmc',
+      },
+      placeholder: '请选择保养方',
+    },
+  },
+  {
+    label: '保养内容',
+    field: 'content',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入维修内容',
+    },
+  },
+  {
+    label: '保养费用(元)',
+    field: 'costYuan',
+    required: true,
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入维修内容',
+      min: 0,
+    },
+  },
+  {
+    label: '保养图片',
+    field: 'files',
+    component: 'XTUpload',
+    componentProps: ({ formModel, schema }) => {
+      return {
+        api: uploadApi,
+        maxSize: 1,
+        maxNumber: 1,
+        helpText: '仅支持上传jpg/png图片,图片大小不超过1M',
+        accept: ['image/*'],
+        onChange: data => {
+          formModel[schema.field] = data;
+        },
+      };
+    },
+  },
+];

+ 114 - 0
src/views/biz/engineer/dialysis/disinfectants/disinfectantsFormModal.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="modals">
+    <BasicModal
+      v-bind="$attrs"
+      destroyOnClose
+      @register="registerModal"
+      :title="getTitle"
+      @ok="handleSubmit"
+      :width="800"
+      @cancel="handleCancel"
+    >
+      <div class="!pl-8 !pt-4">
+        <BasicForm @register="registerForm">
+          <template #uniqueCode>
+            <span>{{ deviceInfo?.uniqueCode }}</span>
+          </template>
+          <template #manufacturer>
+            <span>{{ deviceInfo?.manufacturer }}</span>
+          </template>
+          <template #model>
+            <span>{{ deviceInfo?.model }}</span>
+          </template>
+        </BasicForm>
+      </div>
+    </BasicModal>
+  </div>
+</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 { upkeepDataFormSchema } from './data';
+  import {
+    upkeepAdd,
+    upkeepDetail,
+    upkeepEdit,
+    engineerDialysisDeviceDetail,
+  } from '/@/api/biz/engineer/dialysisDeviceApi';
+  const emit = defineEmits(['success', 'cancel', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '保养设备' : '保养设备'));
+  const isUpdate = ref(false);
+  const deviceInfo = ref();
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { resetFields, validate, setFieldsValue }] = useForm({
+    layout: 'vertical',
+    showResetButton: true,
+    labelWidth: 100,
+    schemas: upkeepDataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 12,
+    },
+    baseColProps: {
+      span: 12,
+    },
+    wrapperCol: {
+      span: 22,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    if (!data.record.name) {
+      deviceInfo.value = await engineerDialysisDeviceDetail(data.record?.id);
+    } else {
+      deviceInfo.value = data.record;
+    }
+    if (unref(isUpdate)) {
+      rowId.value = data.upkeepRecord.id;
+      const resData = await upkeepDetail(data.upkeepRecord.id);
+      console.log('resData::::', resData);
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      values.picture = values.files && values.files.map(ele => ele.id);
+      if (!isUpdate.value) {
+        values.deviceIds = [deviceInfo.value.id];
+      } else {
+        values.deviceId = deviceInfo.value.id;
+      }
+      !unref(isUpdate)
+        ? await upkeepAdd({ ...values })
+        : await upkeepEdit({ ...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>
+<style lang="less" scoped>
+  ::v-deep(.ant-modal) {
+    background-color: #000;
+  }
+</style>

+ 259 - 0
src/views/biz/engineer/dialysis/disinfectants/index.vue

@@ -0,0 +1,259 @@
+<template>
+  <div>
+    <div class="flex justify-between mb-4">
+      <div>
+        <a-button type="primary" size="large" class="btn-add" @click="handleCreate"
+          ><template #icon> <Icon icon="icon-xt-add_default|iconfont" /> </template
+          >新增消毒</a-button
+        >
+      </div>
+      <div>
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'maintainCompany'">
+          {{ formatDictValue(bizDictOptions.dmc, record.maintainCompany) }}
+        </template>
+        <template v-if="column.key === 'picture'">
+          <Image
+            v-if="record.files && record.files.length > 0"
+            :src="record.files[0].absolutePath"
+            :width="60"
+          />
+        </template>
+        <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 { onBeforeMount, onMounted, reactive, ref } from 'vue';
+  import { XTForm } from '/@/components/XTForm/index';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormModal from './upkeepFormModal.vue';
+  import Icon from '/@/components/Icon/index';
+  import { columns } from './data';
+  import { listDictModelBatch } from '/@/api/common';
+  import { upkeepQueryPage, upkeepRemove } from '/@/api/biz/engineer/dialysisDeviceApi';
+  import { useModal } from '/@/components/Modal';
+  import { Image } from 'ant-design-vue';
+  import dayjs from 'dayjs';
+  import { formatDictValue } from '/@/utils';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    },
+  });
+
+  const { createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
+  const tableSort = ref([
+    {
+      field: 'create_time',
+      direction: 'DESC',
+    },
+  ]) as any;
+
+  const dictsOption = ref([]) as any;
+  // formdata
+  const formData = [
+    {
+      label: '保养厂商',
+      name: 'maintainCompany',
+      componentType: 'Select',
+      dicts: dictsOption.value,
+      placeholder: '请选择维修方',
+      prefix: 'icon-xt-search',
+      width: 280,
+    },
+    {
+      name: 'patrolTime',
+      label: '保养时间',
+      componentType: 'RangePicker',
+      placeholder: '请选择保养时间',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  ];
+  const formValue = reactive({
+    patrolTime: [],
+    maintainCompany: '',
+  });
+
+  const [registerTable, { reload }] = useTable({
+    api: upkeepQueryPage,
+    rowKey: 'id',
+    columns,
+    showIndexColumn: false,
+    bordered: true,
+    striped: false,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+  const bizDictData = ref([{ key: 'dmc', dictCode: 'dmc' }]);
+
+  const bizDictOptions = reactive<any>({});
+  onBeforeMount(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 types = bizDictOptions.dmc;
+    dictsOption.value.push({
+      label: '全部',
+      value: '',
+    });
+    types.forEach(item => {
+      dictsOption.value.push({
+        label: item.label,
+        value: item.value,
+      });
+    });
+  });
+  onMounted(async () => {
+    // callForm({
+    //   patrolTime: [
+    //     dayjs().subtract(3, 'month').format('YYYY-MM-DD'),
+    //     dayjs().add(1, 'day').format('YYYY-MM-DD'),
+    //   ],
+    // });
+  });
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+      record: { id: props.info.id },
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record: { id: props.info.id },
+      upkeepRecord: record,
+      isUpdate: true,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    await upkeepRemove([record.id]);
+    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,
+      deviceId: props.info?.id,
+      maintainTime:
+        formValue.patrolTime && formValue.patrolTime.length > 0
+          ? [
+              formValue.patrolTime[0],
+              dayjs(formValue.patrolTime[1]).add(1, 'day').format('YYYY-MM-DD'),
+            ]
+          : undefined,
+      maintainCompany: formValue.maintainCompany,
+    };
+  }
+
+  // 弹窗回调事件
+  async function callSuccess() {
+    await reload();
+  }
+
+  // 查询回调
+  async function callForm(data) {
+    formValue.maintainCompany = data.maintainCompany || '';
+    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>

+ 2 - 4
src/views/biz/engineer/dialysis/index.vue

@@ -249,14 +249,12 @@
 
   // 详情按钮事件
   function handleView(record: Recordable) {
+    //
     router.push({
-      path: '/bizArchives/detail',
+      path: '/bizEngineer/dialysisDevices/details',
       query: {
         id: record.id,
-        accessId: record.accessId,
         name: record.name,
-        gender: formatDictValue(bizDictOptions.gender, record.gender),
-        age: record.age,
       },
     });
   }

+ 210 - 0
src/views/biz/engineer/dialysis/maintenanceList/data.ts

@@ -0,0 +1,210 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Form';
+import { listDictModel, uploadApi } from '/@/api/common';
+import dayjs from 'dayjs';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '维修时间',
+    dataIndex: 'maintenanceTime',
+    align: 'left',
+  },
+  {
+    title: '维修方',
+    dataIndex: 'maintenanceCompany',
+    align: 'left',
+  },
+  {
+    title: '故障阶段',
+    dataIndex: 'malfunctionStage',
+    align: 'left',
+  },
+  {
+    title: '故障内容',
+    dataIndex: 'malfunctionMessage',
+    align: 'left',
+  },
+  {
+    title: '维修内容',
+    dataIndex: 'repairContent',
+    align: 'left',
+  },
+  {
+    title: '解决措施',
+    dataIndex: 'countermeasure',
+    align: 'left',
+  },
+  {
+    title: '解决进度',
+    dataIndex: 'schedule',
+    align: 'left',
+  },
+  {
+    title: '维修费用(元)',
+    dataIndex: 'costYuan',
+    align: 'left',
+  },
+  {
+    title: '维修图片',
+    dataIndex: 'picture',
+    align: 'left',
+  },
+];
+
+export const maintenanceDataFormSchema: FormSchema[] = [
+  {
+    field: 'deviceInfo',
+    component: 'PlainTitle',
+    defaultValue: '设备信息',
+  },
+  {
+    label: '设备编号',
+    field: 'uniqueCode',
+    component: 'Input',
+    slot: 'uniqueCode',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '设备厂家',
+    field: 'manufacturer',
+    component: 'Input',
+    slot: 'manufacturer',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '设备型号',
+    field: 'model',
+    component: 'Input',
+    slot: 'model',
+    colProps: {
+      span: 12,
+    },
+  },
+
+  {
+    field: 'maintenanceInfo',
+    component: 'PlainTitle',
+    defaultValue: '维修记录',
+  },
+
+  {
+    label: '维修时间',
+    field: 'maintenanceTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD HH:mm:ss',
+      placeholder: '请选择维修时间',
+      showTime: true,
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    defaultValue: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+    colProps: {
+      span: 12,
+    },
+  },
+
+  {
+    label: '维修方',
+    field: 'maintenanceCompany',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'dmc',
+      },
+      placeholder: '请选择维修方',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '故障阶段',
+    field: 'maintenanceCompany',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'fs',
+      },
+      placeholder: '请选择故障阶段',
+    },
+  },
+  {
+    label: '故障内容',
+    field: 'malfunctionMessage',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入故障内容',
+    },
+  },
+  {
+    label: '维修内容',
+    field: 'repairContent',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入维修内容',
+    },
+  },
+  {
+    label: '解决措施',
+    field: 'repairContent',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入维修内容',
+    },
+  },
+  {
+    label: '解决进度',
+    field: 'schedule',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'rp',
+      },
+      placeholder: '请选择解决进度',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '维修费用(元)',
+    field: 'costYuan',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入维修费用(元)',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+
+  {
+    label: '维修图片',
+    field: 'files',
+    component: 'XTUpload',
+    componentProps: ({ formModel, schema }) => {
+      return {
+        api: uploadApi,
+        maxSize: 1,
+        maxNumber: 1,
+        helpText: '仅支持上传jpg/png图片,图片大小不超过1M',
+        accept: ['image/*'],
+        onChange: data => {
+          formModel[schema.field] = data;
+        },
+      };
+    },
+    colProps: {
+      span: 24,
+    },
+  },
+];

+ 133 - 0
src/views/biz/engineer/dialysis/maintenanceList/formDrawer.vue

@@ -0,0 +1,133 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerDrawer"
+    :title="getTitle"
+    :width="width"
+    @ok="handleSubmit"
+    :showFooter="true"
+  >
+    <BasicForm @register="registerForm" layout="vertical" />
+  </BasicDrawer>
+</template>
+<script lang="ts" setup>
+  import { ref, computed, unref } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { maintenanceDataFormSchema } from './data';
+
+  import {
+    maintenanceAdd,
+    maintenanceEdit,
+    maintenanceDetail,
+  } from '/@/api/biz/engineer/maintenanceApi';
+  import { engineerDialysisDeviceDetail } from '/@/api/biz/engineer/dialysisDeviceApi';
+  const emit = defineEmits(['success', 'register']);
+  const getTitle = computed(() => (!unref(isUpdate) ? '新增保养记录' : '编辑保养记录'));
+  const width = '35%';
+  const isUpdate = ref(false);
+  const deviceDetail = ref({
+    uniqueCode: null,
+    manufacturer: null,
+    deviceType: null,
+  });
+  const detailData = ref();
+  const rowId = ref();
+  const deviceSelIds = ref([]);
+  const { createMessage } = useMessage();
+  const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+    labelWidth: 100,
+    schemas: maintenanceDataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async data => {
+    await resetFields();
+    setDrawerProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    if (unref(isUpdate)) {
+      const resData = await maintenanceDetail(data.record.id);
+      rowId.value = resData.id;
+      detailData.value = resData;
+      await setFieldsValue({
+        ...resData,
+      });
+    } else {
+      deviceDetail.value = await engineerDialysisDeviceDetail(data.record.deviceId);
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      let updateVal = {};
+      setDrawerProps({ confirmLoading: true });
+      values.picture = values.files && values.files.map(ele => ele.id);
+      if (!isUpdate.value) {
+        if (deviceSelIds.value && deviceSelIds.value.length > 0) {
+          values.deviceIds = deviceSelIds.value;
+        } else {
+          createMessage.error('请选择需要保养的设备');
+          return false;
+        }
+      } else {
+        updateVal = { ...detailData.value, ...values };
+      }
+      !unref(isUpdate)
+        ? await maintenanceAdd({ ...values })
+        : await maintenanceEdit({ ...updateVal, id: rowId.value });
+      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      closeDrawer();
+      emit('success', {
+        isUpdate: unref(isUpdate),
+        values: { ...values, id: rowId.value },
+      });
+    } finally {
+      deviceSelIds.value = [];
+      setDrawerProps({ confirmLoading: false });
+    }
+  }
+</script>
+<style lang="less" scoped>
+  .device-info {
+    width: 100%;
+    max-height: 520px;
+    margin-top: -30px;
+    overflow: auto;
+
+    .device-card {
+      display: inline-block;
+      width: 270px;
+      height: 40px;
+      margin: 10px 20px;
+      background: #f4f6f9;
+      border-radius: 4px;
+      font-size: 14px;
+      font-weight: 500;
+      color: #000a18;
+      text-align: center;
+      line-height: 40px;
+      cursor: pointer;
+    }
+
+    .card-select {
+      background: #fff;
+      border-radius: 4px;
+      border: 1px solid #006dff;
+      color: #006dff;
+    }
+
+    .card-disable {
+      background: #fff;
+      border-radius: 4px;
+      border: 1px solid #a5a5a5;
+      color: #a5a5a5;
+      cursor: default;
+    }
+  }
+</style>

+ 266 - 0
src/views/biz/engineer/dialysis/maintenanceList/index.vue

@@ -0,0 +1,266 @@
+<template>
+  <div>
+    <div class="flex justify-between mb-4">
+      <div>
+        <a-button type="primary" size="large" class="btn-add" @click="handleCreate"
+          ><template #icon> <Icon icon="icon-xt-add_default|iconfont" /> </template
+          >新增维修</a-button
+        >
+      </div>
+      <div>
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'maintenanceCompany'">
+          <span>{{ formatDictValue(bizDictOptions.dmc, record.maintenanceCompany) }}</span>
+        </template>
+        <template v-if="column.key === 'schedule'">
+          <span>{{ formatDictValue(bizDictOptions.rp, record.schedule) }}</span>
+        </template>
+        <template v-if="column.key === 'picture'">
+          <Image
+            v-if="record.files && record.files.length > 0"
+            :src="record.files[0].absolutePath"
+            :width="60"
+          />
+        </template>
+        <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>
+    <FormDrawer @register="registerDrawer" @success="callSuccess" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onBeforeMount, onMounted, reactive, ref } from 'vue';
+  import { XTForm } from '/@/components/XTForm/index';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormDrawer from './formDrawer.vue';
+
+  import Icon from '/@/components/Icon/index';
+  import { columns } from './data';
+  import { listDictModelBatch } from '/@/api/common';
+  import { maintenanceQueryPage, maintenanceRemove } from '/@/api/biz/engineer/maintenanceApi';
+  import { Image } from 'ant-design-vue';
+  import dayjs from 'dayjs';
+  import { formatDictValue } from '/@/utils';
+  import { useDrawer } from '@/components/Drawer';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    },
+  });
+
+  const { createMessage } = useMessage();
+  const [registerDrawer, { openDrawer }] = useDrawer();
+  const tableSort = ref([
+    {
+      field: 'create_time',
+      direction: 'DESC',
+    },
+  ]) as any;
+
+  const dictsOption = ref([]) as any;
+  // formdata
+  const formData = [
+    {
+      label: '维修方',
+      name: 'maintenanceCompany',
+      componentType: 'Select',
+      dicts: dictsOption.value,
+      placeholder: '请选择维修方',
+      prefix: 'icon-xt-search',
+      width: 280,
+    },
+    {
+      name: 'patrolTime',
+      label: '维修时间',
+      componentType: 'RangePicker',
+      placeholder: '请选择维修时间',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  ];
+  const formValue = reactive({
+    patrolTime: [],
+    maintenanceCompany: '',
+  });
+
+  const [registerTable, { reload }] = useTable({
+    api: maintenanceQueryPage,
+    rowKey: 'id',
+    columns,
+    showIndexColumn: false,
+    bordered: true,
+    striped: false,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+  const bizDictData = ref([
+    { key: 'dmc', dictCode: 'dmc' },
+    { key: 'rp', dictCode: 'rp' },
+  ]);
+
+  const bizDictOptions = reactive<any>({});
+  onBeforeMount(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 types = bizDictOptions.dmc;
+    dictsOption.value.push({
+      label: '全部',
+      value: '',
+    });
+    types.forEach(item => {
+      dictsOption.value.push({
+        label: item.label,
+        value: item.value,
+      });
+    });
+  });
+  onMounted(async () => {
+    // callForm({
+    //   patrolTime: [
+    //     dayjs().subtract(3, 'month').format('YYYY-MM-DD'),
+    //     dayjs().add(1, 'day').format('YYYY-MM-DD'),
+    //   ],
+    // });
+  });
+  // 新增按钮事件
+  function handleCreate() {
+    openDrawer(true, {
+      isUpdate: false,
+      record: { id: props.info.id },
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    openDrawer(true, {
+      record: { id: props.info.id },
+      maintenanceRecord: record,
+      isUpdate: true,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    await maintenanceRemove([record.id]);
+    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,
+      deviceId: props.info?.id,
+      maintenanceTime:
+        formValue.patrolTime && formValue.patrolTime.length > 0
+          ? [
+              formValue.patrolTime[0],
+              dayjs(formValue.patrolTime[1]).add(1, 'day').format('YYYY-MM-DD'),
+            ]
+          : undefined,
+      maintenanceCompany: formValue.maintenanceCompany,
+    };
+  }
+
+  // 弹窗回调事件
+  async function callSuccess() {
+    await reload();
+  }
+
+  // 查询回调
+  async function callForm(data) {
+    formValue.maintenanceCompany = data.maintenanceCompany || '';
+    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>

+ 133 - 0
src/views/biz/engineer/dialysis/upkeepList/data.ts

@@ -0,0 +1,133 @@
+import { BasicColumn, FormSchema } from '/@/components/Table';
+import { listDictModel, uploadApi } from '/@/api/common/index';
+import dayjs from 'dayjs';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '保养时间',
+    dataIndex: 'maintainTime',
+    align: 'left',
+  },
+  {
+    title: '保养方',
+    dataIndex: 'maintainCompany',
+    align: 'left',
+  },
+  {
+    title: '保养内容',
+    dataIndex: 'content',
+    align: 'left',
+  },
+  {
+    title: '保养费用(元)',
+    dataIndex: 'costYuan',
+    align: 'left',
+  },
+  {
+    title: '保养图片',
+    dataIndex: 'picture',
+    align: 'left',
+  },
+];
+
+export const upkeepDataFormSchema: FormSchema[] = [
+  {
+    field: 'deviceInfo',
+    component: 'PlainTitle',
+    defaultValue: '设备信息',
+  },
+  {
+    label: '设备编号',
+    field: 'uniqueCode',
+    component: 'Input',
+    slot: 'uniqueCode',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '设备厂家',
+    field: 'manufacturer',
+    component: 'Input',
+    slot: 'manufacturer',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    label: '设备型号',
+    field: 'model',
+    component: 'Input',
+    slot: 'model',
+  },
+
+  {
+    field: 'maintenanceInfo',
+    component: 'PlainTitle',
+    defaultValue: '保养记录',
+  },
+
+  {
+    label: '保养时间',
+    field: 'maintainTime',
+    required: true,
+    component: 'DatePicker',
+    componentProps: {
+      format: 'YYYY-MM-DD HH:mm:ss',
+      placeholder: '请选择维修时间',
+      showTime: true,
+      getPopupContainer: () => document.body,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    defaultValue: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+  },
+  {
+    label: '保养方',
+    required: true,
+    field: 'maintainCompany',
+    component: 'ApiSelect',
+    componentProps: {
+      api: listDictModel,
+      params: {
+        dictCode: 'dmc',
+      },
+      placeholder: '请选择保养方',
+    },
+  },
+  {
+    label: '保养内容',
+    field: 'content',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入维修内容',
+    },
+  },
+  {
+    label: '保养费用(元)',
+    field: 'costYuan',
+    required: true,
+    component: 'InputNumber',
+    componentProps: {
+      placeholder: '请输入维修内容',
+      min: 0,
+    },
+  },
+  {
+    label: '保养图片',
+    field: 'files',
+    component: 'XTUpload',
+    componentProps: ({ formModel, schema }) => {
+      return {
+        api: uploadApi,
+        maxSize: 1,
+        maxNumber: 1,
+        helpText: '仅支持上传jpg/png图片,图片大小不超过1M',
+        accept: ['image/*'],
+        onChange: data => {
+          formModel[schema.field] = data;
+        },
+      };
+    },
+  },
+];

+ 259 - 0
src/views/biz/engineer/dialysis/upkeepList/index.vue

@@ -0,0 +1,259 @@
+<template>
+  <div>
+    <div class="flex justify-between mb-4">
+      <div>
+        <a-button type="primary" size="large" class="btn-add" @click="handleCreate"
+          ><template #icon> <Icon icon="icon-xt-add_default|iconfont" /> </template
+          >新增保养</a-button
+        >
+      </div>
+      <div>
+        <XTForm :form-data="formData" @change="callForm" />
+      </div>
+    </div>
+    <BasicTable @register="registerTable">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'maintainCompany'">
+          {{ formatDictValue(bizDictOptions.dmc, record.maintainCompany) }}
+        </template>
+        <template v-if="column.key === 'picture'">
+          <Image
+            v-if="record.files && record.files.length > 0"
+            :src="record.files[0].absolutePath"
+            :width="60"
+          />
+        </template>
+        <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 { onBeforeMount, onMounted, reactive, ref } from 'vue';
+  import { XTForm } from '/@/components/XTForm/index';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import FormModal from './upkeepFormModal.vue';
+  import Icon from '/@/components/Icon/index';
+  import { columns } from './data';
+  import { listDictModelBatch } from '/@/api/common';
+  import { upkeepQueryPage, upkeepRemove } from '/@/api/biz/engineer/dialysisDeviceApi';
+  import { useModal } from '/@/components/Modal';
+  import { Image } from 'ant-design-vue';
+  import dayjs from 'dayjs';
+  import { formatDictValue } from '/@/utils';
+
+  const props = defineProps({
+    info: {
+      type: Object,
+      default: () => {},
+    },
+  });
+
+  const { createMessage } = useMessage();
+  const [registerModal, { openModal }] = useModal();
+  const tableSort = ref([
+    {
+      field: 'create_time',
+      direction: 'DESC',
+    },
+  ]) as any;
+
+  const dictsOption = ref([]) as any;
+  // formdata
+  const formData = [
+    {
+      label: '保养厂商',
+      name: 'maintainCompany',
+      componentType: 'Select',
+      dicts: dictsOption.value,
+      placeholder: '请选择维修方',
+      prefix: 'icon-xt-search',
+      width: 280,
+    },
+    {
+      name: 'patrolTime',
+      label: '保养时间',
+      componentType: 'RangePicker',
+      placeholder: '请选择保养时间',
+      format: 'YYYY-MM-DD',
+      valueFormat: 'YYYY-MM-DD',
+    },
+  ];
+  const formValue = reactive({
+    patrolTime: [],
+    maintainCompany: '',
+  });
+
+  const [registerTable, { reload }] = useTable({
+    api: upkeepQueryPage,
+    rowKey: 'id',
+    columns,
+    showIndexColumn: false,
+    bordered: true,
+    striped: false,
+    actionColumn: {
+      width: 200,
+      title: '操作',
+      dataIndex: 'action',
+    },
+    beforeFetch: handleBeforeFetch,
+    sortFn: handleSortFn,
+  });
+  const bizDictData = ref([{ key: 'dmc', dictCode: 'dmc' }]);
+
+  const bizDictOptions = reactive<any>({});
+  onBeforeMount(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 types = bizDictOptions.dmc;
+    dictsOption.value.push({
+      label: '全部',
+      value: '',
+    });
+    types.forEach(item => {
+      dictsOption.value.push({
+        label: item.label,
+        value: item.value,
+      });
+    });
+  });
+  onMounted(async () => {
+    // callForm({
+    //   patrolTime: [
+    //     dayjs().subtract(3, 'month').format('YYYY-MM-DD'),
+    //     dayjs().add(1, 'day').format('YYYY-MM-DD'),
+    //   ],
+    // });
+  });
+  // 新增按钮事件
+  function handleCreate() {
+    openModal(true, {
+      isUpdate: false,
+      record: { id: props.info.id },
+    });
+  }
+
+  // 编辑按钮事件
+  function handleEdit(record: Recordable) {
+    openModal(true, {
+      record: { id: props.info.id },
+      upkeepRecord: record,
+      isUpdate: true,
+    });
+  }
+
+  // 删除按钮事件
+  async function handleDelete(record: Recordable) {
+    await upkeepRemove([record.id]);
+    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,
+      deviceId: props.info?.id,
+      maintainTime:
+        formValue.patrolTime && formValue.patrolTime.length > 0
+          ? [
+              formValue.patrolTime[0],
+              dayjs(formValue.patrolTime[1]).add(1, 'day').format('YYYY-MM-DD'),
+            ]
+          : undefined,
+      maintainCompany: formValue.maintainCompany,
+    };
+  }
+
+  // 弹窗回调事件
+  async function callSuccess() {
+    await reload();
+  }
+
+  // 查询回调
+  async function callForm(data) {
+    formValue.maintainCompany = data.maintainCompany || '';
+    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>

+ 114 - 0
src/views/biz/engineer/dialysis/upkeepList/upkeepFormModal.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="modals">
+    <BasicModal
+      v-bind="$attrs"
+      destroyOnClose
+      @register="registerModal"
+      :title="getTitle"
+      @ok="handleSubmit"
+      :width="800"
+      @cancel="handleCancel"
+    >
+      <div class="!pl-8 !pt-4">
+        <BasicForm @register="registerForm">
+          <template #uniqueCode>
+            <span>{{ deviceInfo?.uniqueCode }}</span>
+          </template>
+          <template #manufacturer>
+            <span>{{ deviceInfo?.manufacturer }}</span>
+          </template>
+          <template #model>
+            <span>{{ deviceInfo?.model }}</span>
+          </template>
+        </BasicForm>
+      </div>
+    </BasicModal>
+  </div>
+</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 { upkeepDataFormSchema } from './data';
+  import {
+    upkeepAdd,
+    upkeepDetail,
+    upkeepEdit,
+    engineerDialysisDeviceDetail,
+  } from '/@/api/biz/engineer/dialysisDeviceApi';
+  const emit = defineEmits(['success', 'cancel', 'register']);
+
+  const getTitle = computed(() => (!unref(isUpdate) ? '保养设备' : '保养设备'));
+  const isUpdate = ref(false);
+  const deviceInfo = ref();
+  const rowId = ref();
+
+  const { createMessage } = useMessage();
+  const [registerForm, { resetFields, validate, setFieldsValue }] = useForm({
+    layout: 'vertical',
+    showResetButton: true,
+    labelWidth: 100,
+    schemas: upkeepDataFormSchema,
+    showActionButtonGroup: false,
+    actionColOptions: {
+      span: 12,
+    },
+    baseColProps: {
+      span: 12,
+    },
+    wrapperCol: {
+      span: 22,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    await resetFields();
+    setModalProps({ confirmLoading: false });
+    isUpdate.value = !!data?.isUpdate;
+    if (!data.record.name) {
+      deviceInfo.value = await engineerDialysisDeviceDetail(data.record?.id);
+    } else {
+      deviceInfo.value = data.record;
+    }
+    if (unref(isUpdate)) {
+      rowId.value = data.upkeepRecord.id;
+      const resData = await upkeepDetail(data.upkeepRecord.id);
+      console.log('resData::::', resData);
+      await setFieldsValue({
+        ...resData,
+      });
+    }
+  });
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    try {
+      const values = await validate();
+      setModalProps({ confirmLoading: true });
+      values.picture = values.files && values.files.map(ele => ele.id);
+      if (!isUpdate.value) {
+        values.deviceIds = [deviceInfo.value.id];
+      } else {
+        values.deviceId = deviceInfo.value.id;
+      }
+      !unref(isUpdate)
+        ? await upkeepAdd({ ...values })
+        : await upkeepEdit({ ...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>
+<style lang="less" scoped>
+  ::v-deep(.ant-modal) {
+    background-color: #000;
+  }
+</style>