Forráskód Böngészése

Merge branch 'master' of http://git.qmth.com.cn/sop/web

刘洋 1 éve
szülő
commit
edacb7d806
33 módosított fájl, 835 hozzáadás és 351 törlés
  1. 6 5
      src/api/project-quality.js
  2. 13 0
      src/api/service-unit.js
  3. 12 6
      src/api/work-hours.js
  4. 18 6
      src/components/common/select-customer/index.vue
  5. 64 0
      src/components/common/select-filter-user/index.vue
  6. 62 0
      src/components/common/select-product/index.vue
  7. 18 6
      src/components/common/select-role/index.vue
  8. 18 6
      src/components/common/select-service-level/index.vue
  9. 38 13
      src/components/common/select-service-unit/index.vue
  10. 18 6
      src/components/common/select-supplier/index.vue
  11. 63 0
      src/components/common/select-type-user/index.vue
  12. 0 60
      src/components/common/select-user/index.vue
  13. 3 0
      src/components/global/search-form/components/search-form-item.vue
  14. 28 1
      src/config/constants.js
  15. 14 0
      src/utils/filter.js
  16. 134 51
      src/views/project-quality/project-quality-manage/issues-feedback/index.vue
  17. 105 37
      src/views/project-quality/project-quality-manage/issues-query/index.vue
  18. 14 20
      src/views/service-unit/dispatch/dispatch-manage/add-dispatch-dialog.vue
  19. 6 10
      src/views/service-unit/dispatch/dispatch-manage/index.vue
  20. 9 4
      src/views/service-unit/service-unit-manage/add-range/index.vue
  21. 1 4
      src/views/service-unit/service-unit-manage/range-manage/index.vue
  22. 2 2
      src/views/service-unit/service-unit-manage/regional-planning/add-region-dialog.vue
  23. 4 12
      src/views/service-unit/service-unit-manage/regional-planning/index.vue
  24. 2 2
      src/views/service-unit/service-unit-manage/unit-manage/add-unit-dialog.vue
  25. 2 3
      src/views/service-unit/service-unit-manage/unit-manage/index.vue
  26. 1 1
      src/views/system/config-manage/checkin-manage/edit-checkin-dialog.vue
  27. 0 1
      src/views/system/config-manage/checkin-manage/index.vue
  28. 3 5
      src/views/system/config-manage/customer-manage/edit-customer-dialog.vue
  29. 3 7
      src/views/system/config-manage/customer-manage/index.vue
  30. 1 0
      src/views/system/config-manage/service-level-manage/edit-service-level-dialog.vue
  31. 3 2
      src/views/system/notice-log/notice-manage/edit-notice-dialog.vue
  32. 1 3
      src/views/system/notice-log/notice-manage/notice-message-dialog.vue
  33. 169 78
      src/views/work-hours/work-hours-manage/work-statistics/index.vue

+ 6 - 5
src/api/project-quality.js

@@ -1,15 +1,16 @@
-import { request } from '@/utils/request.js';
+import { request, paramsSerializer } from '@/utils/request.js';
 
 // issuse-feedback
 export const issuesFeedbackListApi = (data) =>
   request({
-    url: '/api/system/issuse-feedback/list',
-    data,
+    url: ' /api/admin/tb/quality/problem/apply/query',
+    params: data,
   });
 export const issuesFeedbackDestroyApi = (ids) =>
   request({
-    url: '/api/system/issuse-feedback/destroy',
-    data: { ids },
+    url: '/api/admin/tb/quality/problem/apply/delete',
+    params: { ids },
+    paramsSerializer,
   });
 
 // issues-query

+ 13 - 0
src/api/service-unit.js

@@ -1,5 +1,13 @@
 import { request, paramsSerializer } from '@/utils/request.js';
 
+// product-list
+export const productListApi = (data) =>
+  request({
+    url: '/api/admin/tb/product/list',
+    params: data,
+    method: 'get',
+  });
+
 // dispatch-manage
 export const dispatchQueryApi = (data) =>
   request({
@@ -45,6 +53,11 @@ export const serviceUnitQueryApi = (data) =>
     url: '/api/service/service/unit/page',
     params: data,
   });
+export const serviceUnitListApi = (data) =>
+  request({
+    url: '/api/admin/common/query_service_unit',
+    params: data,
+  });
 export const serviceUnitEditApi = (data) =>
   request({
     url: '/api/service/service/unit/edit',

+ 12 - 6
src/api/work-hours.js

@@ -61,16 +61,22 @@ export const workAttendanceCancelWithdrawApi = (id) =>
 // work-statistics
 export const workStatisticsListApi = (data) =>
   request({
-    url: '/api/system/work-statistics/list',
-    data,
+    url: '/api/admin/tb/ding/query',
+    params: data,
   });
 export const workStatisticsInfoApi = (data) =>
   request({
-    url: '/api/system/work-statistics/info',
-    data,
+    url: '/api/admin/tb/ding/count',
+    params: data,
+  });
+export const workStatisticsExportApi = (data) =>
+  request({
+    url: ' /api/admin/tb/ding/export',
+    params: data,
+    download: true,
   });
 export const workStatisticsPassApi = (id) =>
   request({
-    url: '/api/system/work-statistics/pass',
-    data: { id },
+    url: '/api/admin/ding/exception/apply/save',
+    params: { id },
   });

+ 18 - 6
src/components/common/select-customer/index.vue

@@ -1,7 +1,13 @@
 <template>
-  <t-select v-model="selected" filterable v-bind="attrs" @change="onChange">
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
     <t-option
-      v-for="item in optionList.value"
+      v-for="item in optionList"
       :key="item.id"
       :value="item.id"
       :label="item.name"
@@ -10,17 +16,17 @@
 </template>
 
 <script setup name="SelectCustomer">
-import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
+import { onMounted, ref, useAttrs, watch } from 'vue';
 import { customerListApi } from '@/api/system';
 
-let optionList = reactive([]);
+let optionList = ref([]);
 let selected = ref('');
 
 const attrs = useAttrs();
 
 const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
-  modelValue: { type: [Number, String], default: '' },
+  modelValue: { type: [Number, String, Array], default: '' },
   type: { type: String, default: '' },
   typeRequired: { type: Boolean, default: false },
 });
@@ -37,8 +43,14 @@ const search = async () => {
 };
 
 const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
   emit('update:modelValue', selected.value);
