소스 검색

异常详情

zhangjie 1 년 전
부모
커밋
370656eb65

+ 5 - 0
src/api/work-hours.js

@@ -6,6 +6,11 @@ export const workHoursWaitCheckListApi = (data) =>
     url: '/api/admin/ding/exception/apply/flow/task/un_done/list',
     params: data,
   });
+export const workHoursCheckDetailApi = (id) =>
+  request({
+    url: '/api/admin/ding/exception/apply/view',
+    params: { id },
+  });
 export const workHoursDoneCheckListApi = (data) =>
   request({
     url: '/api/admin/ding/exception/apply/flow/task/done/list',

+ 28 - 4
src/components/global/attachment-view/index.vue

@@ -34,8 +34,15 @@
       </t-image-viewer>
     </div>
     <div v-if="attachments.files.length" class="attachment-link-list">
-      <t-link v-for="file in attachments.files" :key="file" theme="primary">
-        {{ file }}
+      <t-link
+        v-for="url in attachments.files"
+        :key="url"
+        theme="primary"
+        download
+        :href="url"
+        target="_block"
+      >
+        {{ url.split('/').slice(-1)[0] }}
       </t-link>
     </div>
   </div>
@@ -61,6 +68,12 @@ const props = defineProps({
       return [];
     },
   },
+  fileList: {
+    type: Array,
+    default() {
+      return [];
+    },
+  },
 });
 
 const imgVisible = ref(false);
