刘洋 9 meses atrás
pai
commit
560d7e46ee

+ 2 - 1
src/api/work-hours.js

@@ -89,7 +89,8 @@ export const workStatisticsDetailInfoApi = (data) =>
 // work-statistics
 export const workStatisticsListApi = (data) =>
   request({
-    url: '/api/admin/tb/ding/query',
+    // url: '/api/admin/tb/ding/query',
+    url: '/api/admin/tb/ding/statistic/page',
     params: data,
   });
 export const workStatisticsInfoApi = (data) =>

+ 21 - 0
src/components/common/select-role/index.vue

@@ -5,6 +5,7 @@
     clearable
     v-bind="attrs"
     @change="onChange"
+    :disabled="checkDing && dingRole === 'REGION_COORDINATOR'"
   >
     <t-option
       v-for="item in optionList"
@@ -27,6 +28,8 @@ const attrs = useAttrs();
 const emit = defineEmits(['update:modelValue', 'change']);
 const props = defineProps({
   modelValue: { type: [Number, String, Array], default: '' },
+  checkDing: { type: Boolean, default: false },
+  dingRole: { type: String, default: '' },
 });
 const isMultiple = computed(() => {
   const multiple = attrs.multiple;
@@ -39,6 +42,7 @@ const search = async () => {
   if (!res) return;
 
   optionList.value = res;
+  fixRegionManager();
 };
 
 const onChange = () => {
@@ -64,4 +68,21 @@ watch(
     immediate: true,
   }
 );
+const fixRegionManager = () => {
+  if (
+    optionList.value?.length &&
+    props.checkDing &&
+    props.dingRole === 'REGION_COORDINATOR'
+  ) {
+    selected.value = isMultiple.value
+      ? optionList.value
+          .filter((item) => item.type === 'REGION_MANAGER')
+          .map((item) => item.id)
+      : optionList.value.find((item) => item.type === 'REGION_MANAGER')?.id;
+    onChange();
+  }
+};
+watch([() => props.checkDing, () => props.dingRole], () => {
+  fixRegionManager();
+});
 </script>

+ 8 - 0
src/config/constants.js

@@ -283,3 +283,11 @@ export const PROJECT_PROCESS = {
   FINAL: '收尾',
   FINISH: '已完成',
 };
+
+export const STATISTICAL_DIMENSION = {
+  BY_PERSON: '按人员统计',
+  BY_SOP: '按sop统计',
+  BY_CRM: '按crm统计',
+  BY_CUSTOM: '按客户统计',
+  BY_SUPPLIER: '按供应商统计',
+};

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

@@ -16,8 +16,16 @@
         </t-col>
 
         <t-col :span="6">
-          <t-form-item label="适用考勤对象">
-            <t-select value="工程师" disabled></t-select>
+          <t-form-item label="适用考勤对象" name="roles">
+            <!-- <t-select value="工程师" disabled></t-select> -->
+            <t-select v-model="formData.roles" :disabled="!!curRow">
+              <t-option
+                value="REGION_COORDINATOR"
+                label="区域协调人"
+              ></t-option>
+              <t-option value="ENGINEER" label="工程师"></t-option>
+            </t-select>
+
             <!-- <select-role
               v-model="formData.dingRoleIds"
               :min-collapsed-num="10"
@@ -92,6 +100,8 @@
               :min-collapsed-num="10"
               :filterable="false"
               multiple
+              :checkDing="true"
+              :dingRole="formData.roles"
             >
             </select-role>
           </t-form-item>
@@ -159,6 +169,7 @@ const { formData, isEdit } = useClearDialog(
     id: null,
     name: '',
     serviceId: '',
+    roles: '',
     // dingRoleIds: [],
     supplierId: '',
     signInTime: ['', ''],
@@ -181,6 +192,9 @@ const { formData, isEdit } = useClearDialog(
     formData.approveRoleIds = props.curRow.dingObjs
       .filter((item) => item.type === 'APPROVE')
       .map((item) => item.roleId);
+    formData.roles =
+      props.curRow.dingObjs.find((item) => item.type === 'DING')?.sopRoleType ||
+      '';
   }
 );
 const rules = {
@@ -235,6 +249,24 @@ const rules = {
       trigger: 'change',
     },
   ],
+  roles: [
+    {
+      required: true,
+      message: '请选择适用考勤对象',
+      type: 'error',
+      trigger: 'change',
+    },
+    {
+      validator: () => {
+        if (!formData.roles)
+          return { result: false, message: '请选择适用考勤对象' };
+
+        return { result: true, type: 'success' };
+      },
+      type: 'error',
+      trigger: 'change',
+    },
+  ],
   // signInTime: [
   //   {
   //     required: true,
@@ -319,14 +351,14 @@ const save = async () => {
     //   return { roleId: item, type: 'DING' };
     // }),
     {
-      sopRoleType: 'ENGINEER',
+      sopRoleType: formData.roles,
       type: 'DING',
     },
     ...formData.approveRoleIds.map((item) => {
       return { roleId: item, type: 'APPROVE' };
     }),
   ];
-  data = omit(data, ['dingRoleIds', 'approveRoleIds']);
+  data = omit(data, ['dingRoleIds', 'approveRoleIds', 'roles']);
 
   const res = await checkinEditApi(data).catch(() => {});
   if (!res) return;

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

@@ -21,23 +21,6 @@
       </template>
     </SearchForm>
     <div class="flex-1 page-wrap">
-      <p class="page-wrap-tips">
-        <t-space :size="0">
-          <span>
-            <ErrorCircleFilledIcon /> 考勤总计:{{ statisticsInfo.total }}
-          </span>
-          <span>已提交:{{ statisticsInfo.submitted }}</span>
-          <span>待提交:{{ statisticsInfo.unSubmitted }}</span>
-          <span>已提交累计人天:{{ statisticsInfo.allDays }}天</span>
-          <span
-            >已提交累计工时:{{ statisticsInfo.allHours?.toFixed(1) }}小时</span
-          >
-          <template #separator>
-            <t-divider layout="vertical" />
-          </template>
-        </t-space>
-      </p>
-
       <t-table
         size="small"
         row-key="id"
@@ -113,7 +96,10 @@ import {
   timestampFilter,
   attendanceStatisticsSubmitStatusFilter,
 } from '@/utils/filter';
-import { ATTENDANCE_STATISTICS_SUBMIT_STATUS } from '@/config/constants';
+import {
+  ATTENDANCE_STATISTICS_SUBMIT_STATUS,
+  STATISTICAL_DIMENSION,
+} from '@/config/constants';
 import { dictToOptionList } from '@/utils/tool';
 import usePermission from '@/hooks/usePermission';
 const { perm } = usePermission();
@@ -128,31 +114,12 @@ const fields = ref([
     cell: 'service',
   },
   {
-    prop: 'status',
-    label: '提交状态',
+    prop: 'type',
+    label: '统计维度',
     type: 'select',
     labelWidth: 80,
     colSpan: 6,
-    options: dictToOptionList(ATTENDANCE_STATISTICS_SUBMIT_STATUS).filter(
-      (item) => item.value !== 'WILL_SUBMIT'
-    ),
-    attrs: {
-      clearable: true,
-    },
-  },
-  {
-    prop: 'createId',
-    label: '提交人',
-    type: 'select',
-    labelWidth: 95,
-    colSpan: 6,
-    cell: 'creator',
-  },
-  {
-    prop: 'userName',
-    label: '姓名',
-    labelWidth: 80,
-    colSpan: 6,
+    options: dictToOptionList(STATISTICAL_DIMENSION),
     attrs: {
       clearable: true,
     },
@@ -166,66 +133,17 @@ const fields = ref([
         text: '搜索',
         onClick: () => {
           search();
-          getStatisticsInfo();
         },
       },
     ],
   },
-  {
-    prop: 'supplierId',
-    label: '所属供应商',
-    type: 'select',
-    labelWidth: 90,
-    colSpan: 6,
-    cell: 'supplier',
-  },
-  {
-    prop: 'custom',
-    label: '客户名称',
-    labelWidth: 80,
-    colSpan: 6,
-    attrs: {
-      clearable: true,
-    },
-  },
-  {
-    prop: 'sopNo',
-    label: 'SOP流水号',
-    labelWidth: 95,
-    colSpan: 6,
-    attrs: {
-      clearable: true,
-    },
-  },
-  // {
-  //   prop: 'days',
-  //   type: 'number',
-  //   label: '违规工时',
-  //   labelWidth: 80,
-  //   colSpan: 6,
-  //   attrs: {
-  //     theme: 'column',
-  //     decimalPlaces: 0,
-  //     max: 1000000,
-  //     min: 0,
-  //     label: '>',
-  //     style: 'width: 100%',
-  //   },
-  // },
 ]);
 const mixinSearch = () => {
   search();
-  getStatisticsInfo();
 };
 const params = reactive({
   serviceId: '',
-  status: '',
-  createId: '',
-  userName: '',
-  supplierId: '',
-  custom: '',
-  sopNo: '',
-  // days: '',
+  type: '',
 });
 
 const columns = [
@@ -279,12 +197,6 @@ const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
   { params }
 );
 
-let statisticsInfo = ref({});
-const getStatisticsInfo = async () => {
-  const res = await workStatisticsInfoApi(params);
-  statisticsInfo.value = res || {};
-};
-
 const handleExport = () => {
   const confirmDia = DialogPlugin({
     header: '操作提示',
@@ -300,23 +212,5 @@ const handleExport = () => {
   });
 };
 
-const handlePass = (row) => {
-  const confirmDia = DialogPlugin({
-    header: '同意撤回提示',
-    body: `您确定要同意撤回这条工时数据吗?撤回后,该工时数据可以暂时无法继续结算。`,
-    confirmBtn: '确定',
-    cancelBtn: '取消',
-    onConfirm: async () => {
-      confirmDia.hide();
-      const res = await workStatisticsPassApi(row.id).catch(() => {});
-      if (!res) return;
-      MessagePlugin.success('操作成功');
-      fetchData();
-    },
-  });
-};
-
-onMounted(() => {
-  getStatisticsInfo();
-});
+onMounted(() => {});
 </script>

+ 322 - 0
src/views/work-hours/work-hours-manage/work-statistics/index_old.vue

@@ -0,0 +1,322 @@
+<template>
+  <div class="work-statistics flex flex-col h-full">
+    <div v-if="perm.BUTTON_Export" class="page-action">
+      <t-button theme="primary" @click="handleExport">
+        <template #icon><svg-icon name="export" color="#fff" /></template
+        >导出统计结果
+      </t-button>
+    </div>
+    <SearchForm :fields="fields" :params="params" :search="mixinSearch">
+      <template #service="{ item, params }">
+        <select-service-unit
+          v-model="params[item.prop]"
+          :filterParams="{ statusList: ['PUBLISH'] }"
+        ></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">
+      <p class="page-wrap-tips">
+        <t-space :size="0">
+          <span>
+            <ErrorCircleFilledIcon /> 考勤总计:{{ statisticsInfo.total }}
+          </span>
+          <span>已提交:{{ statisticsInfo.submitted }}</span>
+          <span>待提交:{{ statisticsInfo.unSubmitted }}</span>
+          <span>已提交累计人天:{{ statisticsInfo.allDays }}天</span>
+          <span
+            >已提交累计工时:{{ statisticsInfo.allHours?.toFixed(1) }}小时</span
+          >
+          <template #separator>
+            <t-divider layout="vertical" />
+          </template>
+        </t-space>
+      </p>
+
+      <t-table
+        size="small"
+        row-key="id"
+        :columns="columns"
+        :data="tableData"
+        bordered
+        :pagination="{
+          defaultCurrent: 1,
+          defaultPageSize: 10,
+          onChange,
+          showJumper: true,
+          showPageSize: false,
+          total: pagination.total,
+          current: pagination.pageNumber,
+        }"
+      >
+        <template #user="{ row }">
+          {{ row.userName }}({{ row.userNo }})
+        </template>
+        <template #scanStartTime="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #scanEndTime="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #markPaperStartTime="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #markPaperEndTime="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </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 }">
+          {{ attendanceStatisticsSubmitStatusFilter(row[col.colKey]) }}
+        </template>
+        <template #operate="{ row }">
+          <div v-if="perm.LINK_Reject" class="table-operations">
+            <t-link
+              :disabled="row.status !== 'APPLY_WITHDRAW'"
+              theme="primary"
+              hover="color"
+              @click="handlePass(row)"
+            >
+              同意撤回
+            </t-link>
+          </div>
+        </template>
+      </t-table>
+    </div>
+  </div>
+</template>
+
+<script setup name="WorkStatistics">
+import { ref, reactive, onMounted } from 'vue';
+import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
+import { ErrorCircleFilledIcon } from 'tdesign-icons-vue-next';
+import useFetchTable from '@/hooks/useFetchTable';
+import {
+  workStatisticsListApi,
+  workStatisticsInfoApi,
+  workStatisticsExportApi,
+  workStatisticsPassApi,
+} from '@/api/work-hours';
+import {
+  timestampFilter,
+  attendanceStatisticsSubmitStatusFilter,
+} from '@/utils/filter';
+import { ATTENDANCE_STATISTICS_SUBMIT_STATUS } from '@/config/constants';
+import { dictToOptionList } from '@/utils/tool';
+import usePermission from '@/hooks/usePermission';
+const { perm } = usePermission();
+
+const fields = ref([
+  {
+    prop: 'serviceId',
+    label: '服务单元',
+    type: 'select',
+    labelWidth: 90,
+    colSpan: 6,
+    cell: 'service',
+  },
+  {
+    prop: 'status',
+    label: '提交状态',
+    type: 'select',
+    labelWidth: 80,
+    colSpan: 6,
+    options: dictToOptionList(ATTENDANCE_STATISTICS_SUBMIT_STATUS).filter(
+      (item) => item.value !== 'WILL_SUBMIT'
+    ),
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'createId',
+    label: '提交人',
+    type: 'select',
+    labelWidth: 95,
+    colSpan: 6,
+    cell: 'creator',
+  },
+  {
+    prop: 'userName',
+    label: '姓名',
+    labelWidth: 80,
+    colSpan: 6,
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    type: 'buttons',
+    colSpan: 3,
+    children: [
+      {
+        type: 'button',
+        text: '搜索',
+        onClick: () => {
+          search();
+          getStatisticsInfo();
+        },
+      },
+    ],
+  },
+  {
+    prop: 'supplierId',
+    label: '所属供应商',
+    type: 'select',
+    labelWidth: 90,
+    colSpan: 6,
+    cell: 'supplier',
+  },
+  {
+    prop: 'custom',
+    label: '客户名称',
+    labelWidth: 80,
+    colSpan: 6,
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'sopNo',
+    label: 'SOP流水号',
+    labelWidth: 95,
+    colSpan: 6,
+    attrs: {
+      clearable: true,
+    },
+  },
+  // {
+  //   prop: 'days',
+  //   type: 'number',
+  //   label: '违规工时',
+  //   labelWidth: 80,
+  //   colSpan: 6,
+  //   attrs: {
+  //     theme: 'column',
+  //     decimalPlaces: 0,
+  //     max: 1000000,
+  //     min: 0,
+  //     label: '>',
+  //     style: 'width: 100%',
+  //   },
+  // },
+]);
+const mixinSearch = () => {
+  search();
+  getStatisticsInfo();
+};
+const params = reactive({
+  serviceId: '',
+  status: '',
+  createId: '',
+  userName: '',
+  supplierId: '',
+  custom: '',
+  sopNo: '',
+  // days: '',
+});
+
+const columns = [
+  { colKey: 'service', title: '服务单元', width: 160 },
+  { colKey: 'sopNo', title: 'SOP流水号', width: 200 },
+  { colKey: 'custom', title: '客户名称', width: 120 },
+  { colKey: 'province', title: '省份', width: 120 },
+  { colKey: 'city', title: '城市', width: 120 },
+  // {
+  //   colKey: 'examStartTime',
+  //   title: '项目开始时间',
+  //   width: 180,
+  //   cell: 'start-time',
+  // },
+  // {
+  //   colKey: 'examEndTime',
+  //   title: '项目结束时间',
+  //   width: 180,
+  //   cell: 'end-time',
+  // },
+  { colKey: 'scanStartTime', title: '扫描开始时间', width: 180 },
+  { colKey: 'scanEndTime', title: '扫描结束时间', width: 180 },
+  { colKey: 'markPaperStartTime', title: '阅卷开始时间', width: 180 },
+  { colKey: 'markPaperEndTime', title: '阅卷结束时间', width: 180 },
+  { colKey: 'userName', title: '姓名(人员档案号)', cell: 'user', width: 170 },
+  { colKey: 'roleName', title: '项目角色', width: 120 },
+  { colKey: 'supplier', title: '供应商' },
+  { colKey: 'attendance', title: '实际出勤(天)', width: 140 },
+  { colKey: 'weekdays', title: '工作日(天)', width: 130 },
+  { colKey: 'weekends', title: '周末(天)', width: 120 },
+  { colKey: 'holidays', title: '法定节假日(天)', width: 160 },
+  { colKey: 'workHours', title: '累计工时(小时)', width: 140 },
+  // { colKey: 'violationDays', title: '违规工时(天)', width: 140 },
+  { colKey: 'submitter', title: '提交人', width: 120 },
+  {
+    colKey: 'submissionTime',
+    title: '提交时间',
+    cell: 'submit-time',
+    width: 180,
+  },
+  { colKey: 'status', title: '提交状态', cell: 'status', width: 100 },
+  {
+    title: '管理',
+    colKey: 'operate',
+    fixed: 'right',
+    width: 100,
+  },
+];
+const { pagination, tableData, fetchData, search, onChange } = useFetchTable(
+  workStatisticsListApi,
+  { params }
+);
+
+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: '同意撤回提示',
+    body: `您确定要同意撤回这条工时数据吗?撤回后,该工时数据可以暂时无法继续结算。`,
+    confirmBtn: '确定',
+    cancelBtn: '取消',
+    onConfirm: async () => {
+      confirmDia.hide();
+      const res = await workStatisticsPassApi(row.id).catch(() => {});
+      if (!res) return;
+      MessagePlugin.success('操作成功');
+      fetchData();
+    },
+  });
+};
+
+onMounted(() => {
+  getStatisticsInfo();
+});
+</script>