-  emit('change', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
 };
 
 onMounted(() => {

+ 64 - 0
src/components/common/select-filter-user/index.vue

@@ -0,0 +1,64 @@
+<template>
+  <t-select
+    v-model="selected"
+    clearable
+    filterable
+    :loading="loading"
+    @search="search"
+    @change="onChange"
+  >
+    <t-option
+      v-for="item in optionList"
+      :key="item.id"
+      :value="item.id"
+      :label="item.realName"
+    />
+  </t-select>
+</template>
+
+<script setup name="SelectFilterUser">
+import { ref, watch } from 'vue';
+import { getUserList } from '@/api/user';
+
+const emit = defineEmits(['update:modelValue', 'change']);
+const props = defineProps({
+  modelValue: { type: [Number, String], default: '' },
+});
+
+let optionList = ref([]);
+let selected = ref('');
+let loading = ref(false);
+
+async function search(userInfo) {
+  optionList.value = [];
+  if (!userInfo) return;
+
+  if (loading.value) return;
+  loading.value = true;
+  const res = await getUserList({
+    userInfo,
+    enable: true,
+    pageNumber: 1,
+    pageSize: 10,
+  }).catch(() => {});
+  loading.value = false;
+  if (!res) return;
+
+  optionList.value = res.records;
+}
+
+const onChange = () => {
+  const selectedData = optionList.value.find(
+    (item) => selected.value === item.id
+  );
+  emit('update:modelValue', selected.value);
+  emit('change', selectedData);
+};
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    selected.value = val;
+  }
+);
+</script>

+ 62 - 0
src/components/common/select-product/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
+    <t-option
+      v-for="item in optionList"
+      :key="item.id"
+      :value="item.id"
+      :label="item.name"
+    />
+  </t-select>
+</template>
+
+<script setup name="SelectProduct">
+import { onMounted, ref, useAttrs, watch } from 'vue';
+import { productListApi } from '@/api/service-unit';
+
+let optionList = ref([]);
+let selected = ref('');
+
+const attrs = useAttrs();
+
+const emit = defineEmits(['update:modelValue', 'change']);
+const props = defineProps({
+  modelValue: { type: [Number, String, Array], default: '' },
+});
+
+const search = async () => {
+  optionList.value = [];
+  let data = { enable: true };
+  const res = await productListApi(data).catch(() => {});
+  if (!res) return;
+
+  optionList.value = res;
+};
+
+const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
+  emit('update:modelValue', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
+};
+
+onMounted(() => {
+  search();
+});
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    selected.value = val;
+  }
+);
+</script>

+ 18 - 6
src/components/common/select-role/index.vue

@@ -1,7 +1,13 @@
 <template>
-  <t-select v-model="selected" filterable v-bind="attrs" @change="onChange">
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
     <t-option
-      v-for="item in optionList.value"
+      v-for="item in optionList"
       :key="item.id"
       :value="item.id"
       :label="item.name"
@@ -10,17 +16,17 @@
 </template>
 
 <script setup name="SelectSupplier">
-import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
+import { onMounted, ref, useAttrs, watch } from 'vue';
 import { getAllRoleList } from '@/api/user';
 
-let optionList = reactive([]);
+let optionList = ref([]);
 let selected = ref('');
 
 const attrs = useAttrs();
 
 const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
-  modelValue: { type: [Number, String], default: '' },
+  modelValue: { type: [Number, String, Array], default: '' },
 });
 
 const search = async () => {
@@ -32,8 +38,14 @@ const search = async () => {
 };
 
 const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
   emit('update:modelValue', selected.value);
-  emit('change', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
 };
 
 onMounted(() => {

+ 18 - 6
src/components/common/select-service-level/index.vue

@@ -1,7 +1,13 @@
 <template>
-  <t-select v-model="selected" filterable v-bind="attrs" @change="onChange">
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
     <t-option
-      v-for="item in optionList.value"
+      v-for="item in optionList"
       :key="item.id"
       :value="item.id"
       :label="item.level"
@@ -10,17 +16,17 @@
 </template>
 
 <script setup name="SelectServiceLevel">
-import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
+import { onMounted, ref, useAttrs, watch } from 'vue';
 import { serviceLevelListApi } from '@/api/system';
 
-let optionList = reactive([]);
+let optionList = ref([]);
 let selected = ref('');
 
 const attrs = useAttrs();
 
 const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
-  modelValue: { type: [Number, String], default: '' },
+  modelValue: { type: [Number, String, Array], default: '' },
   type: { type: String, default: '' },
   typeRequired: { type: Boolean, default: false },
 });
@@ -38,8 +44,14 @@ const search = async () => {
 };
 
 const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
   emit('update:modelValue', selected.value);
-  emit('change', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
 };
 
 onMounted(() => {

+ 38 - 13
src/components/common/select-service-unit/index.vue

@@ -1,7 +1,13 @@
 <template>
-  <t-select v-model="selected" filterable v-bind="attrs" @change="onChange">
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
     <t-option
-      v-for="item in optionList.value"
+      v-for="item in optionList"
       :key="item.id"
       :value="item.id"
       :label="item.name"
@@ -10,35 +16,45 @@
 </template>
 
 <script setup name="SelectServiceUnit">
-import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
-import { serviceUnitQueryApi } from '@/api/service-unit';
+import { onMounted, ref, useAttrs, watch } from 'vue';
+import { serviceUnitListApi } from '@/api/service-unit';
 
-let optionList = reactive([]);
+let optionList = ref([]);
 let selected = ref('');
 
 const attrs = useAttrs();
 
 const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
-  modelValue: { type: [Number, String], default: '' },
-  status: { type: String },
+  modelValue: { type: [Number, String, Array], default: '' },
+  filterParams: {
+    type: Object,
+    default() {
+      // type:'',status:''
+      return {};
+    },
+  },
 });
 
 const search = async () => {
   optionList.value = [];
 
-  let data = { pageNumber: 1, pageSize: 100 };
-  if (props.status) data.status = props.status;
-
-  const res = await serviceUnitQueryApi(data).catch(() => {});
+  let data = { ...props.filterParams };
+  const res = await serviceUnitListApi(data).catch(() => {});
   if (!res) return;
 
-  optionList.value = res.records || [];
+  optionList.value = res;
 };
 
 const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
   emit('update:modelValue', selected.value);
-  emit('change', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
 };
 
 onMounted(() => {
@@ -51,4 +67,13 @@ watch(
     selected.value = val;
   }
 );
+watch(
+  () => props.filterParams,
+  (val, oldval) => {
+    if (JSON.stringify(val) !== JSON.stringify(oldval)) search();
+  },
+  {
+    deep: true,
+  }
+);
 </script>

+ 18 - 6
src/components/common/select-supplier/index.vue

@@ -1,7 +1,13 @@
 <template>
-  <t-select v-model="selected" filterable v-bind="attrs" @change="onChange">
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
     <t-option
-      v-for="item in optionList.value"
+      v-for="item in optionList"
       :key="item.id"
       :value="item.id"
       :label="item.name"
@@ -10,17 +16,17 @@
 </template>
 
 <script setup name="SelectSupplier">
-import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
+import { onMounted, ref, useAttrs, watch } from 'vue';
 import { supplierListApi } from '@/api/system';
 
-let optionList = reactive([]);
+let optionList = ref([]);
 let selected = ref('');
 
 const attrs = useAttrs();
 
 const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
-  modelValue: { type: [Number, String], default: '' },
+  modelValue: { type: [Number, String, Array], default: '' },
   type: { type: String, default: '' },
   typeRequired: { type: Boolean, default: false },
 });