@@ -98,8 +111,11 @@ const getAttachments = async () => {
 
   // 直接传图片数据时,直接展示
   if (props.imageList && props.imageList.length) {
-    attachments.images = props.imageList;
-    return;
+    attachments.images = [...props.imageList];
+  }
+  // 直接传图片数据时,直接展示
+  if (props.fileList && props.fileList.length) {
+    attachments.files = [...props.fileList];
   }
 
   if (!attachmentIds.value.length) return;
@@ -130,3 +146,11 @@ watch(
   { immediate: true }
 );
 </script>
+
+<style lang="less" scoped>
+.attachment-view {
+  :deep(.t-link) {
+    word-break: break-all;
+  }
+}
+</style>

+ 1 - 0
src/components/global/more-content/index.vue

@@ -33,6 +33,7 @@ const emit = defineEmits(['action']);
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
+    color: @brand-color;
   }
   .more-action {
     flex-shrink: 0;

+ 13 - 2
src/views/sop/sop-manage/device-out-in/index.vue

@@ -33,7 +33,7 @@
     <div class="flex-1 page-wrap">
       <t-table
         size="small"
-        row-key="id"
+        row-key="serialNo"
         :columns="columns"
         :data="tableData"
         bordered
@@ -61,6 +61,12 @@
         <template #status="{ col, row }">
           {{ runningStatusFilter(row[col.colKey]) }}
         </template>
+        <template #usageType="{ col, row }">
+          {{ deviceUsageTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #inOutType="{ col, row }">
+          {{ inoutTypeFilter(row[col.colKey]) }}
+        </template>
       </t-table>
     </div>
 
@@ -87,7 +93,12 @@ import { deviceOutInSearch } from '@/api/sop';
 import { omit } from 'lodash';
 import { DEVICE_USAGE_TYPE, RUNNING_STATUS } from '@/config/constants';
 import { dictToOptionList } from '@/utils/tool';
-import { timestampFilter, runningStatusFilter } from '@/utils/filter';
+import {
+  timestampFilter,
+  runningStatusFilter,
+  inoutTypeFilter,
+  deviceUsageTypeFilter,
+} from '@/utils/filter';
 import usePermission from '@/hooks/usePermission';
 const { perm } = usePermission();
 

+ 16 - 9
src/views/sop/sop-manage/sop-step/index.vue

@@ -372,7 +372,9 @@ const initFill = async () => {
   crmInfo.value = res.crmInfo;
   updateSopInfo(crmInfo.value);
   sopInfo.value.sopNo = res.sopNo;
-  currFlowTaskResultSetup.value = res.currFlowTaskResult.setup;
+  if (res.currFlowTaskResult) {
+    currFlowTaskResultSetup.value = res.currFlowTaskResult.setup;
+  }
   res.flowTaskHistoryList = res.flowTaskHistoryList || [];
   res.flowTaskHistoryList.forEach((item) => {
     item.formProperty.forEach((v) => {
@@ -391,11 +393,16 @@ const initFill = async () => {
     res.flowApproveHistoryList,
     allSteps.value
   );
-  curStep.value = res.currFlowTaskResult.taskKey;
-  const stepData = allSteps.value.find(
-    (item) => item.taskKey === curStep.value
-  );
-  curStepData.value = stepData;
+  if (res.currFlowTaskResult) {
+    curStep.value = res.currFlowTaskResult.taskKey;
+    const stepData = allSteps.value.find(
+      (item) => item.taskKey === curStep.value
+    );
+    curStepData.value = stepData;
+  } else {
+    curStepData.value = allSteps.value[0];
+    curStep.value = curStepData.value.taskKey;
+  }
 };
 const initEdit = async () => {
   loading.value = true;
@@ -425,10 +432,10 @@ const initEdit = async () => {
   curStepData.value = stepData;
 };
 const init = () => {
-  if (IS_FILL_MODE.value) {
+  if (IS_FILL_MODE.value || IS_VIEW_MODE.value) {
     // 填报
     initFill();
-  } else if (IS_EDIT_MODE.value || IS_VIEW_MODE.value) {
+  } else if (IS_EDIT_MODE.value) {
     // 编辑
     initEdit();
   } else {
@@ -457,7 +464,7 @@ const showAction = computed(() => {
 const curFormConfig = computed(() => {
   const formProperty = curStepData.value.formProperty || [];
   formProperty.forEach((item) => {
-    if (IS_EDIT_MODE.value || IS_VIEW_MODE.value) {
+    if (IS_EDIT_MODE.value) {
       item.value = allFormData.value[item.formName];
     }
 

+ 226 - 0
src/views/work-hours/work-hours-manage/abnormal-check/abnormal-detail-dialog.vue

@@ -0,0 +1,226 @@
+<template>
+  <my-drawer
+    class="sop-dialog"
+    :visible="visible"
+    header="异常详情"
+    size="80%"
+    attach="body"
+    :closeOnOverlayClick="false"
+    :close-btn="true"
+    :footer="false"
+    @close="emit('update:visible', false)"
+  >
+    <div class="sop-step">
+      <div class="t-tabs sop-step-list">
+        <div class="t-tabs__content">
+          <div class="t-tab-panel">
+            <t-form colon label-width="72px" class="sop-step-body">
+              <t-row :gutter="[0, 4]">
+                <t-col :span="3">
+                  <t-form-item label="异常编号">{{ row.code }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="服务单元">{{
+                    row.serviceName
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="SOP流水号">{{ row.sopNo }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="姓名">{{
+                    row.createRealName
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="供应商">{{
+                    row.supplierName
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="客户名称">{{
+                    row.customName
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="客户类型">{{
+                    customerTypeFilter(row.customType)
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="异常类型">{{
+                    row.dingExceptionTypeStr
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="异常日期">{{
+                    timestampFilter(row.exceptionTime)
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="补卡时间">{{
+                    timestampFilter(row.applyTime)
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="12">
+                  <t-form-item label="理由">{{ row.reason }}</t-form-item>
+                </t-col>
+                <t-col :span="12">
+                  <t-form-item label="附件/截图">
+                    <attachment-view
+                      :imageList="getUrls('image')"
+                      :fileList="getUrls('file')"
+                      :imgSize="60"
+                    ></attachment-view>
+                  </t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="审核状态">{{
+                    row.statusStr
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="当前审核人">{{
+                    row.approveUserName
+                  }}</t-form-item>
+                </t-col>
+                <t-col :span="3">
+                  <t-form-item label="申请时间">{{
+                    timestampFilter(row.createTime)
+                  }}</t-form-item>
+                </t-col>
+              </t-row>
+            </t-form>
+            <!-- setup history -->
+            <div v-if="flowApproveHistoryList.length" class="sop-step-history">
+              <div class="sop-step-history-label" @click="toViewHistory">
+                <ChevronRightDoubleIcon v-if="stepHistoryShow" />
+                <ChevronLeftDoubleIcon v-else />
+                流程动态
+              </div>
+              <transition name="fade-slide" mode="out-in" appear>
+                <div v-if="stepHistoryShow" class="sop-step-history-content">
+                  <div class="content-head">
+                    <h2>
+                      {{ row.statusStr }}
+                    </h2>
+                  </div>
+                  <div class="content-body">
+                    <t-collapse>
+                      <t-collapse-panel
+                        v-for="(item, findex) in flowApproveHistoryList"
+                        :key="findex"
+                        :header="item.taskName"
+                      >
+                        <template #headerRightContent>
+                          <t-space size="small">
+                            <span class="collapse-head-right">{{
+                              timestampFilter(item.createTime)
+                            }}</span>
+                            <t-tag
+                              :theme="
+                                item.approveOperation === 'REJECT'
+                                  ? 'danger'
+                                  : 'success'
+                              "
+                              variant="light"
+                            >
+                              {{ item.approveRemark }}
+                            </t-tag>
+                          </t-space>
+                        </template>
+                        <div class="content-detail">
+                          <div class="content-detail-head">
+                            {{ item.approveUserName || '--' }}
+                          </div>
+                          <div class="content-detail-body">
+                            <p
+                              >开始处理:{{
+                                timestampFilter(item.createTime)
+                              }}</p
+                            >
+                            <p>处理耗时:{{ item.duration }}</p>
+                          </div>
+                        </div>
+                      </t-collapse-panel>
+                    </t-collapse>
+                  </div>
+                </div>
+              </transition>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </my-drawer>
+</template>
+
+<script setup name="AbnormalDetailDialog">
+import { workHoursCheckDetailApi } from '@/api/work-hours';
+import { timeNumberToText } from '@/utils/tool';
+import { timestampFilter } from '@/utils/filter';
+import { watch } from 'vue';
+
+const emit = defineEmits(['update:visible', 'confirm']);
+const props = defineProps({
+  visible: Boolean,
+  row: {
+    type: Object,
+    default() {
+      return {};
+    },
+  },
+});
+
+const stepHistoryShow = ref(false);
+const flowApproveHistoryList = ref([]);
+
+const getUrls = (type) => {
+  const list = props.attachmentPaths.split(',');
+  const imgs = ['jpg', 'png', 'jpeg'];
+  if (type === 'image') {
+    return list.filter((item) => {
+      const evt = item.split('.').slice(-1)[0];
+      return imgs.includes(evt);
+    });
+  } else {
+    return list.filter((item) => {
+      const evt = item.split('.').slice(-1)[0];
+      return !imgs.includes(evt);
+    });
+  }
+};
+
+function getFlowApproveHistoryList(data) {
+  if (!data) return [];
+
+  let lastTime = 0;
+  return data.map((item, index) => {
+    let nitem = { ...item };
+    nitem.duration =
+      index === 0 ? '-' : timeNumberToText(item.createTime - lastTime);
+    lastTime = item.createTime;
+    nitem.taskName = item.approveSetup;
+    return nitem;
+  });
+}
+
+const initDetail = async () => {
+  stepHistoryShow.value = false;
+  flowApproveHistoryList.value = [];
+  const res = await workHoursCheckDetailApi(props.row.objId);
+  flowApproveHistoryList.value = getFlowApproveHistoryList(
+    res.flowApproveHistoryList
+  );
+};
+const toViewHistory = () => {
+  stepHistoryShow.value = !stepHistoryShow.value;
+};
+
+watch(
+  () => props.visible,
+  (val) => {
+    if (val) initDetail();
+  }
+);
+</script>

+ 45 - 10
src/views/work-hours/work-hours-manage/abnormal-check/done-check.vue

@@ -25,6 +25,12 @@
           current: pagination.pageNumber,
         }"
       >
+        <template #code="{ col, row }">
+          <more-content
+            :content="row[col.colKey]"
+            @action="handleDetail(row)"
+          ></more-content>
+        </template>
         <template #backup-time="{ col, row }">
           {{ timestampFilter(row[col.colKey]) }}
         </template>
@@ -37,11 +43,21 @@
         <template #custom-type="{ col, row }">
           {{ customerTypeFilter(row[col.colKey]) }}
         </template>
-        <template #result="{ col, row }">
-          {{ auditingResultFilter(row[col.colKey]) }}
+        <template #attachmentPaths="{ col, row }">
+          <attachment-view
+            :imageList="getUrls(row[col.colKey], 'image')"
+            :fileList="getUrls(row[col.colKey], 'file')"
+            :imgSize="60"
+          ></attachment-view>
         </template>
       </t-table>
     </div>
+
+    <!-- AbnormalDetailDialog -->
+    <abnormal-detail-dialog
+      v-model:visible="showAbnormalDetailDialog"
+      :row="curRow"
+    ></abnormal-detail-dialog>
   </div>
 </template>
 
@@ -51,13 +67,10 @@ import { omit } from 'lodash';
 
 import useFetchTable from '@/hooks/useFetchTable';
 import { workHoursDoneCheckListApi } from '@/api/work-hours';
-import {
-  timestampFilter,
-  customerTypeFilter,
-  auditingResultFilter,
-} from '@/utils/filter';
+import { timestampFilter, customerTypeFilter } from '@/utils/filter';
 import { AUDITING_RESULT } from '@/config/constants';
 import { dictToOptionList } from '@/utils/tool';
+import AbnormalDetailDialog from './abnormal-detail-dialog.vue';
 
 const fields = ref([
   {
@@ -159,7 +172,24 @@ const computedParams = computed(() => {
   return data;
 });
 
+const getUrls = (data, type) => {
+  const list = data.split(',');
+  const imgs = ['jpg', 'png', 'jpeg'];
+  if (type === 'image') {
+    return list.filter((item) => {
+      const evt = item.split('.').slice(-1)[0];
+      return imgs.includes(evt);
+    });
+  } else {
+    return list.filter((item) => {
+      const evt = item.split('.').slice(-1)[0];
+      return !imgs.includes(evt);
+    });
+  }
+};
+
 const columns = [
+  { colKey: 'code', title: '异常编号', width: 200 },
   { colKey: 'serviceName', title: '服务单元', width: 140 },
   { colKey: 'sopNo', title: 'SOP流水号', width: 200 },
   { colKey: 'createRealName', title: '姓名', width: 140 },
@@ -170,14 +200,12 @@ const columns = [
   { colKey: 'exceptionTime', title: '异常日期', width: 140 },
   { colKey: 'applyTime', title: '补卡时间', cell: 'backup-time', width: 180 },
   { colKey: 'reason', title: '理由' },
-  // TODO:附件展示
-  { colKey: 'attachmentPaths', title: '附件/截图' },
+  { colKey: 'attachmentPaths', title: '附件/截图', minWidth: 240 },
   { colKey: 'createTime', title: '申请时间', cell: 'apply-time', width: 180 },
   { colKey: 'statusStr', title: '审核状态', width: 120 },
   {
     colKey: 'dingExceptionApprove',
     title: '审核结果',
-    cell: 'result',
     width: 100,
   },
   { colKey: 'approveUsersName', title: '审核人', width: 140 },
@@ -192,4 +220,11 @@ const { pagination, tableData, search, onChange } = useFetchTable(
   workHoursDoneCheckListApi,
   { params: computedParams }
 );
+
+const showAbnormalDetailDialog = ref(false);
+const curRow = ref(null);
+const handleDetail = async (row) => {
+  curRow.value = row;
+  showAbnormalDetailDialog.value = true;
+};
 </script>

+ 50 - 3
src/views/work-hours/work-hours-manage/abnormal-check/wait-check.vue

@@ -47,6 +47,12 @@
         :selected-row-keys="selectedRowKeys"
         @select-change="selectChange"
       >
+        <template #code="{ col, row }">
+          <more-content
+            :content="row[col.colKey]"
+            @action="handleDetail(row)"
+          ></more-content>
+        </template>
         <template #backup-time="{ col, row }">
           {{ timestampFilter(row[col.colKey]) }}
         </template>
@@ -56,6 +62,13 @@
         <template #custom-type="{ col, row }">
           {{ customerTypeFilter(row[col.colKey]) }}
         </template>
+        <template #attachmentPaths="{ col, row }">
+          <attachment-view
+            :imageList="getUrls(row[col.colKey], 'image')"
+            :fileList="getUrls(row[col.colKey], 'file')"
+            :imgSize="60"
+          ></attachment-view>
+        </template>
         <template #operate="{ row }">
           <div class="table-operations" @click.stop>
             <t-link
@@ -78,6 +91,12 @@
         </template>
       </t-table>
     </div>
+
+    <!-- AbnormalDetailDialog -->
+    <abnormal-detail-dialog
+      v-model:visible="showAbnormalDetailDialog"
+      :row="curRow"
+    ></abnormal-detail-dialog>
   </div>
 </template>
 
@@ -92,6 +111,7 @@ import {
   workHoursWaitCheckAuditApi,
 } from '@/api/work-hours';
 import { timestampFilter, customerTypeFilter } from '@/utils/filter';
+import AbnormalDetailDialog from './abnormal-detail-dialog.vue';
 import usePermission from '@/hooks/usePermission';
 const { perm } = usePermission();
 
@@ -188,6 +208,22 @@ const computedParams = computed(() => {
   return data;
 });
 
+const getUrls = (data, type) => {
+  const list = data.split(',');
+  const imgs = ['jpg', 'png', 'jpeg'];
+  if (type === 'image') {
+    return list.filter((item) => {
+      const evt = item.split('.').slice(-1)[0];
+      return imgs.includes(evt);
+    });
+  } else {
+    return list.filter((item) => {
+      const evt = item.split('.').slice(-1)[0];
+      return !imgs.includes(evt);
+    });
+  }
+};
+
 const columns = [
   {
     colKey: 'row-select',
@@ -195,18 +231,22 @@ const columns = [
     width: 50,
     fixed: 'left',
   },
+  { colKey: 'code', title: '异常编号', width: 200 },
   { colKey: 'serviceName', title: '服务单元', width: 140 },
   { colKey: 'sopNo', title: 'SOP流水号', width: 200 },
   { colKey: 'createRealName', title: '姓名', width: 140 },
   { colKey: 'supplierName', title: '供应商', width: 140 },
   { colKey: 'customName', title: '客户名称', width: 140 },
   { colKey: 'customType', title: '客户类型', cell: 'custom-type', width: 120 },
-  { colKey: 'dingExceptionTypeStr', title: '异常类型' },
+  { colKey: 'dingExceptionTypeStr', title: '异常类型', width: 120 },
   { colKey: 'exceptionTime', title: '异常日期', width: 140 },
   { colKey: 'applyTime', title: '补卡时间', cell: 'backup-time', width: 180 },
   { colKey: 'reason', title: '理由' },
-  // TODO:附件展示
-  { colKey: 'attachmentPaths', title: '附件/截图' },
+  {
+    colKey: 'attachmentPaths',
+    title: '附件/截图',
+    minWidth: 240,
+  },
   { colKey: 'statusStr', title: '审核状态', width: 120 },
   { colKey: 'approveUserName', title: '当前审核人', width: 140 },
   { colKey: 'createTime', title: '申请时间', cell: 'apply-time', width: 180 },
@@ -227,6 +267,13 @@ const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
   }
 );
 
+const showAbnormalDetailDialog = ref(false);
+const curRow = ref(null);
+const handleDetail = async (row) => {
+  curRow.value = row;
+  showAbnormalDetailDialog.value = true;
+};
+
 const handleAudit = async (selectedIds, pass) => {
   if (!selectedIds.length) {
     MessagePlugin.error('请选择要通过的记录');