Browse Source

工时管理页面完善

zhangjie 1 year ago
parent
commit
5252fdd91e

+ 33 - 11
src/api/work-hours.js

@@ -1,26 +1,28 @@
-import { request } from '@/utils/request.js';
+import { request, paramsSerializer } from '@/utils/request.js';
 
 // abnormal-check
 export const workHoursWaitCheckListApi = (data) =>
   request({
     url: '/api/system/work-hourse/wait-check/list',
-    data,
+    params: data,
   });
 export const workHoursDoneCheckListApi = (data) =>
   request({
     url: '/api/system/work-hourse/done-check/list',
-    data,
+    params: data,
   });
 export const workHoursWaitCheckAuditApi = (ids, pass) => {
   if (pass) {
     return request({
       url: '/api/system/work-hourse/wait-check/pass',
-      data: { ids },
+      params: { ids },
+      paramsSerializer,
     });
   } else {
     return request({
       url: '/api/system/work-hourse/wait-check/refuse',
-      data: { ids },
+      params: { ids },
+      paramsSerializer,
     });
   }
 };
@@ -29,33 +31,53 @@ export const workHoursWaitCheckAuditApi = (ids, pass) => {
 export const workAttendanceListApi = (data) =>
   request({
     url: '/api/system/work-attendance/list',
-    data,
+    params: data,
   });
 export const workAttendanceInfoApi = (data) =>
   request({
     url: '/api/system/work-attendance/info',
-    data,
+    params: data,
   });
 export const workAttendanceSubmitApi = (ids) =>
   request({
     url: '/api/system/work-attendance/submit',
-    data: { ids },
+    params: { ids },
+    paramsSerializer,
   });
 export const workAttendanceExportApi = (ids) =>
   request({
     url: '/api/system/work-attendance/export',
-    data: { ids },
+    params: { ids },
     download: true,
+    paramsSerializer,
   });
 export const workAttendanceWithdrawApi = (id) =>
   request({
     url: '/api/system/work-attendance/withdraw',
-    data: { id },
+    params: { id },
   });
 export const workAttendanceCancelWithdrawApi = (id) =>
   request({
     url: '/api/system/work-attendance/withdraw-cancel',
-    data: { id },
+    params: { id },
+  });
+
+// work-attendance-detail
+export const workAttendanceDetailListApi = (data) =>
+  request({
+    url: '/api/system/work-attendance-detail/list',
+    params: data,
+  });
+export const workAttendanceDetailExportApi = (ids) =>
+  request({
+    url: '/api/system/work-attendance-detail/export',
+    params: { ids },
+    download: true,
+  });
+export const workStatisticsDetailInfoApi = (data) =>
+  request({
+    url: '/api/system/work-attendance-detail/info',
+    params: data,
   });
 
 // work-statistics

+ 1 - 2
src/components/global/search-form/index.vue

@@ -224,7 +224,6 @@ const firstLineItems = computed(() => {
   for (let i = 0; i < props.fields.length; i++) {
     let column = props.fields[i];
     if (init + (column.colSpan || 0) > 24) {
-      cutIndex.value = i;
       break;
     } else {
       arr.push(cloneDeep(column));
@@ -232,7 +231,7 @@ const firstLineItems = computed(() => {
     }
   }
 
-  if (cutIndex.value === 0 && props.fields.length) cutIndex.value = arr.length;
+  cutIndex.value = arr.length;
 
   return arr;
 });

+ 16 - 1
src/config/constants.js

@@ -11,7 +11,11 @@ export const ROLE_TYPE = {
   CUSTOM: '技术客服',
   DEFINED: '自定义',
 };
-
+// 审核结果
+export const AUDITING_RESULT = {
+  NOT_PASS: '未通过',
+  PASS: '通过',
+};
 // 系统管理 ------->
 // 客户类型
 export const CUSTOMER_TYPE = {
@@ -66,6 +70,17 @@ export const ISSUES_REASON_TYPE = {
 };
 export const ISSUES_INFLUENCE_DEGREE = ['A', 'B', 'C', 'D'];
 
+// 工时管理
+// 考勤类型
+export const ATTENDANCE_TYPE = {
+  INTER: '签到',
+  OUTER: '签退',
+};
+export const ATTENDANCE_RESULT = {
+  NORMAL: '正常',
+  EXCEPTION: '异常',
+};
+
 // SOP
 export const FLOW_STATUS = {
   START: '已开始',

+ 13 - 0
src/router/modules/workHours.js

@@ -46,6 +46,19 @@ export default {
             alias: 'dingSubmit',
           },
         },
+        {
+          name: 'WorkAttendanceDetail',
+          path: '/work-hours/work-hours-manage/work-attendance-detail',
+          component: () =>
+            import(
+              '@/views/work-hours/work-hours-manage/work-attendance-detail/index.vue'
+            ),
+          meta: {
+            title: '考勤明细查询',
+            sort: 2,
+            alias: 'dingDetail',
+          },
+        },
         {
           name: 'WorkStatistics',
           path: '/work-hours/work-hours-manage/work-statistics',

+ 13 - 0
src/utils/filter.js

@@ -8,6 +8,9 @@ import {
   ISSUES_TYPE,
   ISSUES_REASON_TYPE,
   FLOW_STATUS,
+  AUDITING_RESULT,
+  ATTENDANCE_TYPE,
+  ATTENDANCE_RESULT,
 } from '@/config/constants';
 import { dateFormat } from './tool';
 
@@ -17,6 +20,9 @@ const DEFAULT_FIELD = '--';
 export function enableFilter(val) {
   return val ? '启用' : '禁用';
 }
+export function auditingResultFilter(val) {
+  return AUDITING_RESULT[val] || DEFAULT_FIELD;
+}
 export function timestampFilter(val) {
   return val ? dateFormat(val) : DEFAULT_FIELD;
 }
@@ -47,6 +53,13 @@ export function issuesTypeFilter(val) {
 export function issuesReasonTypeFilter(val) {
   return ISSUES_REASON_TYPE[val] || DEFAULT_FIELD;
 }
+// 工时管理
+export function attendanceTypeFilter(val) {
+  return ATTENDANCE_TYPE[val] || DEFAULT_FIELD;
+}
+export function attendanceResultFilter(val) {
+  return ATTENDANCE_RESULT[val] || DEFAULT_FIELD;
+}
 // sop
 export function flowStatusFilter(val) {
   return FLOW_STATUS[val] || DEFAULT_FIELD;

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

@@ -27,7 +27,7 @@
             @checked-change="handleCheckedChange"
           >
             <template #tree="slotProps">
-              <t-tree v-bind="slotProps" checkable hover transition />
+              <t-tree v-bind="slotProps" checkable hover />
             </template>
           </t-transfer>
         </div>

+ 93 - 41
src/views/work-hours/work-hours-manage/abnormal-check/done-check.vue

@@ -1,6 +1,13 @@
 <template>
   <div class="wait-check">
-    <SearchForm :fields="fields" :params="params" showAll></SearchForm>
+    <SearchForm :fields="fields" :params="params" showAll>
+      <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>
+    </SearchForm>
     <t-table
       size="small"
       row-key="id"
@@ -15,85 +22,99 @@
         current: pagination.pageNumber,
       }"
     >
+      <template #backup-time="{ col, row }">
+        {{ timestampFilter(row[col.colKey]) }}
+      </template>
+      <template #apply-time="{ col, row }">
+        {{ timestampFilter(row[col.colKey]) }}
+      </template>
+      <template #audit-time="{ col, row }">
+        {{ timestampFilter(row[col.colKey]) }}
+      </template>
+      <template #custom-type="{ col, row }">
+        {{ customerTypeFilter(row[col.colKey]) }}
+      </template>
+      <template #result="{ col, row }">
+        {{ auditingResultFilter(row[col.colKey]) }}
+      </template>
     </t-table>
   </div>
 </template>
 
 <script setup lang="jsx" name="DoneCheck">
-import { reactive, ref } from 'vue';
+import { reactive, ref, computed } from 'vue';
 import useFetchTable from '@/hooks/useFetchTable';
 import { workHoursDoneCheckListApi } 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: '客户类型' },
-  { 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, search, onChange } = useFetchTable(
-  workHoursDoneCheckListApi
-);
+import {
+  timestampFilter,
+  customerTypeFilter,
+  auditingResultFilter,
+} from '@/utils/filter';
+import { AUDITING_RESULT } 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: 'name',
     label: '姓名',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'c',
+    prop: 'supplierId',
     label: '所属供应商',
     type: 'select',
-    labelWidth: 100,
+    labelWidth: 120,
     colSpan: 5,
+    cell: 'supplier',
   },
   {
-    prop: 'd',
+    prop: 'result',
     label: '审核结果',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    options: dictToOptionList(AUDITING_RESULT),
   },
   {
-    prop: 'e',
+    prop: 'custom',
     label: '客户名称',
     labelWidth: 100,
-    colSpan: 4,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
-
   {
-    prop: 'f',
+    prop: 'exceptionTime',
     label: '异常时间',
     type: 'daterange',
     labelWidth: 100,
     colSpan: 10,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'g',
+    prop: 'applyTime',
     label: '申请时间',
     type: 'daterange',
     labelWidth: 100,
     colSpan: 10,
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',
@@ -110,12 +131,43 @@ const fields = ref([
   },
 ]);
 const params = reactive({
-  a: '',
-  b: '',
-  c: '',
-  d: '',
-  e: '',
-  f: [],
-  g: [],
+  serviceId: '',
+  name: '',
+  supplierId: '',
+  result: '',
+  custom: '',
+  exceptionTime: [],
+  applyTime: [],
+});
+const computedParams = computed(() => {
+  let data = omit(params, ['exceptionTime', 'applyTime']);
+  data.exceptionStartTime = params.exceptionTime[0];
+  data.exceptionEndTime = params.exceptionTime[1];
+  data.applyStartTime = params.applyTime[0];
+  data.applyEndTime = params.applyTime[1];
+  return data;
 });
+
+const columns = [
+  { colKey: 'service', title: '服务单元' },
+  { colKey: 'sopNo', title: 'SOP流水号' },
+  { colKey: 'name', title: '姓名' },
+  { colKey: 'supplier', title: '供应商' },
+  { colKey: 'custom', title: '客户名称' },
+  { colKey: 'customType', title: '客户类型', cell: 'custom-type', width: 100 },
+  { colKey: 'exceptionType', title: '异常类型' },
+  { colKey: 'exceptionDate', title: '异常日期' },
+  { colKey: 'backupTime', title: '补卡时间', cell: 'backup-time', width: 170 },
+  { colKey: 'reason', title: '理由' },
+  { colKey: 'attachment', title: '附件/截图' },
+  { colKey: 'applyTime', title: '申请时间', cell: 'apply-time', width: 170 },
+  { colKey: 'status', title: '审核状态', width: 100 },
+  { colKey: 'result', title: '审核结果', cell: 'result', width: 100 },
+  { colKey: 'auditor', title: '审核人' },
+  { colKey: 'auditTime', title: '审核时间', cell: 'audit-time', width: 170 },
+];
+const { pagination, tableData, search, onChange } = useFetchTable(
+  workHoursDoneCheckListApi,
+  { params: computedParams }
+);
 </script>

+ 112 - 78
src/views/work-hours/work-hours-manage/abnormal-check/wait-check.vue

@@ -1,6 +1,13 @@
 <template>
   <div class="wait-check">
-    <SearchForm :fields="fields" :params="params" showAll></SearchForm>
+    <SearchForm :fields="fields" :params="params" showAll>
+      <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>
+    </SearchForm>
     <div class="btn-group">
       <t-button
         theme="success"
@@ -32,112 +39,86 @@
       select-on-row-click
       @select-change="selectChange"
     >
+      <template #backup-time="{ col, row }">
+        {{ timestampFilter(row[col.colKey]) }}
+      </template>
+      <template #apply-time="{ col, row }">
+        {{ timestampFilter(row[col.colKey]) }}
+      </template>
+      <template #custom-type="{ col, row }">
+        {{ customerTypeFilter(row[col.colKey]) }}
+      </template>
+      <template #operate="{ row }">
+        <div class="table-operations" @click.stop>
+          <t-link
+            theme="primary"
+            hover="color"
+            @click="handleAudit([row.id], true)"
+          >
+            通过
+          </t-link>
+          <t-link
+            theme="danger"
+            hover="color"
+            @click="handleAudit([row.id], false)"
+          >
+            拒绝
+          </t-link>
+        </div>
+      </template>
     </t-table>
   </div>
 </template>
 
 <script setup lang="jsx" name="WaitCheck">
-import { reactive, ref } from 'vue';
+import { reactive, ref, computed } from 'vue';
 import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import {
   workHoursWaitCheckListApi,
   workHoursWaitCheckAuditApi,
 } from '@/api/work-hours';
+import { timestampFilter, customerTypeFilter } from '@/utils/filter';
+
 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: 'SOP流水号' },
-  { 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: '申请时间' },
-  {
-    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();
-              handleAudit([row.id], true);
-            }}
-          >
-            通过
-          </t-link>
-          <t-link
-            theme="danger"
-            hover="color"
-            onClick={(e) => {
-              e.stopPropagation();
-              handleAudit([row.id], false);
-            }}
-          >
-            拒绝
-          </t-link>
-        </div>
-      );
-    },
-  },
-];
-const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
-  workHoursWaitCheckListApi,
-  {
-    fetchDataHandle: () => {
-      selectedRowKeys.value = [];
-    },
-  }
-);
-
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
     labelWidth: 100,
     colSpan: 5,