@@ -38,8 +44,14 @@ const search = async () => {
 };
 
 const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
   emit('update:modelValue', selected.value);
-  emit('change', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
 };
 
 onMounted(() => {

+ 63 - 0
src/components/common/select-type-user/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <t-select
+    v-model="selected"
+    filterable
+    clearable
+    v-bind="attrs"
+    @change="onChange"
+  >
+    <t-option
+      v-for="item in optionList"
+      :key="item.id"
+      :value="item.id"
+      :label="item.realName"
+    />
+  </t-select>
+</template>
+
+<script setup name="SelectTypeUser">
+import { onMounted, ref, useAttrs, watch } from 'vue';
+import { getRoleUserList } from '@/api/user';
+
+let optionList = ref([]);
+let selected = ref('');
+
+const attrs = useAttrs();
+
+const emit = defineEmits(['update:modelValue', 'change']);
+const props = defineProps({
+  modelValue: { type: [Number, String, Array], default: '' },
+  type: { type: String, default: '' },
+});
+
+const search = async () => {
+  if (!props.type) return;
+  optionList.value = [];
+  const res = await getRoleUserList(props.type).catch(() => {});
+  if (!res) return;
+
+  optionList.value = res;
+};
+
+const onChange = () => {
+  const isMultiple = Object.prototype.hasOwnProperty.call(attrs, 'multiple');
+  const selectedData = isMultiple
+    ? optionList.value.filter(
+        (item) => selected.value && selected.value.includes(item.id)
+      )
+    : optionList.value.filter((item) => selected.value === item.id);
+  emit('update:modelValue', selected.value);
+  emit('change', isMultiple ? selectedData : selectedData[0]);
+};
+
+onMounted(() => {
+  search();
+});
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    selected.value = val;
+  }
+);
+</script>

+ 0 - 60
src/components/common/select-user/index.vue

@@ -1,60 +0,0 @@
-<template>
-  <t-select v-model="selected" filterable v-bind="attrs" @change="onChange">
-    <t-option
-      v-for="item in optionList.value"
-      :key="item.id"
-      :value="item.id"
-      :label="item.realName"
-    />
-  </t-select>
-</template>
-
-<script setup name="SelectUser">
-import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
-import { getRoleUserList, getAllUserList } from '@/api/user';
-
-let optionList = reactive([]);
-let selected = ref('');
-
-const attrs = useAttrs();
-
-const emit = defineEmits(['update:modelValue', 'change']);
-const props = defineProps({
-  modelValue: { type: [Number, String], default: '' },
-  type: { type: String, default: '' },
-  typeRequired: { type: Boolean, default: false },
-});
-
-const search = async () => {
-  if (!props.typeRequired && !props.type) {
-    optionList.value = [];
-    const res = await getAllUserList().catch(() => {});
-    if (!res) return;
-    optionList.value = res;
-    return;
-  }
-
-  if (props.typeRequired && !props.type) return;
-  optionList.value = [];
-  const res = await getRoleUserList(props.type).catch(() => {});
-  if (!res) return;
-
-  optionList.value = res;
-};
-
-const onChange = () => {
-  emit('update:modelValue', selected.value);
-  emit('change', selected.value);
-};
-
-onMounted(() => {
-  search();
-});
-
-watch(
-  () => props.modelValue,
-  (val) => {
-    selected.value = val;
-  }
-);
-</script>

+ 3 - 0
src/components/global/search-form/components/search-form-item.vue

@@ -35,6 +35,9 @@
   <template v-if="item.type == undefined || item.type == 'text'">
     <t-input v-model="params[item.prop]" v-bind="attrs" />
   </template>
+  <template v-if="item.type == 'number'">
+    <t-input-number v-model="params[item.prop]" v-bind="attrs" />
+  </template>
   <!-- 下拉选择框 -->
   <template v-if="item.type == 'select' || item.type == 'multipleSelect'">
     <t-select

+ 28 - 1
src/config/constants.js

