Bläddra i källkod

Merge branch 'release_v1.0.0' of http://git.qmth.com.cn/sop/web into dev_v1.0.1

刘洋 1 år sedan
förälder
incheckning
92939c817a
35 ändrade filer med 688 tillägg och 134 borttagningar
  1. 5 0
      src/api/work-hours.js
  2. 28 4
      src/components/global/attachment-view/index.vue
  3. 1 0
      src/components/global/more-content/index.vue
  4. 2 2
      src/config/constants.js
  5. 3 0
      src/style/global.less
  6. 16 11
      src/views/login/index.vue
  7. 28 2
      src/views/project-quality/project-quality-manage/issues-feedback/index.vue
  8. 8 0
      src/views/project-quality/project-quality-manage/issues-query/index.vue
  9. 1 1
      src/views/resource-guard/person-guard/person-allocate/index.vue
  10. 2 2
      src/views/service-unit/dispatch/dispatch-manage/index.vue
  11. 3 2
      src/views/service-unit/service-unit-manage/range-manage/index.vue
  12. 3 4
      src/views/service-unit/service-unit-manage/regional-planning/index.vue
  13. 7 7
      src/views/service-unit/service-unit-manage/unit-manage/index.vue
  14. 12 5
      src/views/sop/components/dynamic-form-item/SIGN.vue
  15. 1 1
      src/views/sop/components/dynamic-form-item/index.vue
  16. 13 2
      src/views/sop/sop-manage/device-out-in/index.vue
  17. 19 3
      src/views/sop/sop-manage/plan-change/index.vue
  18. 17 3
      src/views/sop/sop-manage/project-change-report/index.vue
  19. 122 14
      src/views/sop/sop-manage/quality-issue/index.vue
  20. 19 12
      src/views/sop/sop-manage/sop-step/index.vue
  21. 1 1
      src/views/sop/sop-monitor/delay-warning/index.vue
  22. 15 15
      src/views/sop/sop-monitor/violation-registration/index.vue
  23. 1 1
      src/views/system/config-manage/device-manage/edit-device-dialog.vue
  24. 1 1
      src/views/system/config-manage/supplier-manage/index.vue
  25. 2 0
      src/views/system/notice-log/log-manage/index.vue
  26. 16 16
      src/views/system/notice-log/notice-manage/index.vue
  27. 2 2
      src/views/system/task/task-manage/index.vue
  28. 1 0
      src/views/user/auth-manage/role-manage/index.vue
  29. 2 2
      src/views/user/auth-manage/user-manage/index.vue
  30. 1 1
      src/views/user/org-struct-manage/struct-manage/index.vue
  31. 234 0
      src/views/work-hours/work-hours-manage/abnormal-check/abnormal-detail-dialog.vue
  32. 45 10
      src/views/work-hours/work-hours-manage/abnormal-check/done-check.vue
  33. 50 3
      src/views/work-hours/work-hours-manage/abnormal-check/wait-check.vue
  34. 6 6
      src/views/work-hours/work-hours-manage/work-attendance/index.vue
  35. 1 1
      src/views/work-hours/work-hours-manage/work-statistics/index.vue

+ 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;

+ 2 - 2
src/config/constants.js

@@ -33,7 +33,7 @@ export const ABLE_TYPE = {
 };
 // 审核结果
 export const AUDITING_RESULT = {
-  NOT_PASS: '未通过',
+  NO_PASS: '未通过',
   PASS: '通过',
 };
 // 系统管理 ------->