+    cell: 'service',
   },
   {
-    prop: 'b',
+    prop: 'name',
     label: '姓名',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'c',
+    prop: 'supplierId',
     label: '所属供应商',
     type: 'select',
-    labelWidth: 100,
+    labelWidth: 120,
     colSpan: 5,
+    cell: 'supplier',
   },
   {
-    prop: 'd',
+    prop: 'custom',
     label: '客户名称',
     labelWidth: 100,
     colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',
@@ -153,29 +134,82 @@ const fields = ref([
     ],
   },
   {
-    prop: 'e',
+    prop: 'exceptionTime',
     label: '异常时间',
     type: 'daterange',
     labelWidth: 100,
     colSpan: 10,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'f',
+    prop: 'applyTime',
     label: '申请时间',
     type: 'daterange',
     labelWidth: 100,
     colSpan: 10,
+    attrs: {
+      clearable: true,
+    },
   },
 ]);
 const params = reactive({
-  a: '',
-  b: '',
-  c: '',
-  d: '',
-  e: [],
-  f: [],
+  serviceId: '',
+  name: '',
+  supplierId: '',
+  custom: '',
+  exceptionTime: [],
+  applyTime: [],
+});
+const computedParams = computed(() => {
+  let data = omit(params, ['exceptionTime', 'applyTime']);
+  data.exceptionStartTime = params.exceptionTime[0];
+  data.exceptionEndTime = params.exceptionTime[1];
+  data.applyStartTime = params.applyTime[0];
+  data.applyEndTime = params.applyTime[1];
+  return data;
 });
 