@@ -6,7 +6,7 @@ export const ROLE_TYPE = {
   REGION_COORDINATOR: '区域协调人',
   EFFECT_ENGINEER: '实施工程师',
   ASSISTANT_ENGINEER: '助理工程师',
-  CUSTOMER_MANAGER: '客户经理',
+  ACCOUNT_MANAGER: '客户经理',
   QA: 'QA',
   CUSTOM: '技术客服',
   DEFINED: '自定义',
@@ -48,6 +48,33 @@ export const SERVICE_UNIT_STATUS = {
   CANCEL: '已作废',
 };
 
+// 项目质量管理 ------->
+// 问题类型
+export const ISSUES_TYPE = {
+  UPDATE: '修正类',
+  OPTIMIZE: '优化类',
+  NO_PROBLEM: '不是问题',
+};
+// 问题原因类型
+export const ISSUES_REASON_TYPE = {
+  EXEC: '执行类',
+  MANAGER: '管理协调类',
+  FLOW: '流程制度类',
+  PRODUCT_PROBLEM: '产品缺陷类',
+  PRODUCT_MANAGER: '产品运维类',
+  OTHER: '其它',
+};
+export const ISSUES_INFLUENCE_DEGREE = ['A', 'B', 'C', 'D'];
+
+// SOP
+export const FLOW_STATUS = {
+  START: '已开始',
+  AUDITING: '审核中',
+  REJECT: '已驳回',
+  END: '已终止',
+  FINISH: '已结束',
+};
+
 export const MESSAGE_TYPE = {
   BEFORE: '提前提醒',
   AFTER: '延期提醒',

+ 14 - 0
src/utils/filter.js

@@ -5,6 +5,9 @@ import {
   NOTICE_TYPE,
   PUBLISH_STATUS,
   SERVICE_UNIT_STATUS,
+  ISSUES_TYPE,
+  ISSUES_REASON_TYPE,
+  FLOW_STATUS,
 } from '@/config/constants';
 import { dateFormat } from './tool';
 
@@ -37,3 +40,14 @@ export function publishStatusFilter(val) {
 export function serviceUnitStatusFilter(val) {
   return SERVICE_UNIT_STATUS[val] || DEFAULT_FIELD;
 }
+// 项目质量管理
+export function issuesTypeFilter(val) {
+  return ISSUES_TYPE[val] || DEFAULT_FIELD;
+}
+export function issuesReasonTypeFilter(val) {
+  return ISSUES_REASON_TYPE[val] || DEFAULT_FIELD;
+}
+// sop
+export function flowStatusFilter(val) {
+  return FLOW_STATUS[val] || DEFAULT_FIELD;
+}

+ 134 - 51
src/views/project-quality/project-quality-manage/issues-feedback/index.vue

@@ -1,6 +1,10 @@
 <template>
   <div class="issues-feedback flex flex-col h-full">
-    <SearchForm :fields="fields" :params="params"></SearchForm>
+    <SearchForm :fields="fields" :params="params">
+      <template #service="{ item, params }">
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
+      </template>
+    </SearchForm>
     <div class="flex-1 page-wrap">
       <div class="btn-group">
         <t-button
@@ -27,86 +31,95 @@
         select-on-row-click
         @select-change="selectChange"
       >
+        <template #type="{ col, row }">
+          {{ customerTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #flow-status="{ col, row }">
+          {{ flowStatusFilter(row[col.colKey]) }}
+        </template>
+        <template #issues-type="{ col, row }">
+          {{ issuesTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #issues-reason="{ col, row }">
+          {{ issuesReasonTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #submit-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #update-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
       </t-table>
     </div>
   </div>
 </template>
 
 <script setup lang="jsx" name="IssuesFeedback">
-import { reactive, ref } from 'vue';
+import { computed, reactive, ref } from 'vue';
+import { omit } from 'lodash';
 import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
+
 import {
   issuesFeedbackListApi,
   issuesFeedbackDestroyApi,
 } from '@/api/project-quality';
 import useFetchTable from '@/hooks/useFetchTable';
+import { dictToOptionList } from '@/utils/tool';
+import {
+  timestampFilter,
+  customerTypeFilter,
+  issuesReasonTypeFilter,
+  issuesTypeFilter,
+  flowStatusFilter,
+} from '@/utils/filter';
+import {
+  ISSUES_INFLUENCE_DEGREE,
+  ISSUES_REASON_TYPE,
+  ISSUES_TYPE,
+} from '@/config/constants';
 
 const selectedRowKeys = ref([]);
 const selectChange = (value) => {
   selectedRowKeys.value = value;
 };
-const columns = [
-  {
-    colKey: 'row-select',
-    type: 'multiple',
-    width: 50,
-    fixed: 'left',
-  },
-  { colKey: 'a', title: '质量问题编号' },
-  { colKey: 'b', title: '项目单号' },
-  { colKey: 'c', title: '客户类型' },
-  { colKey: 'd', title: '客户名称' },
-  { colKey: 'e', title: '实施产品' },
-  { colKey: 'f', title: '问题简要' },
-  { colKey: 'g', title: '责任人' },
-  { colKey: 'h', title: '问题类型' },
-  { colKey: 'i', title: '问题归因' },
-  { colKey: 'j', title: '影响度' },
-  { colKey: 'k', title: '提交人' },
-  { colKey: 'l', title: '提交时间' },
-  { colKey: 'm', title: '更新时间' },
-  { colKey: 'n', title: '流程状态' },
-  { colKey: 'o', title: '当前节点' },
-  { colKey: 'p', title: '当前负责人' },
-];
-
-const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
-  issuesFeedbackListApi,
-  {
-    fetchDataHandle: () => {
-      selectedRowKeys.value = [];
-    },
-  }
-);
 
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'service',
   },
   {
-    prop: 'b',
+    prop: 'userId',
     label: '责任人',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
   },
   {
-    prop: 'c',
+    prop: 'type',
     label: '问题类型',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: dictToOptionList(ISSUES_TYPE),
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'd',
+    prop: 'reason',
     label: '问题归因',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: dictToOptionList(ISSUES_REASON_TYPE),
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',
@@ -122,43 +135,113 @@ const fields = ref([
     ],
   },
   {
-    prop: 'e',
+    prop: 'degree',
     label: '影响度',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: ISSUES_INFLUENCE_DEGREE.map((item) => {
+      return {
+        label: item,
+        value: item,
+      };
+    }),
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'f',
+    prop: 'custom',
     label: '客户名称',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'g',
+    prop: 'problemNo',
     label: '质量问题编号',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'h',
+    prop: 'submitTime',
     label: '提交时间',
     type: 'daterange',
     labelWidth: 100,
     colSpan: 9,
+    attrs: {
+      clearable: true,
+      valueType: 'time-stamp',
+    },
   },
 ]);
 const params = reactive({
-  a: '',
-  b: '',
-  c: '',
-  d: '',
-  e: '',
-  f: '',
-  g: '',
-  h: [],
+  serviceId: '',
+  userId: '',
+  type: '',
+  reason: '',
+  degree: '',
+  custom: '',
+  problemNo: '',
+  submitTime: [],
+});
+const computedParams = computed(() => {
+  let data = omit(params, ['submitTime']);
+  data.startTime = params.submitTime[0];
+  data.endTime = params.submitTime[1];
+  return data;
 });
 
+const columns = [
+  {
+    colKey: 'row-select',
+    type: 'multiple',
+    width: 50,
+    fixed: 'left',
+  },
+  { colKey: 'problemNo', title: '质量问题编号', minWidth: 120 },
+  { colKey: 'crmNo', title: '项目单号', minWidth: 120 },
+  { colKey: 'customType', title: '客户类型', cell: 'type', width: 100 },
+  { colKey: 'custom', title: '客户名称' },
+  { colKey: 'sopType', title: '实施产品' },
+  { colKey: 'summary', title: '问题简要' },
+  { colKey: 'userNames', title: '责任人' },
+  { colKey: 'type', title: '问题类型', cell: 'issues-type', width: 120 },
+  { colKey: 'reason', title: '问题归因', cell: 'issues-reason', width: 120 },
+  { colKey: 'influenceDegree', title: '影响度', width: 70 },
+  { colKey: 'submitter', title: '提交人' },
+  {
+    colKey: 'submissionTime',
+    title: '提交时间',
+    cell: 'submit-time',
+    width: 170,
+  },
+  {
+    colKey: 'updateDateTime',
+    title: '更新时间',
+    cell: 'update-time',
+    width: 170,
+  },
+  { colKey: 'status', title: '流程状态', cell: 'flow-status', width: 100 },
+  { colKey: 'setup', title: '当前节点' },
+  { colKey: 'pendApproveUsers', title: '当前负责人' },
+];
+
+const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
+  issuesFeedbackListApi,
+  {
+    fetchDataHandle: () => {
+      selectedRowKeys.value = [];
+    },
+    params: computedParams,
+  }
+);
+
 const handleDestroy = () => {
   if (!selectedRowKeys.value.length) {
     MessagePlugin.error('请选择要作废的记录');

+ 105 - 37
src/views/project-quality/project-quality-manage/issues-query/index.vue

@@ -1,6 +1,10 @@
 <template>
   <div class="issues-feedback flex flex-col h-full">
-    <SearchForm :fields="fields" :params="params"></SearchForm>
+    <SearchForm :fields="fields" :params="params">
+      <template #service="{ item, params }">
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
+      </template>
+    </SearchForm>
     <div class="flex-1 page-wrap">
       <t-table
         size="small"
@@ -16,59 +20,79 @@
           current: pagination.pageNumber,
         }"
       >
+        <template #type="{ col, row }">
+          {{ customerTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #issues-type="{ col, row }">
+          {{ issuesTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #issues-reason="{ col, row }">
+          {{ issuesReasonTypeFilter(row[col.colKey]) }}
+        </template>
+        <template #create-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
       </t-table>
     </div>
   </div>
 </template>
 
 <script setup lang="jsx" name="IssuesQuery">
-import { reactive, ref } from 'vue';
-import { issuesQueryListApi } from '@/api/project-quality';
-import useFetchTable from '@/hooks/useFetchTable';
+import { computed, reactive, ref } from 'vue';
+import { omit } from 'lodash';
 
-const columns = [
-  { colKey: 'a', title: '质量问题编号' },
-  { colKey: 'b', title: '项目单号' },
-  { colKey: 'c', title: '客户类型' },
-  { colKey: 'd', title: '客户名称' },
-  { colKey: 'e', title: '实施产品' },
-  { colKey: 'f', title: '问题简要' },
-  { colKey: 'g', title: '责任人' },
-  { colKey: 'h', title: '问题类型' },
-  { colKey: 'i', title: '问题归因' },
-  { colKey: 'j', title: '影响度' },
-  { colKey: 'k', title: '提交人' },
-  { colKey: 'l', title: '提交时间' },
-];
+import { issuesFeedbackListApi } from '@/api/project-quality';
+import useFetchTable from '@/hooks/useFetchTable';
+import { dictToOptionList } from '@/utils/tool';
+import {
+  timestampFilter,
+  customerTypeFilter,
+  issuesReasonTypeFilter,
+  issuesTypeFilter,
+} from '@/utils/filter';
+import {
+  ISSUES_INFLUENCE_DEGREE,
+  ISSUES_REASON_TYPE,
+  ISSUES_TYPE,
+} from '@/config/constants';
 
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'service',
   },
   {
-    prop: 'b',
+    prop: 'userId',
     label: '责任人',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
   },
   {
-    prop: 'c',
+    prop: 'type',
     label: '问题类型',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: dictToOptionList(ISSUES_TYPE),
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'd',
+    prop: 'reason',
     label: '问题归因',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: dictToOptionList(ISSUES_REASON_TYPE),
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',
@@ -84,43 +108,87 @@ const fields = ref([
     ],
   },
   {
-    prop: 'e',
+    prop: 'degree',
     label: '影响度',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: ISSUES_INFLUENCE_DEGREE.map((item) => {
+      return {
+        label: item,
+        value: item,
+      };
+    }),
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'f',
+    prop: 'custom',
     label: '客户名称',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'g',
+    prop: 'problemNo',
     label: '质量问题编号',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'h',
+    prop: 'submitTime',
     label: '提交时间',
     type: 'daterange',
     labelWidth: 100,
-    colSpan: 9,
+    colSpan: 10,
+    attrs: {
+      clearable: true,
+      valueType: 'time-stamp',
+    },
   },
 ]);
 const params = reactive({
-  a: '',
-  b: '',
-  c: '',
-  d: '',
-  e: '',
-  f: '',
-  g: '',
-  h: [],
+  serviceId: '',
+  userId: '',
+  type: '',
+  reason: '',
+  degree: '',
+  custom: '',
+  problemNo: '',
+  submitTime: [],
+});
+const computedParams = computed(() => {
+  let data = omit(params, ['submitTime']);
+  data.startTime = params.submitTime[0];
+  data.endTime = params.submitTime[1];
+  return data;
 });
 
-const { pagination, tableData, search, onChange } =
-  useFetchTable(issuesQueryListApi);
+const columns = [
+  { colKey: 'problemNo', title: '质量问题编号' },
+  { colKey: 'crmNo', title: '项目单号' },
+  { colKey: 'customType', title: '客户类型', cell: 'type', width: 100 },
+  { colKey: 'custom', title: '客户名称' },
+  { colKey: 'sopType', title: '实施产品' },
+  { colKey: 'summary', title: '问题简要' },
+  { colKey: 'userName', title: '责任人' },
+  { colKey: 'type', title: '问题类型', cell: 'issues-type', width: 120 },
+  { colKey: 'reason', title: '问题归因', cell: 'issues-reason', width: 120 },
+  { colKey: 'influenceDegree', title: '影响度', width: 70 },
+  { colKey: 'createName', title: '提交人' },
+  { colKey: 'createTime', title: '提交时间', cell: 'create-time', width: 170 },
+];
+
+const { pagination, tableData, search, onChange } = useFetchTable(
+  issuesFeedbackListApi,
+  {
+    params: computedParams,
+  }
+);
 </script>

+ 14 - 20
src/views/service-unit/dispatch/dispatch-manage/add-dispatch-dialog.vue

@@ -7,27 +7,23 @@
     :closeOnOverlayClick="false"
   >
     <t-form ref="formRef" :data="formData" :rules="rules" labelWidth="120px">
-      <t-form-item label="项目单号" name="crmNo">
+      <t-form-item label="项目单号" name="crmNo">
         <t-input v-model="formData.crmNo" clearable></t-input>
       </t-form-item>
-      <t-form-item label="项目名称" name="name">
+      <t-form-item label="项目名称" name="name">
         <t-input v-model="formData.name" clearable></t-input>
       </t-form-item>
-      <t-form-item label="派单时间" name="beginTime">
+      <t-form-item label="派单时间" name="beginTime">
         <t-date-picker v-model="formData.beginTime" value-type="time-stamp" />
       </t-form-item>
-      <t-form-item label="服务单元:">
-        <select-service-unit
-          v-model="formData.serviceId"
-          clearable
-        ></select-service-unit>
+      <t-form-item label="服务单元">
+        <select-service-unit v-model="formData.serviceId"></select-service-unit>
       </t-form-item>
-      <t-form-item label="客户经理" name="crmUserId">
-        <select-user
+      <t-form-item label="客户经理" name="crmUserId">
+        <select-type-user
           v-model="formData.crmUserId"
-          type="CUSTOMER_MANAGER"
-          clearable
-        ></select-user>
+          type="ACCOUNT_MANAGER"
+        ></select-type-user>
       </t-form-item>
       <t-form-item label="客户类型" name="customType">
         <t-select v-model="formData.customType">
@@ -39,31 +35,29 @@
           />
         </t-select>
       </t-form-item>
-      <t-form-item label="客户名称" name="customId">
+      <t-form-item label="客户名称" name="customId">
         <select-customer
           v-model="formData.customId"
-          clearable
           :type="formData.customType"
           type-required
         ></select-customer>
       </t-form-item>
-      <t-form-item label="考试开始时间">
+      <t-form-item label="考试开始时间">
         <t-date-picker
           v-model="formData.examStartTime"
           :disable-date="{ after: formData.examEndTime || undefined }"
           value-type="time-stamp"
         />
       </t-form-item>
-      <t-form-item label="考试结束时间">
+      <t-form-item label="考试结束时间">
         <t-date-picker
           v-model="formData.examEndTime"
           :disable-date="{ before: formData.examStartTime || undefined }"
           value-type="time-stamp"
         />
       </t-form-item>
-      <t-form-item label="实施产品:" name="productId">
-        <!-- todo: 使用选择产品列表组件-->
-        <t-select v-model="formData.productId" clearable></t-select>
+      <t-form-item label="实施产品" name="productId">
+        <select-product v-model="formData.productId" clearable></select-product>
       </t-form-item>
     </t-form>
     <template #foot>

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

@@ -2,23 +2,19 @@
   <div class="dispatch-manage flex flex-col h-full">
     <SearchForm :fields="fields" :params="params">
       <template #service="{ item, params }">
-        <select-service-unit
-          v-model="params[item.prop]"
-          clearable
-        ></select-service-unit>
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
       </template>
       <template #lead="{ item, params }">
-        <select-user
+        <select-type-user
           v-model="params[item.prop]"
-          clearable
           type="REGION_MANAGER"
-        ></select-user>
+        ></select-type-user>
       </template>
       <template #user="{ item, params }">
-        <select-user
+        <select-type-user
           v-model="params[item.prop]"
-          type="CUSTOMER_MANAGER"
-        ></select-user>
+          type="ACCOUNT_MANAGER"
+        ></select-type-user>
       </template>
     </SearchForm>
     <div class="flex-1 page-wrap">

+ 9 - 4
src/views/service-unit/service-unit-manage/add-range/index.vue

@@ -1,6 +1,13 @@
 <template>
   <div class="add-range flex flex-col h-full">
-    <SearchForm :fields="fields" :params="params"></SearchForm>
+    <SearchForm :fields="fields" :params="params">
+      <template #user="{ item, params }">
+        <select-type-user
+          v-model="params[item.prop]"
+          type="ACCOUNT_MANAGER"
+        ></select-type-user>
+      </template>
+    </SearchForm>
 
     <div class="flex-1 page-wrap">
       <div class="btn-group">
@@ -74,9 +81,7 @@ const fields = ref([
     type: 'select',
     labelWidth: 80,
     colSpan: 5,
-    attrs: {
-      clearable: true,
-    },
+    cell: 'user',
   },
   {
     prop: 'productType',

+ 1 - 4
src/views/service-unit/service-unit-manage/range-manage/index.vue

@@ -2,10 +2,7 @@
   <div class="unit-manage flex flex-col h-full">
     <SearchForm :fields="fields" :params="params">
       <template #service-unit="{ item, params }">
-        <select-service-unit
-          v-model="params[item.prop]"
-          clearable
-        ></select-service-unit>
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
       </template>
     </SearchForm>
     <div class="flex-1 page-wrap">

+ 2 - 2
src/views/service-unit/service-unit-manage/regional-planning/add-region-dialog.vue

@@ -14,8 +14,8 @@
         </select-service-unit>
       </t-form-item>
       <t-form-item label="大区经理:" name="leadId">
-        <select-user v-model="formData.leadId" type="REGION_MANAGER">
-        </select-user>
+        <select-type-user v-model="formData.leadId" type="REGION_MANAGER">
+        </select-type-user>
       </t-form-item>
       <t-form-item label-width="0px" name="targetValue">
         <div style="height: 400px; width: 100%">

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

@@ -2,24 +2,16 @@
   <div class="unit-manage flex flex-col h-full">
     <SearchForm :fields="fields" :params="params">
       <template #service="{ item, params }">
-        <select-service-unit
-          v-model="params[item.prop]"
-          clearable
-        ></select-service-unit>
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
       </template>
       <template #lead="{ item, params }">
-        <select-user
+        <select-type-user
           v-model="params[item.prop]"
           type="REGION_MANAGER"
-          clearable
-        ></select-user>
+        ></select-type-user>
       </template>
       <template #city="{ item, params }">
-        <select-area
-          v-model="params[item.prop]"
-          clearable
-          :level="2"
-        ></select-area>
+        <select-area v-model="params[item.prop]" :level="2"></select-area>
       </template>
     </SearchForm>
     <div class="flex-1 page-wrap">

+ 2 - 2
src/views/service-unit/service-unit-manage/unit-manage/add-unit-dialog.vue

@@ -55,10 +55,10 @@
         <span style="padding: 0 10px"> (区域协调人数量/项目数量)</span>
       </t-form-item>
       <t-form-item label="负责人:" name="serviceLeadId">
-        <select-user
+        <select-type-user
           v-model="formData.serviceLeadId"
           type="BUSSINESS"
-        ></select-user>
+        ></select-type-user>
       </t-form-item>
     </t-form>
     <template #foot>

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

@@ -2,11 +2,10 @@
   <div class="unit-manage flex flex-col h-full">
     <SearchForm :fields="fields" :params="params">
       <template #leader="{ item, params }">
-        <select-user
+        <select-type-user
           v-model="params[item.prop]"
           type="BUSSINESS"
-          clearable
-        ></select-user>
+        ></select-type-user>
       </template>
     </SearchForm>
     <div class="flex-1 page-wrap">

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

@@ -96,7 +96,7 @@ const { formData, isEdit } = useClearDialog(
   {
     id: null,
     name: '',
-    serviceId: '1',
+    serviceId: '',
     dingRoleIds: [],
     supplierId: '',
     signInTime: ['06:00', '10:00'],

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

@@ -4,7 +4,6 @@
       <template #service="{ item, params }">
         <select-service-unit
           v-model="params[item.prop]"
-          clearable
         ></select-service-unit>
       </template>
     </SearchForm>

+ 3 - 5
src/views/system/config-manage/customer-manage/edit-customer-dialog.vue

@@ -33,19 +33,17 @@
         </t-col>
         <t-col :span="6">
           <t-form-item label="客户经理" name="managerId">
-            <select-user
+            <select-type-user
               v-model="formData.managerId"
-              type="CUSTOMER_MANAGER"
-              clearable
+              type="ACCOUNT_MANAGER"
             >
-            </select-user>
+            </select-type-user>
           </t-form-item>
         </t-col>
         <t-col :span="6">
           <t-form-item label="服务档位">
             <select-service-level
               v-model="formData.levelId"
-              clearable
               :type="formData.type"
               type-required
             >

+ 3 - 7
src/views/system/config-manage/customer-manage/index.vue

@@ -2,15 +2,11 @@
   <div class="registration-query flex flex-col h-full">
     <SearchForm :fields="fields" :params="params">
       <template #manager="{ item, params }">
-        <select-user
-          v-model="params[item.prop]"
-          clearable
-          type="CUSTOMER_MANAGER"
-        >
-        </select-user>
+        <select-type-user v-model="params[item.prop]" type="ACCOUNT_MANAGER">
+        </select-type-user>
       </template>
       <template #level="{ item, params }">
-        <select-service-level v-model="params[item.prop]" clearable>
+        <select-service-level v-model="params[item.prop]">
         </select-service-level>
       </template>
     </SearchForm>

+ 1 - 0
src/views/system/config-manage/service-level-manage/edit-service-level-dialog.vue

@@ -28,6 +28,7 @@
         </t-col>
         <t-col :span="12">
           <t-form-item label="项目角色配置" name="roleList">
+            <!-- TODO: -->
             <t-select v-model="formData.roleList">
               <t-option
                 v-for="(val, key) in CUSTOMER_TYPE"

+ 3 - 2
src/views/system/notice-log/notice-manage/edit-notice-dialog.vue

@@ -23,7 +23,8 @@
         </t-col>
         <t-col :span="6">
           <t-form-item label="服务单元" name="serviceId">
-            <t-select v-model="formData.serviceId"> </t-select>
+            <select-service-unit v-model="formData.serviceId">
+            </select-service-unit>
           </t-form-item>
         </t-col>
         <t-col :span="12">
@@ -74,7 +75,7 @@ const { formData, isEdit } = useClearDialog(
   {
     id: null,
     type: '',
-    serviceId: '1',
+    serviceId: '',
     title: '',
     content: '',
     status: 'UN_PUBLISH',

+ 1 - 3
src/views/system/notice-log/notice-manage/notice-message-dialog.vue

@@ -12,8 +12,7 @@
   >
     <SearchForm :fields="fields" :params="params">
       <template #supplier="{ item, params }">
-        <select-supplier v-model="params[item.prop]" clearable>
-        </select-supplier>
+        <select-supplier v-model="params[item.prop]"> </select-supplier>
       </template>
     </SearchForm>
 
@@ -96,7 +95,6 @@ const fields = ref([
     labelWidth: 70,
     colSpan: 5,
     cell: 'supplier',
-    options: [],
   },
   {
     prop: 'status',

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

@@ -1,14 +1,31 @@
 <template>
   <div class="work-statistics flex flex-col h-full">
-    <SearchForm :fields="fields" :params="params"></SearchForm>
+    <SearchForm :fields="fields" :params="params">
+      <template #service="{ item, params }">
+        <select-service-unit v-model="params[item.prop]"></select-service-unit>
+      </template>
+      <template #supplier="{ item, params }">
+        <select-supplier v-model="params[item.prop]"> </select-supplier>
+      </template>
+      <template #creator="{ item, params }">
+        <select-filter-user v-model="params[item.prop]"> </select-filter-user>
+      </template>
+    </SearchForm>
     <div class="flex-1 page-wrap">
-      <t-space>
-        <span>考勤总计:{{ statisticsInfo.a }}</span>
-        <span>已提交:{{ statisticsInfo.b }}</span>
-        <span>待提交:{{ statisticsInfo.c }}</span>
-        <span>已提交累计人天:{{ statisticsInfo.d }}天</span>
-        <span>已提交累计工时:{{ statisticsInfo.e }}小时</span>
-      </t-space>
+      <div class="flex justify-between items-center">
+        <t-space>
+          <span>考勤总计:{{ statisticsInfo.total }}</span>
+          <span>已提交:{{ statisticsInfo.submitted }}</span>
+          <span>待提交:{{ statisticsInfo.unSubmitted }}</span>
+          <span>已提交累计人天:{{ statisticsInfo.allDays }}天</span>
+          <span>已提交累计工时:{{ statisticsInfo.allHours }}小时</span>
+        </t-space>
+        <div class="btn-group">
+          <t-button theme="success" @click="handleExport"
+            >导出统计结果</t-button
+          >
+        </div>
+      </div>
       <t-table
         size="small"
         row-key="id"
@@ -23,101 +40,88 @@
           current: pagination.pageNumber,
         }"
       >
+        <template #user="{ row }">
+          {{ row.userName }}({{ row.userNo }})
+        </template>
+        <template #start-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #end-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #submit-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #status="{ col, row }">
+          {{ flowStatusFilter(row[col.colKey]) }}
+        </template>
+        <template #operate="{ row }">
+          <div class="table-operations">
+            <t-link
+              v-if="row.status === 'AUDITING'"
+              theme="primary"
+              hover="color"
+              @click="handlePass(row)"
+            >
+              同意撤回
+            </t-link>
+          </div>
+        </template>
       </t-table>
     </div>
   </div>
 </template>
 
 <script setup lang="jsx" name="WorkStatistics">
-import { ref, reactive } from 'vue';
+import { ref, reactive, onMounted } from 'vue';
 import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import {
   workStatisticsListApi,
   workStatisticsInfoApi,
+  workStatisticsExportApi,
   workStatisticsPassApi,
 } from '@/api/work-hours';
-
-const columns = [
-  { colKey: 'a', title: '服务单元' },
-  { colKey: 'b', title: 'SOP流水号' },
-  { colKey: 'c', title: '客户名称' },
-  { colKey: 'd', title: '省份' },
-  { colKey: 'e', title: '城市' },
-  { colKey: 'f', title: '项目开始时间', width: 170 },
-  { colKey: 'g', title: '项目结束时间', width: 170 },
-  { colKey: 'h', title: '姓名(人员档案号)', width: 150 },
-  { colKey: 'i', title: '项目角色' },
-  { colKey: 'j', title: '供应商' },
-  { colKey: 'k', title: '实际出勤(天)', width: 120 },
-  { colKey: 'l', title: '工作日(天)', width: 110 },
-  { colKey: 'm', title: '周末(天)', width: 100 },
-  { colKey: 'n', title: '法定节假日(天)', width: 140 },
-  { colKey: 'o', title: '累计工时(天)', width: 120 },
-  { colKey: 'p', title: '违规工时(天)', width: 120 },
-  { colKey: 'q', title: '提交人' },
-  { colKey: 'r', title: '提交时间', width: 170 },
-  { colKey: 's', title: '提交状态', width: 100 },
-  {
-    title: '管理',
-    colKey: 'operate',
-    fixed: 'right',
-    width: 120,
-    cell: (h, { row }) => {
-      return (
-        <div class="table-operations">
-          <t-link
-            theme="primary"
-            hover="color"
-            onClick={(e) => {
-              e.stopPropagation();
-              handlePass(row);
-            }}
-          >
-            同意撤回
-          </t-link>
-        </div>
-      );
-    },
-  },
-];
-const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
-  workStatisticsListApi
-);
-
-let statisticsInfo = reactive({ a: 1, b: 2, c: 3, d: 4, e: 5 });
-const getStatisticsInfo = async () => {
-  const res = await workStatisticsInfoApi(params);
-  statisticsInfo = res.data || {};
-};
+import { timestampFilter, flowStatusFilter } from '@/utils/filter';
+import { FLOW_STATUS } from '@/config/constants';
+import { dictToOptionList } from '@/utils/tool';
 
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'service',
   },
   {
-    prop: 'b',
+    prop: 'status',
     label: '提交状态',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: dictToOptionList(FLOW_STATUS),
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'c',
+    prop: 'createId',
     label: '提交人',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'creator',
   },
   {
-    prop: 'd',
+    prop: 'userName',
     label: '姓名',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',
@@ -134,42 +138,125 @@ const fields = ref([
     ],
   },
   {
-    prop: 'e',
+    prop: 'supplierId',
     label: '所属供应商',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'supplier',
   },
   {
-    prop: 'f',
+    prop: 'custom',
     label: '客户名称',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'g',
+    prop: 'sopNo',
     label: 'SOP流水号',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'h',
+    prop: 'days',
+    type: 'number',
     label: '违规工时>',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      theme: 'column',
+      decimalPlaces: 0,
+      max: 1000000,
+      min: 0,
+      style: 'width: 120px',
+    },
   },
 ]);
 const params = reactive({
-  a: '',
-  b: '',
-  c: '',
-  d: '',
-  e: '',
-  f: '',
-  g: '',
-  h: '',
+  serviceId: '',
+  status: '',
+  createId: '',
+  userName: '',
+  supplierId: '',
+  custom: '',
+  sopNo: '',
+  days: '',
 });
 
+const columns = [
+  { colKey: 'service', title: '服务单元' },
+  { colKey: 'sopNo', title: 'SOP流水号' },
+  { colKey: 'custom', title: '客户名称' },
+  { colKey: 'province', title: '省份' },
+  { colKey: 'city', title: '城市' },
+  {
+    colKey: 'examStartTime',
+    title: '项目开始时间',
+    width: 170,
+    cell: 'start-time',
+  },
+  {
+    colKey: 'examEndTime',
+    title: '项目结束时间',
+    width: 170,
+    cell: 'end-time',
+  },
+  { colKey: 'userName', title: '姓名(人员档案号)', cell: 'user', width: 150 },
+  { colKey: 'roleName', title: '项目角色' },
+  { colKey: 'supplier', title: '供应商' },
+  { colKey: 'attendance', title: '实际出勤(天)', width: 120 },
+  { colKey: 'weekdays', title: '工作日(天)', width: 110 },
+  { colKey: 'weekends', title: '周末(天)', width: 100 },
+  { colKey: 'holidays', title: '法定节假日(天)', width: 140 },
+  { colKey: 'workHours', title: '累计工时(天)', width: 120 },
+  { colKey: 'violationDays', title: '违规工时(天)', width: 120 },
+  { colKey: 'submitter', title: '提交人' },
+  {
+    colKey: 'submissionTime',
+    title: '提交时间',
+    cell: 'submit-time',
+    width: 170,
+  },
+  { colKey: 'status', title: '提交状态', cell: 'status', width: 100 },
+  {
+    title: '管理',
+    colKey: 'operate',
+    cell: 'operate',
+    fixed: 'right',
+    width: 100,
+  },
+];
+const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
+  workStatisticsListApi
+);
+
+let statisticsInfo = ref({});
+const getStatisticsInfo = async () => {
+  const res = await workStatisticsInfoApi(params);
+  statisticsInfo.value = res || {};
+};
+
+const handleExport = () => {
+  const confirmDia = DialogPlugin({
+    header: '操作提示',
+    body: `确定要导出查询到的所有结果吗?`,
+    confirmBtn: '确定',
+    cancelBtn: '取消',
+    onConfirm: async () => {
+      confirmDia.hide();
+      const res = await workStatisticsExportApi(params).catch(() => {});
+      if (!res) return;
+      MessagePlugin.success('开始下载');
+    },
+  });
+};
+
 const handlePass = (row) => {
   const confirmDia = DialogPlugin({
     header: '同意撤回提示',
@@ -185,4 +272,8 @@ const handlePass = (row) => {
     },
   });
 };
+
+onMounted(() => {
+  getStatisticsInfo();
+});
 </script>