@@ -111,7 +111,7 @@ export const ATTENDANCE_TYPE = {
 };
 export const ATTENDANCE_RESULT = {
   NORMAL: '正常',
-  EXCEPTION: '异常',
+  ABNORMAL: '异常',
 };
 // 考勤提交
 export const ATTENDANCE_SUBMIT_STATUS = {

+ 3 - 0
src/style/global.less

@@ -315,6 +315,9 @@ body {
   justify-content: space-between;
   align-items: center;
 }
+.flex-row {
+  flex-direction: row;
+}
 
 .text-indent {
   text-indent: 2em;

+ 16 - 11
src/views/login/index.vue

@@ -19,14 +19,14 @@
           <template #icon><RollbackIcon /></template>
           返回登录
         </t-button>
-        <t-button
+        <!-- <t-button
           v-else
           variant="outline"
           @click="loginType = loginType === 'ACCOUNT' ? 'PHONE' : 'ACCOUNT'"
         >
           <template #icon><SwapRightIcon /></template>
           {{ loginType === 'ACCOUNT' ? '手机号' : '账号' }}登录
-        </t-button>
+        </t-button> -->
       </div>
       <div class="title2" v-if="forgetStatus">{{ `请输入新的账号与密码` }}</div>
       <div class="title2" v-else>{{
@@ -41,16 +41,16 @@
           :rules="rules"
           v-if="loginType === 'ACCOUNT'"
         >
-          <t-form-item name="loginName">
+          <t-form-item name="mobileNumber">
             <t-input
-              v-model="formData.loginName"
+              v-model="formData.mobileNumber"
               clearable
-              placeholder="请输入账号"
+              placeholder="请输入手机号码"
               size="large"
               @enter="loginHandle"
             >
               <template #prefix-icon>
-                <desktop-icon />
+                <MobileIcon />
               </template>
             </t-input>
           </t-form-item>
@@ -192,7 +192,7 @@ const route = useRoute();
 const router = useRouter();
 
 const formData = reactive({
-  loginName: '',
+  mobileNumber: '',
   password: '',
 });
 const formData2 = reactive({
@@ -200,8 +200,13 @@ const formData2 = reactive({
   code: '',
 });
 const rules = {
-  loginName: [
-    { required: true, message: '请输入账号', type: 'error', trigger: 'change' },
+  mobileNumber: [
+    {
+      required: true,
+      message: '请输入手机号',
+      type: 'error',
+      trigger: 'change',
+    },
   ],
   password: [
     { required: true, message: '请输入密码', type: 'error', trigger: 'change' },
@@ -237,7 +242,7 @@ const loginHandle = () => {
         let params =
           loginType.value === 'ACCOUNT'
             ? {
-                loginName: formData.loginName,
+                mobileNumber: formData.mobileNumber,
                 password: getBase64(formData.password),
               }
             : formData2;
@@ -262,7 +267,7 @@ const loginHandle = () => {
 };
 const findSuccess = () => {
   forgetStatus.value = false;
-  formData.loginName = '';
+  formData.mobileNumber = '';
   formData.password = '';
 };
 </script>

+ 28 - 2
src/views/project-quality/project-quality-manage/issues-feedback/index.vue

@@ -14,6 +14,13 @@
       <template #service="{ item, params }">
         <select-service-unit v-model="params[item.prop]"></select-service-unit>
       </template>
+      <template #user="{ item, params }">
+        <select-url-user
+          v-model="params[item.prop]"
+          url="/api/admin/tb/quality/problem/apply/user/list"
+          clearable
+        ></select-url-user>
+      </template>
     </SearchForm>
     <div class="flex-1 page-wrap">
       <t-table
@@ -34,6 +41,12 @@
         :selected-row-keys="selectedRowKeys"
         @select-change="selectChange"
       >
+        <template #problemNo="{ col, row }">
+          <more-content
+            :content="row[col.colKey]"
+            @action="handleDetail(row)"
+          ></more-content>
+        </template>
         <template #custom-type="{ col, row }">
           {{ customerTypeFilter(row[col.colKey]) }}
         </template>
@@ -58,6 +71,7 @@
               v-if="perm.LINK_Approve"
               theme="primary"
               hover="color"
+              :disabled="row.status === 'FINISH' || !row.myself"
               @click="auditQualityIssueHandle(row)"
             >
               审核
@@ -72,7 +86,7 @@
       v-if="perm.LINK_Approve"
       v-model:visible="showQualityIssueDialog"
       :sop="curSopData"
-      type="fill"
+      :type="curType"
       @confirm="fetchData"
     ></quality-issue-dialog>
   </div>
@@ -126,6 +140,7 @@ const fields = ref([
     type: 'select',
     labelWidth: 100,
     colSpan: 6,
+    cell: 'user',
   },
   {
     prop: 'type',
@@ -257,7 +272,7 @@ const columns = [
     width: 180,
   },
   { colKey: 'status', title: '流程状态', cell: 'flow-status', width: 120 },
-  { colKey: 'setup', title: '当前节点', minWidth: 160 },
+  { colKey: 'taskName', title: '当前节点', minWidth: 160 },
   { colKey: 'pendApproveUsers', title: '当前负责人', width: 140 },
   {
     title: '管理',
@@ -301,12 +316,23 @@ const handleDestroy = () => {
 };
 
 const showQualityIssueDialog = ref(false);
+const curType = ref('fill');
 const auditQualityIssueHandle = (row) => {
   curSopData.value = {
     ...row,
     customManagerTypeStr: row.customTypeStr,
     customName: row.custom,
   };
+  curType.value = 'fill';
+  showQualityIssueDialog.value = true;
+};
+const handleDetail = (row) => {
+  curSopData.value = {
+    ...row,
+    customManagerTypeStr: row.customTypeStr,
+    customName: row.custom,
+  };
+  curType.value = 'view';
   showQualityIssueDialog.value = true;
 };
 </script>

+ 8 - 0
src/views/project-quality/project-quality-manage/issues-query/index.vue

@@ -4,6 +4,13 @@
       <template #service="{ item, params }">
         <select-service-unit v-model="params[item.prop]"></select-service-unit>
       </template>
+      <template #user="{ item, params }">
+        <select-url-user
+            v-model="params[item.prop]"
+            url="/api/admin/tb/quality/problem/apply/user/list"
+            clearable
+        ></select-url-user>
+      </template>
     </SearchForm>
     <div class="flex-1 page-wrap">
       <t-table
@@ -73,6 +80,7 @@ const fields = ref([
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'user',
   },
   {
     prop: 'type',

+ 1 - 1
src/views/resource-guard/person-guard/person-allocate/index.vue

@@ -101,7 +101,7 @@
           <div class="table-operations">
             <template v-if="perm.LINK_Allocate">
               <t-link
-                v-if="row.crmStatus === 'UN_PUBLISH'"
+                :disabled="row.crmStatus !== 'UN_PUBLISH'"
                 theme="primary"
                 hover="color"
                 @click="handleDeploy(row)"

+ 2 - 2
src/views/service-unit/dispatch/dispatch-manage/index.vue

@@ -79,7 +79,7 @@
           <div class="table-operations" @click.stop>
             <template v-if="perm.LINK_Delimit || perm.LINK_ReDelimit">
               <t-link
-                v-if="row.serviceUnitStatus !== 'FINISH'"
+                :disabled="row.serviceUnitStatus === 'FINISH'"
                 theme="primary"
                 hover="color"
                 @click="handleDelineation(row)"
@@ -89,7 +89,7 @@
             </template>
             <template v-if="perm.LINK_Update">
               <t-link
-                v-if="row.serviceUnitStatus !== 'FINISH'"
+                :disabled="row.serviceUnitStatus === 'FINISH'"
                 theme="primary"
                 hover="color"
                 @click="handleEdit(row)"

+ 3 - 2
src/views/service-unit/service-unit-manage/range-manage/index.vue

@@ -53,9 +53,10 @@
         <template #operate="{ row }">
           <div class="table-operations">
             <t-link
-              v-if="perm.LINK_Delete && row.serviceUnitStatus !== 'FINISH'"
-              theme="danger"
+              v-if="perm.LINK_Delete"
+              theme="primary"
               hover="color"
+              :disabled="row.serviceUnitStatus === 'FINISH'"
               @click="handleDelete(row)"
             >
               移除

+ 3 - 4
src/views/service-unit/service-unit-manage/regional-planning/index.vue

@@ -39,14 +39,12 @@
         v-loading="tableLoading"
       >
         <template #operate="{ row }">
-          <div
-            v-if="row.serviceUnitStatus !== 'FINISH'"
-            class="table-operations"
-          >
+          <div class="table-operations">
             <t-link
               v-if="perm.LINK_Update"
               theme="primary"
               hover="color"
+              :disabled="row.serviceUnitStatus === 'FINISH'"
               @click="handleEdit(row)"
             >
               修改大区
@@ -55,6 +53,7 @@
               v-if="perm.LINK_Delete"
               theme="primary"
               hover="color"
+              :disabled="row.serviceUnitStatus === 'FINISH'"
               @click="handleDelete(row)"
             >
               删除

+ 7 - 7
src/views/service-unit/service-unit-manage/unit-manage/index.vue

@@ -51,7 +51,7 @@
           <div class="table-operations">
             <template v-if="perm.LINK_Update">
               <t-link
-                v-if="row.status !== 'FINISH'"
+                :disabled="row.status === 'FINISH'"
                 theme="primary"
                 hover="color"
                 @click="handleEdit(row)"
@@ -61,7 +61,7 @@
             </template>
             <template v-if="perm.LINK_Publish">
               <t-link
-                v-if="row.status === 'NEW'"
+                :disabled="row.status !== 'NEW'"
                 theme="primary"
                 hover="color"
                 @click="handlePublish(row)"
@@ -71,7 +71,7 @@
             </template>
             <template v-if="perm.LINK_Cancel">
               <t-link
-                v-if="row.status === 'NEW'"
+                :disabled="row.status !== 'NEW'"
                 theme="primary"
                 hover="color"
                 @click="handleDestroy(row)"
@@ -81,7 +81,7 @@
             </template>
             <template v-if="perm.LINK_Restart">
               <t-link
-                v-if="row.status === 'FINISH'"
+                :disabled="row.status !== 'FINISH'"
                 theme="primary"
                 hover="color"
                 @click="handleRestart(row)"
@@ -91,7 +91,7 @@
             </template>
             <template v-if="perm.LINK_Close">
               <t-link
-                v-if="row.status === 'PUBLISH'"
+                :disabled="row.status !== 'PUBLISH'"
                 theme="primary"
                 hover="color"
                 @click="handleCloze(row)"
@@ -101,7 +101,7 @@
             </template>
             <template v-if="perm.LINK_Set">
               <t-link
-                v-if="row.status === 'NEW'"
+                :disabled="row.status !== 'NEW'"
                 theme="primary"
                 hover="color"
                 @click="handleSetGroup(row)"
@@ -246,7 +246,7 @@ const columns = [
     title: '管理',
     colKey: 'operate',
     fixed: 'right',
-    width: 240,
+    width: 320,
     cell: 'operate',
   },
 ];

+ 12 - 5
src/views/sop/components/dynamic-form-item/SIGN.vue

@@ -1,7 +1,11 @@
 <template>
   <div class="sign-box">
     <img class="sign-result-img" :src="valueData" v-if="valueData" />
-    <t-popup trigger="click" :onVisibleChange="visibleChange" v-else>
+    <t-popup
+      v-if="!valueData && !disabled"
+      trigger="click"
+      :onVisibleChange="visibleChange"
+    >
       <template #triggerElement>
         <t-button>添加签名</t-button>
       </template>
@@ -29,12 +33,15 @@ const props = defineProps({
   modelValue: { type: String },
 });
 const emit = defineEmits(['update:modelValue', 'change']);
+const disabled = computed(() => {
+  return !props.config?.writable;
+});
 
 const valueData = ref('');
 const getSignResultImg = () => {
-  // getAttachmentListByKey(key.value).then((res) => {
-  //先用体验版的测试扫码链接,只能写死传'1',后期小程序发正式版本再增加支持活的参数
-  getAttachmentListByKey('1').then((res) => {
+  getAttachmentListByKey(key.value).then((res) => {
+    //先用体验版的测试扫码链接,只能写死传'1',后期小程序发正式版本再增加支持活的参数
+    // getAttachmentListByKey('1').then((res) => {
     if (Array.isArray(res)) {
       if (res && res[0]?.url) {
         valueData.value = res[0].url;
@@ -81,7 +88,7 @@ const visibleChange = (visible) => {
 };
 const createQrcodeValue = computed(() => {
   //先用体验版的测试扫码链接,只能写死,后期小程序发正式版本再增加支持活的参数
-  return 'https://sopwxapp.qmth.com.cn/sign/?key=1';
+  return 'https://sopwxapp.qmth.com.cn/sign/?key=' + key.value;
 });
 </script>
 

+ 1 - 1
src/views/sop/components/dynamic-form-item/index.vue

@@ -37,7 +37,7 @@ const emit = defineEmits(['change']);
 
 const valueData = ref(null);
 
-const bigTitles = ['FORM_GROUP_TITLE', 'ONLY_TITLE'];
+const bigTitles = ['FORM_GROUP_TITLE', 'ONLY_TITLE', 'LABEL'];
 const isBigTitle = computed(() => {
   return bigTitles.includes(props.config.code);
 });

+ 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();
 

+ 19 - 3
src/views/sop/sop-manage/plan-change/index.vue

@@ -140,7 +140,10 @@
             </t-col>
             <t-col :span="12">
               <t-form-item label="处理结果">
-                <t-radio-group v-model="formData.projectExchangeApprove">
+                <t-radio-group
+                  v-model="formData.projectExchangeApprove"
+                  :disabled="IS_VIEW_MODE"
+                >
                   <t-radio value="FINISH">已完成</t-radio>
                   <t-radio value="NOT_UPDATE"
                     >经沟通,取消变更(原因请填写变更备注)</t-radio
@@ -153,13 +156,23 @@
             </t-col>
             <t-col :span="12">
               <t-form-item label="变更备注" name="remark">
-                <t-textarea v-model="formData.remark"></t-textarea>
+                <t-textarea
+                  v-model="formData.remark"
+                  :disabled="IS_VIEW_MODE"
+                ></t-textarea>
               </t-form-item>
             </t-col>
           </template>
         </t-row>
       </t-form>
-      <t-space class="sop-step-footer">
+      <t-space
+        v-if="IS_VIEW_MODE"
+        class="sop-step-footer"
+        style="flex-direction: row"
+      >
+        <t-button theme="primary" @click="cancelHandle">返回</t-button>
+      </t-space>
+      <t-space v-else class="sop-step-footer">
         <t-button theme="primary" @click="submitHandle">提交</t-button>
         <t-button theme="default" @click="cancelHandle">取消</t-button>
       </t-space>
@@ -275,6 +288,9 @@ function getFlowApproveHistoryList(data) {
 const IS_NEW_MODE = computed(() => {
   return props.type === 'new';
 });
+const IS_VIEW_MODE = computed(() => {
+  return props.type === 'view';
+});
 const readonly = computed(() => {
   return !IS_NEW_MODE.value;
 });

+ 17 - 3
src/views/sop/sop-manage/project-change-report/index.vue

@@ -31,6 +31,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 #createTime="{ col, row }">
           {{ timestampFilter(row[col.colKey]) }}
         </template>
@@ -40,7 +46,9 @@
         <template #operate="{ row }">
           <div v-if="perm.LINK_Finish" class="table-operations">
             <t-link
-              v-if="row.projectExchangeFlowStatus === 'UN_APPROVE'"
+              :disabled="
+                row.projectExchangeFlowStatus !== 'UN_APPROVE' || !row.myself
+              "
               theme="primary"
               hover="color"
               @click="handleApply(row)"
@@ -57,7 +65,7 @@
       v-if="perm.LINK_Finish"
       v-model:visible="showPlanChangeDialog"
       :sop="curSopData"
-      type="audit"
+      :type="curType"
       @confirm="fetchData"
     ></plan-change-dialog>
   </div>
@@ -108,9 +116,15 @@ const columns = [
 ];
 const curSopData = ref({});
 const showPlanChangeDialog = ref(false);
-
+const curType = ref('audit');
 const handleApply = (row) => {
   curSopData.value = row;
+  curType.value = 'audit';
+  showPlanChangeDialog.value = true;
+};
+const handleDetail = async (row) => {
+  curSopData.value = row;
+  curType.value = 'view';
   showPlanChangeDialog.value = true;
 };
 const fields = ref([

+ 122 - 14
src/views/sop/sop-manage/quality-issue/index.vue

@@ -82,14 +82,68 @@
             </t-col>
           </t-row>
         </t-form>
-        <t-space class="sop-step-footer">
-          <template v-if="showAction">
-            <t-button theme="primary" @click="submitHandle('START')"
-              >提交</t-button
-            >
-            <t-button theme="default" @click="cancelHandle">取消</t-button>
-          </template>
+        <t-space v-if="showAction" class="sop-step-footer">
+          <t-button theme="primary" @click="submitHandle('START')"
+            >提交</t-button
+          >
+          <t-button theme="default" @click="cancelHandle">取消</t-button>
+        </t-space>
+        <t-space v-else class="sop-step-footer" style="flex-direction: row">
+          <t-button theme="primary" @click="cancelHandle">返回</t-button>
         </t-space>
+        <!-- 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>
+                  {{ sopInfo.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>
       </t-tab-panel>
     </t-tabs>
   </div>
@@ -97,6 +151,10 @@
 
 <script setup name="QualityIssue">
 import { ref, computed, watch, reactive } from 'vue';
+import {
+  ChevronLeftDoubleIcon,
+  ChevronRightDoubleIcon,
+} from 'tdesign-icons-vue-next';
 import {
   issuesFeedbackSaveApi,
   issuesFeedbackApproveApi,
@@ -104,6 +162,9 @@ import {
 import { flowFormPropertiesApi, sopFlowViewApi } from '@/api/sop';
 import { MessagePlugin } from 'tdesign-vue-next';
 import DynamicFormItem from '../../components/dynamic-form-item/index.vue';
+import { CUSTOMER_TYPE } from '@/config/constants';
+import { timeNumberToText } from '@/utils/tool';
+import { timestampFilter } from '@/utils/filter';
 
 const props = defineProps({
   sop: {
@@ -119,9 +180,35 @@ const props = defineProps({
 });
 const emit = defineEmits(['confirm', 'cancel']);
 
+const stepHistoryShow = ref(false);
+const flowApproveHistoryList = ref([]);
+function getFlowApproveHistoryList(data, allStepData) {
+  if (!data) return [];
+
+  let setupData = {};
+  allStepData.forEach((item) => {
+    setupData[item.setup] = item.taskName;
+  });
+  let lastTime = 0;
+  return data.map((item, index) => {
+    let nitem = { ...item };
+    nitem.duration =
+      index === 0 ? '-' : timeNumberToText(item.createTime - lastTime);
+    lastTime = item.createTime;
+    nitem.taskName = setupData[item.approveSetup];
+    return nitem;
+  });
+}
+const toViewHistory = () => {
+  stepHistoryShow.value = !stepHistoryShow.value;
+};
+
 const IS_NEW_MODE = computed(() => {
   return props.type === 'new';
 });
+const IS_VIEW_MODE = computed(() => {
+  return props.type === 'view';
+});
 const IS_FILL_MODE = computed(() => {
   return props.type === 'fill';
 });
@@ -134,6 +221,7 @@ const sopInfo = reactive({
   crmNo: props.sop.crmNo,
   productName: props.sop.productName,
   crmName: props.sop.crmName,
+  statusStr: props.sop.statusStr,
 });
 
 const needValueCodes = [
@@ -197,17 +285,15 @@ const initFill = async () => {
   sopInfo.crmName = crmInfo.value.crmName;
   sopInfo.productName = crmInfo.value.productName;
   sopInfo.serviceName = crmInfo.value.serviceUnitName;
+  sopInfo.sopNo = res.sopNo;
+  sopInfo.customManagerTypeStr = CUSTOMER_TYPE[crmInfo.value.customType];
 
-  curStep.value = res.currFlowTaskResult.taskName;
-  currFlowTaskResultSetup.value = res.currFlowTaskResult.setup;
-  curStepSetup.value = res.currFlowTaskResult.setup;
   res.flowTaskHistoryList = res.flowTaskHistoryList || [];
   res.flowTaskHistoryList.forEach((item) => {
     item.formProperty.forEach((v) => {
       v.writable = false;
     });
   });
-  allSteps.value = [...res.flowTaskHistoryList, res.currFlowTaskResult];
   tabs.value = [
     ...res.flowTaskHistoryList.map((item) => {
       return {
@@ -215,16 +301,33 @@ const initFill = async () => {
         label: item.taskName,
       };
     }),
-    {
+  ];
+
+  if (res.currFlowTaskResult) {
+    curStep.value = res.currFlowTaskResult.taskName;
+    currFlowTaskResultSetup.value = res.currFlowTaskResult.setup;
+    curStepSetup.value = res.currFlowTaskResult.setup;
+
+    allSteps.value = [...res.flowTaskHistoryList, res.currFlowTaskResult];
+    tabs.value.push({
       value: res.currFlowTaskResult.taskName,
       label: res.currFlowTaskResult.taskName,
-    },
-  ];
+    });
+  } else {
+    allSteps.value = [...res.flowTaskHistoryList];
+    curStepSetup.value = allSteps.value[0].setup;
+    curStep.value = allSteps.value[0].taskName;
+  }
+
   allSteps.value.forEach((item) => {
     item.formProperty.forEach((prop) => {
       prop.value = prop.value ? JSON.parse(prop.value).value : null;
     });
   });
+  flowApproveHistoryList.value = getFlowApproveHistoryList(
+    res.flowApproveHistoryList,
+    allSteps.value
+  );
   loading.value = false;
 };
 
@@ -257,6 +360,11 @@ const curFormConfig = computed(() => {
   if (!stepData) return [];
 
   const formProperty = stepData.formProperty || [];
+  if (IS_VIEW_MODE.value) {
+    formProperty.forEach((item) => {
+      item.writable = false;
+    });
+  }
   return formProperty;
 });
 watch(curFormConfig, (val) => {

+ 19 - 12
src/views/sop/sop-manage/sop-step/index.vue

@@ -372,16 +372,28 @@ const initFill = async () => {
   crmInfo.value = res.crmInfo;
   updateSopInfo(crmInfo.value);
   sopInfo.value.sopNo = res.sopNo;
-  currFlowTaskResultSetup.value = res.currFlowTaskResult.setup;
+
   res.flowTaskHistoryList = res.flowTaskHistoryList || [];
   res.flowTaskHistoryList.forEach((item) => {
     item.formProperty.forEach((v) => {
       v.writable = false;
     });
   });
-  allSteps.value = [...res.flowTaskHistoryList, res.currFlowTaskResult].sort(
-    (a, b) => a.setup - b.setup
-  );
+  if (res.currFlowTaskResult) {
+    currFlowTaskResultSetup.value = res.currFlowTaskResult.setup;
+    allSteps.value = [...res.flowTaskHistoryList, res.currFlowTaskResult];
+
+    curStep.value = res.currFlowTaskResult.taskKey;
+    const stepData = allSteps.value.find(
+      (item) => item.taskKey === curStep.value
+    );
+    curStepData.value = stepData;
+  } else {
+    allSteps.value = [...res.flowTaskHistoryList];
+    curStepData.value = allSteps.value[0];
+    curStep.value = curStepData.value.taskKey;
+  }
+  allSteps.value = allSteps.value.sort((a, b) => a.setup - b.setup);
   allSteps.value.forEach((item) => {
     item.formProperty.forEach((prop) => {
       prop.value = prop.value ? JSON.parse(prop.value).value : null;
@@ -391,11 +403,6 @@ 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;
 };
 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];
     }
 

+ 1 - 1
src/views/sop/sop-monitor/delay-warning/index.vue

@@ -47,7 +47,7 @@
           <div class="table-operations">
             <template v-if="perm.LINK_Follow">
               <t-link
-                v-if="row.status !== 'CLOSE'"
+                :disabled="row.status === 'CLOSE'"
                 theme="primary"
                 hover="color"
                 @click="handleFollow(row)"

+ 15 - 15
src/views/sop/sop-monitor/violation-registration/index.vue

@@ -8,25 +8,25 @@
         ></select-service-unit>
       </template>
       <template #user="{ item, params }">
-<!--        <select-filter-user-->
-<!--          v-model="params[item.prop]"-->
-<!--          clearable-->
-<!--        ></select-filter-user>-->
+        <!--        <select-filter-user-->
+        <!--          v-model="params[item.prop]"-->
+        <!--          clearable-->
+        <!--        ></select-filter-user>-->
         <select-url-user
-            v-model="params[item.prop]"
-            url="/api/admin/tb/violation/user/list"
-            clearable
+          v-model="params[item.prop]"
+          url="/api/admin/tb/violation/user/list"
+          clearable
         ></select-url-user>
       </template>
       <template #creator="{ item, params }">
-<!--        <select-filter-user-->
-<!--          v-model="params[item.prop]"-->
-<!--          clearable-->
-<!--        ></select-filter-user>-->
+        <!--        <select-filter-user-->
+        <!--          v-model="params[item.prop]"-->
+        <!--          clearable-->
+        <!--        ></select-filter-user>-->
         <select-url-user
-            v-model="params[item.prop]"
-            url="/api/admin/tb/violation/creator/list"
-            clearable
+          v-model="params[item.prop]"
+          url="/api/admin/tb/violation/creator/list"
+          clearable
         ></select-url-user>
       </template>
     </SearchForm>
@@ -63,7 +63,7 @@
           <div class="table-operations">
             <template v-if="perm.LINK_Follow">
               <t-link
-                v-if="row.status !== 'CLOSE'"
+                :disabled="row.status === 'CLOSE'"
                 theme="primary"
                 hover="color"
                 @click="handleFollow(row)"

+ 1 - 1
src/views/system/config-manage/device-manage/edit-device-dialog.vue

@@ -81,7 +81,7 @@
         </t-col>
         <t-col :span="12">
           <t-form-item label="当前所在地" name="location">
-            <t-input v-model="formData.location" clearable />
+            <select-area v-model="formData.location" :level="2"></select-area>
           </t-form-item>
         </t-col>
       </t-row>

+ 1 - 1
src/views/system/config-manage/supplier-manage/index.vue

@@ -41,7 +41,7 @@
             </t-link>
             <t-link
               v-if="perm.LINK_Enable"
-              theme=" primary"
+              theme="primary"
               hover="color"
               @click="handleEnable(row)"
               >{{ enableFilter(!row.enable) }}</t-link

+ 2 - 0
src/views/system/notice-log/log-manage/index.vue

@@ -13,6 +13,8 @@
             defaultCurrent: 1,
             defaultPageSize: 10,
             onChange,
+            showJumper: true,
+            showPageSize: false,
             total: pagination.total,
             current: pagination.pageNumber,
           }"

+ 16 - 16
src/views/system/notice-log/notice-manage/index.vue

@@ -55,44 +55,44 @@
         </template>
         <template #operate="{ col, row }">
           <div class="table-operations">
-            <template v-if="perm.LINK_Cancel">
+            <template v-if="perm.LINK_Select">
               <t-link
-                v-if="row.status === 'PUBLISH'"
-                theme="danger"
+                :disabled="row.status !== 'PUBLISH'"
+                theme="primary"
                 hover="color"
-                @click="handleCancelPublish(row)"
+                @click="handleFeedbackView(row)"
               >
-                撤销发布
+                回执查询
               </t-link>
             </template>
-            <template v-if="perm.LINK_Publish">
+            <template v-if="perm.LINK_Update">
               <t-link
-                v-if="row.status !== 'PUBLISH'"
+                :disabled="row.status === 'PUBLISH'"
                 theme="primary"
                 hover="color"
-                @click="handlePublish(row)"
+                @click="handleEdit(row)"
               >
-                发布
+                修改
               </t-link>
             </template>
-            <template v-if="perm.LINK_Select">
+            <template v-if="perm.LINK_Cancel">
               <t-link
                 v-if="row.status === 'PUBLISH'"
                 theme="primary"
                 hover="color"
-                @click="handleFeedbackView(row)"
+                @click="handleCancelPublish(row)"
               >
-                回执查询
+                撤销发布
               </t-link>
             </template>
-            <template v-if="perm.LINK_Update">
+            <template v-if="perm.LINK_Publish">
               <t-link
                 v-if="row.status !== 'PUBLISH'"
                 theme="primary"
                 hover="color"
-                @click="handleEdit(row)"
+                @click="handlePublish(row)"
               >
-                修改
+                发布
               </t-link>
             </template>
           </div>
@@ -214,7 +214,7 @@ const columns = [
     title: '管理',
     colKey: 'operate',
     fixed: 'right',
-    width: 160,
+    width: 220,
   },
 ];
 const {

+ 2 - 2
src/views/system/task/task-manage/index.vue

@@ -43,7 +43,7 @@
           <div class="table-operations">
             <template v-if="perm.LINK_Export">
               <t-link
-                v-if="row.hasReportFile"
+                :disabled="!row.hasReportFile"
                 theme="primary"
                 hover="color"
                 @click="handleDownload(row, 'REPORT_FILE')"
@@ -53,7 +53,7 @@
             </template>
             <template v-if="perm.LINK_Download">
               <t-link
-                v-if="row.hasResultFile"
+                :disabled="!row.hasResultFile"
                 theme="primary"
                 hover="color"
                 @click="handleDownload(row, 'EXPORT_FILE')"

+ 1 - 0
src/views/user/auth-manage/role-manage/index.vue

@@ -20,6 +20,7 @@
           showJumper: true,
           showPageSize: false,
           total: pagination.total,
+          current: pagination.pageNumber,
         }"
         v-loading="tableLoading"
       >

+ 2 - 2
src/views/user/auth-manage/user-manage/index.vue

@@ -20,6 +20,7 @@
           showJumper: true,
           showPageSize: false,
           total: pagination.total,
+          current: pagination.pageNumber,
         }"
         v-loading="tableLoading"
       >
@@ -114,7 +115,6 @@ const {
   tableData,
   fetchData,
   onChange,
-  search,
 } = useFetchTable(getUserList);
 
 const handleAdd = () => {
@@ -170,7 +170,7 @@ const handleModifyPwd = (row) => {
 const addSuccess = () => {
   showAddUserDialog.value = false;
   MessagePlugin.success('操作成功');
-  search();
+  fetchData();
 };
 const updatePwdSuccess = () => {
   // showUpdateUserPwdDialog.value = false;

+ 1 - 1
src/views/user/org-struct-manage/struct-manage/index.vue

@@ -88,7 +88,7 @@ const columns = [
     title: '管理',
     colKey: 'operate',
     fixed: 'right',
-    width: 220,
+    width: 160,
   },
 ];
 

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

@@ -0,0 +1,234 @@
+<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="80px" 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="异常日期">{{
+                    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>
+            <t-space class="sop-step-footer" style="flex-direction: row">
+              <t-button theme="primary" @click="emit('update:visible', false)"
+                >返回</t-button
+              >
+            </t-space>
+            <!-- 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, customerTypeFilter } from '@/utils/filter';
+import { ref, watch } from 'vue';
+import {
+  ChevronLeftDoubleIcon,
+  ChevronRightDoubleIcon,
+} from 'tdesign-icons-vue-next';
+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.row.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('请选择要通过的记录');

+ 6 - 6
src/views/work-hours/work-hours-manage/work-attendance/index.vue

@@ -81,9 +81,9 @@
           <div class="table-operations" @click.stop>
             <template v-if="perm.LINK_Submit">
               <t-link
-                v-if="
-                  row.submitStatus === 'WILL_SUBMIT' ||
-                  row.submitStatus === 'AGREE_WITHDRAW'
+                :disabled="
+                  row.submitStatus !== 'WILL_SUBMIT' &&
+                  row.submitStatus !== 'AGREE_WITHDRAW'
                 "
                 theme="primary"
                 hover="color"
@@ -94,7 +94,7 @@
             </template>
             <template v-if="perm.LINK_Reject">
               <t-link
-                v-if="row.submitStatus === 'ALREADY_SUBMIT'"
+                :disabled="row.submitStatus !== 'ALREADY_SUBMIT'"
                 theme="primary"
                 hover="color"
                 @click="handleWithdraw(row)"
@@ -102,7 +102,7 @@
                 撤回
               </t-link>
               <t-link
-                v-if="row.submitStatus === 'APPLY_WITHDRAW'"
+                :disabled="row.submitStatus !== 'APPLY_WITHDRAW'"
                 theme="primary"
                 hover="color"
                 @click="handleCancelWithdraw(row)"
@@ -362,7 +362,7 @@ const columns = [
     title: '管理',
     colKey: 'operate',
     fixed: 'right',
-    width: 120,
+    width: 180,
   },
 ];
 

+ 1 - 1
src/views/work-hours/work-hours-manage/work-statistics/index.vue

@@ -67,7 +67,7 @@
         <template #operate="{ row }">
           <div v-if="perm.LINK_Reject" class="table-operations">
             <t-link
-              v-if="row.status === 'APPLY_WITHDRAW'"
+              :disabled="row.status !== 'APPLY_WITHDRAW'"
               theme="primary"
               hover="color"
               @click="handlePass(row)"