+const columns = [
+  {
+    colKey: 'row-select',
+    type: 'multiple',
+    width: 50,
+    fixed: 'left',
+  },
+  { colKey: 'service', title: '服务单元' },
+  { colKey: 'sopNo', title: 'SOP流水号' },
+  { colKey: 'name', title: '姓名' },
+  { colKey: 'supplier', title: '供应商' },
+  { colKey: 'custom', title: '客户名称' },
+  { colKey: 'customType', title: '客户类型', cell: 'custom-type', width: 100 },
+  { colKey: 'exceptionType', title: '异常类型' },
+  { colKey: 'exceptionDate', title: '异常日期' },
+  { colKey: 'backupTime', title: '补卡时间', cell: 'backup-time', width: 170 },
+  { colKey: 'reason', title: '理由' },
+  { colKey: 'attachment', title: '附件/截图' },
+  { colKey: 'status', title: '审核状态', width: 100 },
+  { colKey: 'approvorName', title: '当前审核人' },
+  { colKey: 'applyTime', title: '申请时间', cell: 'apply-time', width: 170 },
+  {
+    title: '操作',
+    colKey: 'operate',
+    cell: 'operate',
+    fixed: 'right',
+    width: 120,
+  },
+];
+const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
+  workHoursWaitCheckListApi,
+  {
+    fetchDataHandle: () => {
+      selectedRowKeys.value = [];
+    },
+    params: computedParams,
+  }
+);
+
 const handleAudit = async (selectedIds, pass) => {
   if (!selectedIds.length) {
     MessagePlugin.error('请选择要通过的记录');

+ 240 - 0
src/views/work-hours/work-hours-manage/work-attendance-detail/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div class="work-attendance flex flex-col h-full">
+    <SearchForm :fields="fields" :params="params" showAll>
+      <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>
+    </SearchForm>
+
+    <div class="flex-1 page-wrap">
+      <div class="flex justify-between items-center">
+        <t-space>
+          <span>考勤总计:{{ statisticsInfo.total }}</span>
+          <span>异常考勤:{{ statisticsInfo.exception }}</span>
+          <span>累计人天:{{ statisticsInfo.allDays }}天</span>
+          <span>累计工时:{{ statisticsInfo.allHours }}小时</span>
+        </t-space>
+        <div class="btn-group">
+          <t-button theme="success" @click="multExport">批量导出</t-button>
+        </div>
+      </div>
+
+      <t-table
+        size="small"
+        row-key="id"
+        :columns="columns"
+        :data="tableData"
+        bordered
+        :pagination="{
+          defaultCurrent: 1,
+          defaultPageSize: 10,
+          onChange,
+          total: pagination.total,
+          current: pagination.pageNumber,
+        }"
+      >
+        <template #create-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #result="{ col, row }">
+          {{ attendanceResultFilter(row[col.colKey]) }}
+        </template>
+        <template #type="{ col, row }">
+          {{ attendanceTypeFilter(row[col.colKey]) }}
+        </template>
+      </t-table>
+    </div>
+  </div>
+</template>
+
+<script setup name="WorkAttendanceDetail">
+import { reactive, ref } from 'vue';
+import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
+import useFetchTable from '@/hooks/useFetchTable';
+import {
+  workAttendanceDetailListApi,
+  workStatisticsDetailInfoApi,
+  workAttendanceDetailExportApi,
+} from '@/api/work-hours';
+import {
+  timestampFilter,
+  attendanceResultFilter,
+  attendanceTypeFilter,
+} from '@/utils/filter';
+import { ATTENDANCE_RESULT, ATTENDANCE_TYPE } from '@/config/constants';
+import { dictToOptionList } from '@/utils/tool';
+
+const fields = ref([
+  {
+    prop: 'serviceId',
+    label: '服务单元',
+    type: 'select',
+    labelWidth: 100,
+    colSpan: 5,
+    cell: 'service',
+  },
+  {
+    prop: 'userName',
+    label: '姓名',
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'createTime',
+    label: '考勤日期',
+    type: 'daterange',
+    labelWidth: 100,
+    colSpan: 10,
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    type: 'buttons',
+    colSpan: 3,
+    children: [
+      {
+        type: 'button',
+        text: '查询',
+        onClick: () => {
+          search();
+          getStatisticsInfo();
+        },
+      },
+    ],
+  },
+  {
+    prop: 'supplierId',
+    label: '所属供应商',
+    type: 'select',
+    labelWidth: 120,
+    colSpan: 5,
+    cell: 'supplier',
+  },
+  {
+    prop: 'custom',
+    label: '客户名称',
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'sopNo',
+    label: 'SOP流水号',
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'exceptionCount',
+    type: 'number',
+    label: '考勤异常数>',
+    labelWidth: 120,
+    colSpan: 5,
+    attrs: {
+      theme: 'column',
+      decimalPlaces: 0,
+      max: 1000000,
+      min: 0,
+      style: 'width: 120px',
+    },
+  },
+  {
+    prop: 'type',
+    type: 'number',
+    label: '考勤类型',
+    labelWidth: 120,
+    colSpan: 5,
+    options: dictToOptionList(ATTENDANCE_TYPE),
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'result',
+    type: 'number',
+    label: '考勤结果',
+    labelWidth: 120,
+    colSpan: 5,
+    options: dictToOptionList(ATTENDANCE_RESULT),
+    attrs: {
+      clearable: true,
+    },
+  },
+]);
+const params = reactive({
+  serviceId: '',
+  userName: '',
+  createTime: [],
+  supplierId: '',
+  custom: '',
+  sopNo: '',
+  exceptionCount: null,
+  type: null,
+  result: null,
+});
+const computedParams = computed(() => {
+  let data = omit(params, ['createTime']);
+  data.createStartTime = params.createTime[0];
+  data.createEndTime = params.createTime[1];
+  return data;
+});
+
+const columns = [
+  { colKey: 'serviceUnitName', title: '服务单元' },
+  { colKey: 'sopNo', title: 'SOP流水号' },
+  { colKey: 'customName', title: '客户名称' },
+  { colKey: 'province', title: '省份', minWidth: 60 },
+  { colKey: 'city', title: '城市', minWidth: 60 },
+  { colKey: 'userName', title: '姓名', width: 150 },
+  { colKey: 'roleName', title: '项目角色' },
+  { colKey: 'supplier', title: '所属供应商' },
+  { colKey: 'day', title: '考勤日期', width: 120 },
+  { colKey: 'type', title: '考勤类型', cell: 'type', width: 80 },
+  { colKey: 'createTime', title: '打卡时间', cell: 'create-time', width: 170 },
+  { colKey: 'address', title: '打卡地址' },
+  { colKey: 'result', title: '考勤结果', cell: 'result', width: 80 },
+];
+const { pagination, tableData, search, onChange } = useFetchTable(
+  workAttendanceDetailListApi,
+  {
+    params: computedParams,
+  }
+);
+let statisticsInfo = ref({});
+const getStatisticsInfo = async () => {
+  const res = await workStatisticsDetailInfoApi(params);
+  statisticsInfo.value = res || {};
+};
+
+const multExport = () => {
+  if (!selectedRowKeys.value.length) {
+    MessagePlugin.error('请选择要导出的记录');
+    return;
+  }
+  const confirmDia = DialogPlugin({
+    header: '操作提示',
+    body: `确定要导出选择的所有记录吗?`,
+    confirmBtn: '确定',
+    cancelBtn: '取消',
+    onConfirm: async () => {
+      confirmDia.hide();
+      const res = await workAttendanceDetailExportApi(computedParams).catch(
+        () => {}
+      );
+      if (!res) return;
+      MessagePlugin.success('开始下载');
+    },
+  });
+};
+</script>

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

@@ -1,15 +1,25 @@
 <template>
   <div class="work-attendance 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">
       <div class="flex justify-between items-center">
         <t-space>
-          <span>考勤总计:{{ statisticsInfo.a }}</span>
-          <span>已提交:{{ statisticsInfo.b }}</span>
-          <span>待提交:{{ statisticsInfo.c }}</span>
-          <span>累计人天:{{ statisticsInfo.d }}天</span>
-          <span>累计工时:{{ statisticsInfo.e }}小时</span>
+          <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
@@ -44,6 +54,38 @@
         select-on-row-click
         @select-change="selectChange"
       >
+        <template #user="{ row }">
+          {{ row.userName }}({{ row.userNo }})
+        </template>
+        <template #enter-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #outer-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" @click.stop>
+            <t-link theme="primary" hover="color" @click="handleSubmit(row)">
+              提交
+            </t-link>
+            <t-link theme="danger" hover="color" @click="handleWithdraw(row)">
+              撤回
+            </t-link>
+            <t-link
+              theme="danger"
+              hover="color"
+              @click="handleCancelWithdraw(row)"
+            >
+              取消撤回
+            </t-link>
+          </div>
+        </template>
       </t-table>
     </div>
   </div>
@@ -55,134 +97,61 @@ import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
 import useFetchTable from '@/hooks/useFetchTable';
 import {
   workAttendanceListApi,
-  workAttendanceInfoApi,
+  workStatisticsInfoApi,
   workAttendanceSubmitApi,
   workAttendanceExportApi,
   workAttendanceWithdrawApi,
   workAttendanceCancelWithdrawApi,
 } from '@/api/work-hours';
+import { timestampFilter, flowStatusFilter } from '@/utils/filter';
+import { FLOW_STATUS } from '@/config/constants';
+import { dictToOptionList } from '@/utils/tool';
 
 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: '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: '考勤异常数(天)', width: 140 },
-  { colKey: 'r', title: '剩余补卡次数', width: 110 },
-  { colKey: 's', title: '待处理异常数', width: 110 },
-  { colKey: 't', title: '提交状态' },
-  { colKey: 'u', title: '提交人' },
-  { colKey: 'v', title: '提交时间', width: 170 },
-  {
-    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();
-              handleSubmit(row);
-            }}
-          >
-            提交
-          </t-link>
-          <t-link
-            theme="danger"
-            hover="color"
-            onClick={(e) => {
-              e.stopPropagation();
-              handleWithdraw(row);
-            }}
-          >
-            撤回
-          </t-link>
-          <t-link
-            theme="danger"
-            hover="color"
-            onClick={(e) => {
-              e.stopPropagation();
-              handleCancelWithdraw(row);
-            }}
-          >
-            取消撤回
-          </t-link>
-        </div>
-      );
-    },
-  },
-];
-const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
-  workAttendanceListApi,
-  {
-    fetchDataHandle: () => {
-      selectedRowKeys.value = [];
-    },
-  }
-);
-let statisticsInfo = ref({ a: 1, b: 2, c: 3, d: 4, e: 5 });
-const getStatisticsInfo = async () => {
-  const res = await workAttendanceInfoApi(params);
-  statisticsInfo.value = res || {};
-};
 
 const fields = ref([
   {
-    prop: 'a',
+    prop: 'serviceId',
     label: '服务单元',
     type: 'select',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    cell: 'service',
   },
   {
-    prop: 'b',
+    prop: 'status',
     label: '提交状态',
     type: 'select',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    options: dictToOptionList(FLOW_STATUS),
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'c',
+    prop: 'createId',
     label: '提交人',
     type: 'select',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    cell: 'creator',
   },
   {
-    prop: 'd',
+    prop: 'userName',
     label: '姓名',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
     type: 'buttons',
-    colSpan: 2,
+    colSpan: 3,
     children: [
       {
         type: 'button',
@@ -195,63 +164,159 @@ const fields = ref([
     ],
   },
   {
-    prop: 'e',
+    prop: 'supplierId',
     label: '所属供应商',
     type: 'select',
     labelWidth: 120,
-    colSpan: 5.4,
+    colSpan: 5,
+    cell: 'supplier',
   },
   {
-    prop: 'f',
+    prop: 'custom',
     label: '客户名称',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'g',
+    prop: 'sopNo',
     label: 'SOP流水号',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      clearable: true,
+    },
   },
   {
-    prop: 'h',
+    prop: 'days',
+    type: 'number',
     label: '违规工时>',
-    labelWidth: 120,
-    colSpan: 5.4,
+    labelWidth: 100,
+    colSpan: 5,
+    attrs: {
+      theme: 'column',
+      decimalPlaces: 0,
+      max: 1000000,
+      min: 0,
+      style: 'width: 120px',
+    },
   },
   {
     prop: 'i',
+    type: 'number',
     label: '考勤异常数>',
     labelWidth: 120,
-    colSpan: 5.4,
+    colSpan: 5,
+    attrs: {
+      theme: 'column',
+      decimalPlaces: 0,
+      max: 1000000,
+      min: 0,
+      style: 'width: 120px',
+    },
   },
   {
     prop: 'j',
+    type: 'number',
     label: '剩余补卡次数>',
     labelWidth: 120,
-    colSpan: 5.4,
+    colSpan: 5,
+    attrs: {
+      theme: 'column',
+      decimalPlaces: 0,
+      max: 1000000,
+      min: 0,
+      style: 'width: 120px',
+    },
   },
   {
     prop: 'k',
+    type: 'number',
     label: '待处理异常数>',
     labelWidth: 120,
-    colSpan: 5.4,
+    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: '',
-  i: '',
-  j: '',
-  k: '',
+  serviceId: '',
+  status: '',
+  createId: '',
+  userName: '',
+  supplierId: '',
+  custom: '',
+  sopNo: '',
+  days: null,
+  i: null,
+  j: null,
+  k: null,
 });
 
+const columns = [
+  {
+    colKey: 'row-select',
+    type: 'multiple',
+    width: 50,
+    fixed: 'left',
+  },
+  { colKey: 'serviceUnitName', title: '服务单元' },
+  { colKey: 'sopNo', title: 'SOP流水号' },
+  { colKey: 'customName', title: '客户名称' },
+  { colKey: 'province', title: '省份', minWidth: 60 },
+  { colKey: 'city', title: '城市', minWidth: 60 },
+  { colKey: 'enterTime', title: '进场时间', cell: 'enter-time', width: 170 },
+  { colKey: 'outerTime', title: '撤场时间', cell: 'outer-time', width: 170 },
+  { 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: 'q', title: '考勤异常数(天)', width: 140 },
+  { colKey: 'r', title: '剩余补卡次数', width: 110 },
+  { colKey: 's', title: '待处理异常数', width: 110 },
+  { colKey: 'status', title: '提交状态', cell: 'status', width: 100 },
+  { colKey: 'submitter', title: '提交人' },
+  {
+    colKey: 'submissionTime',
+    title: '提交时间',
+    cell: 'submit-time',
+    width: 170,
+  },
+  {
+    title: '操作',
+    colKey: 'operate',
+    cell: 'operate',
+    fixed: 'right',
+    width: 120,
+  },
+];
+const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
+  workAttendanceListApi,
+  {
+    fetchDataHandle: () => {
+      selectedRowKeys.value = [];
+    },
+    params,
+  }
+);
+let statisticsInfo = ref({});
+const getStatisticsInfo = async () => {
+  const res = await workStatisticsInfoApi(params);
+  statisticsInfo.value = res || {};
+};
+
 const multSubmit = () => {
   if (!selectedRowKeys.value.length) {
     MessagePlugin.error('请选择要提交的记录');

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

@@ -233,7 +233,8 @@ const columns = [
   },
 ];
 const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
-  workStatisticsListApi
+  workStatisticsListApi,
+  { params }
 );
 
 let statisticsInfo = ref({});