|
@@ -1,34 +1,314 @@
|
|
-import React, { useEffect } from "react";
|
|
|
|
|
|
+import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
import styles from "./index.module.scss";
|
|
import styles from "./index.module.scss";
|
|
|
|
|
|
import { isHoliday } from "chinese-days/dist/index.min.js";
|
|
import { isHoliday } from "chinese-days/dist/index.min.js";
|
|
|
|
+import { MessageFu } from "@/utils/message";
|
|
|
|
+import { API_upFile } from "@/store/action/layout";
|
|
|
|
+import { fileDomInitialFu } from "@/utils/domShow";
|
|
|
|
+import dayjs from "dayjs";
|
|
|
|
+import ExportJsonExcel from "js-export-excel";
|
|
|
|
+import { Button } from "antd";
|
|
|
|
+import MyTable from "@/components/MyTable";
|
|
|
|
+import { API_A2getList } from "@/store/action/all";
|
|
|
|
+import A2staff from "./A2staff";
|
|
|
|
|
|
-const arr = [
|
|
|
|
- "2025-03-07",
|
|
|
|
- "2025-03-08",
|
|
|
|
- "2025-03-09",
|
|
|
|
- "2025-03-22",
|
|
|
|
- "2025-03-23",
|
|
|
|
- "2025-03-24",
|
|
|
|
- "2025-04-01",
|
|
|
|
- "2025-04-04",
|
|
|
|
- "2025-04-05",
|
|
|
|
- "2025-04-06",
|
|
|
|
- "2025-04-07",
|
|
|
|
- "2025-04-12",
|
|
|
|
-];
|
|
|
|
|
|
+type listType = {
|
|
|
|
+ startTime: string;
|
|
|
|
+ endTime: string;
|
|
|
|
+ day: number;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+type Res2ListType = {
|
|
|
|
+ account: string;
|
|
|
|
+ consumed: string;
|
|
|
|
+ date: string;
|
|
|
|
+ name: string;
|
|
|
|
+};
|
|
|
|
|
|
function A2manHour() {
|
|
function A2manHour() {
|
|
- useEffect(() => {}, []);
|
|
|
|
|
|
+ // 2个数组数据整理
|
|
|
|
+ const mergeArrays = useCallback((arr1: any[], arr2: any[]) => {
|
|
|
|
+ // 创建以姓名为键的临时对象
|
|
|
|
+ const merged = arr1.reduce((acc, item) => {
|
|
|
|
+ const converted = Object.entries(item).reduce(
|
|
|
|
+ (obj: any, [key, value]) => {
|
|
|
|
+ obj[key] = key === "姓名" ? value : Number(value);
|
|
|
|
+ return obj;
|
|
|
|
+ },
|
|
|
|
+ {}
|
|
|
|
+ );
|
|
|
|
+ acc[converted.姓名] = converted;
|
|
|
|
+ return acc;
|
|
|
|
+ }, {});
|
|
|
|
+
|
|
|
|
+ // 处理第二个数组
|
|
|
|
+ arr2.forEach((item) => {
|
|
|
|
+ const existing = merged[item.姓名];
|
|
|
|
+ const converted = Object.entries(item).reduce(
|
|
|
|
+ (obj: any, [key, value]) => {
|
|
|
|
+ obj[key] = key === "姓名" ? value : Number(value);
|
|
|
|
+ return obj;
|
|
|
|
+ },
|
|
|
|
+ {}
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (existing) {
|
|
|
|
+ // 合并相同姓名的数据
|
|
|
|
+ Object.keys(converted).forEach((key) => {
|
|
|
|
+ if (key !== "姓名") {
|
|
|
|
+ existing[key] += converted[key];
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ // 添加新条目
|
|
|
|
+ merged[converted.姓名] = converted;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ // 转换为数组并返回
|
|
|
|
+ return Object.values(merged);
|
|
|
|
+ }, []);
|
|
|
|
+
|
|
|
|
+ const [list, setList] = useState<listType[]>([]);
|
|
|
|
+
|
|
|
|
+ const myInput = useRef<HTMLInputElement>(null);
|
|
|
|
+
|
|
|
|
+ const [biaoTou, setBiaoTou] = useState<string[]>([]);
|
|
|
|
+
|
|
|
|
+ // 根据表头来算出表格需要的数据
|
|
|
|
+ const tableTouRes = useMemo(() => {
|
|
|
|
+ let arr: any = [
|
|
|
|
+ // ["txt", "节假日", "isHoliday"],
|
|
|
|
+ // ["txt", "异常", "anomaly"],
|
|
|
|
+ ];
|
|
|
|
+ biaoTou.forEach((v) => {
|
|
|
|
+ if (v === "姓名") {
|
|
|
|
+ arr.push(["txt", v, v]);
|
|
|
|
+ } else {
|
|
|
|
+ arr.push([
|
|
|
|
+ "txt",
|
|
|
|
+ isHoliday(v) ? <span className="yiTable">{v}(假)</span> : v,
|
|
|
|
+ v,
|
|
|
|
+ ]);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return arr;
|
|
|
|
+ }, [biaoTou]);
|
|
|
|
+
|
|
|
|
+ const dataChangeFu = useCallback(
|
|
|
|
+ async (resData: any) => {
|
|
|
|
+ // 表头
|
|
|
|
+ let touArr = resData[0].map((v: string) => v.replace(" 00:00:00", ""));
|
|
|
|
+
|
|
|
|
+ if (touArr.length < 3) return MessageFu.warning("最少填入2个日期");
|
|
|
|
+ if (touArr.length > 11) return MessageFu.warning("最多填入10个日期");
|
|
|
|
+
|
|
|
|
+ // [
|
|
|
|
+ // "姓名",
|
|
|
|
+ // "2025-03-05",
|
|
|
|
+ // "2025-03-06",
|
|
|
|
+ // "2025-03-07",
|
|
|
|
+ // "2025-03-08",
|
|
|
|
+ // "2025-03-09",
|
|
|
|
+ // "2025-03-10",
|
|
|
|
+ // "2025-03-11",
|
|
|
|
+ // "2025-03-12",
|
|
|
|
+ // "2025-03-13",
|
|
|
|
+ // "2025-03-14"
|
|
|
|
+ // ]
|
|
|
|
+
|
|
|
|
+ // 拿到开始时间和结束时间
|
|
|
|
+ const objTime = {
|
|
|
|
+ startTime: touArr[1] + " 00:00:00",
|
|
|
|
+ endTime: touArr[touArr.length - 1] + " 23:59:59",
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const res = await API_A2getList(objTime);
|
|
|
|
+
|
|
|
|
+ if (res.code === 0) {
|
|
|
|
+ const resData2Temp: Res2ListType[] = res.data;
|
|
|
|
+
|
|
|
|
+ const res2ttArr: any[] = [];
|
|
|
|
+
|
|
|
|
+ const res2ttObj: any = {};
|
|
|
|
+
|
|
|
|
+ resData2Temp.forEach((v) => {
|
|
|
|
+ if (res2ttObj[v.name]) {
|
|
|
|
+ res2ttObj[v.name].push(v);
|
|
|
|
+ } else res2ttObj[v.name] = [v];
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ for (const k in res2ttObj) {
|
|
|
|
+ res2ttObj[k] = res2ttObj[k].sort(
|
|
|
|
+ (a: any, b: any) =>
|
|
|
|
+ dayjs(a.date).valueOf() - dayjs(b.date).valueOf()
|
|
|
|
+ );
|
|
|
|
+ const objc: any = { 姓名: k };
|
|
|
|
+ res2ttObj[k].forEach((c: any) => {
|
|
|
|
+ objc[c.date] = Number(c.consumed);
|
|
|
|
+ });
|
|
|
|
+ res2ttArr.push(objc);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ setBiaoTou(touArr);
|
|
|
|
+
|
|
|
|
+ const resArr1: any = [];
|
|
|
|
+ const arr2: any = resData.slice(1);
|
|
|
|
+
|
|
|
|
+ arr2.forEach((v1: string[], i1: number) => {
|
|
|
|
+ let obj: any = {};
|
|
|
|
+
|
|
|
|
+ touArr.forEach((v2: string, i2: number) => {
|
|
|
|
+ obj[v2] = v1[i2] ? v1[i2] : "0";
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ resArr1.push(obj);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const resList: any[] = mergeArrays(resArr1, res2ttArr);
|
|
|
|
+
|
|
|
|
+ // 异常的处理
|
|
|
|
+ resList.forEach((v) => {
|
|
|
|
+ for (const k in v) {
|
|
|
|
+ if (k !== "姓名") {
|
|
|
|
+ let flag = isHoliday(k);
|
|
|
|
+ if (flag) {
|
|
|
|
+ if (v[k]) v[k] = v[k] + "(异)";
|
|
|
|
+ } else {
|
|
|
|
+ if (v[k] < 8) v[k] = v[k] + "(异)";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ setList(resList);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ [mergeArrays]
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // 上传表格
|
|
|
|
+ const handeUpPhoto = useCallback(
|
|
|
|
+ async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
+ if (e.target.files) {
|
|
|
|
+ // 拿到files信息
|
|
|
|
+ const filesInfo = e.target.files[0];
|
|
|
|
+
|
|
|
|
+ // 校验格式
|
|
|
|
+ // const type = format;
|
|
|
|
+ if (!filesInfo.name.includes(".xlsx"))
|
|
|
|
+ return MessageFu.warning("只支持.xlsx文件!");
|
|
|
|
+
|
|
|
|
+ // 创建FormData对象
|
|
|
|
+ const fd = new FormData();
|
|
|
|
+ fd.append("type", "doc");
|
|
|
|
+ fd.append("dirCode", "A1Day");
|
|
|
|
+ fd.append("file", filesInfo);
|
|
|
|
+
|
|
|
|
+ e.target.value = "";
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ const res = await API_upFile(fd, "cms/taskTime/upload");
|
|
|
|
+ if (res.code === 0) {
|
|
|
|
+ MessageFu.success("上传成功!");
|
|
|
|
+ // setFileUrl(res.data);
|
|
|
|
+ dataChangeFu(res.data || []);
|
|
|
|
+ }
|
|
|
|
+ fileDomInitialFu();
|
|
|
|
+ } catch (error) {
|
|
|
|
+ fileDomInitialFu();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ [dataChangeFu]
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // 点击导出
|
|
|
|
+ const deriveFu = useCallback(async () => {
|
|
|
|
+ // if (list.length <= 0) return;
|
|
|
|
+
|
|
|
|
+ const name = "工时统计" + dayjs(new Date()).format("YYYY-MM-DD HH:mm");
|
|
|
|
+
|
|
|
|
+ // console.log(123, list, biaoTou);
|
|
|
|
+
|
|
|
|
+ const option = {
|
|
|
|
+ fileName: name,
|
|
|
|
+ datas: [
|
|
|
|
+ {
|
|
|
|
+ sheetData: list,
|
|
|
|
+ sheetName: name,
|
|
|
|
+ sheetFilter: biaoTou,
|
|
|
|
+ sheetHeader: biaoTou.map((v) =>
|
|
|
|
+ v !== "姓名" ? (isHoliday(v) ? v + "(假)" : v) : v
|
|
|
|
+ ),
|
|
|
|
+ columnWidths: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const toExcel = new ExportJsonExcel(option); //new
|
|
|
|
+ toExcel.saveExcel(); //保存
|
|
|
|
+ }, [biaoTou, list]);
|
|
|
|
+
|
|
|
|
+ // 人员名单
|
|
|
|
+ const [staffShow, setStaffShow] = useState(false);
|
|
|
|
|
|
return (
|
|
return (
|
|
<div className={styles.A2manHour}>
|
|
<div className={styles.A2manHour}>
|
|
- {arr.map((v) => (
|
|
|
|
- <div key={v}>
|
|
|
|
- <span>{v}</span>
|
|
|
|
- {isHoliday(v) ? "节假日" : "工作日"}
|
|
|
|
|
|
+ <input
|
|
|
|
+ id="upInput"
|
|
|
|
+ type="file"
|
|
|
|
+ accept=".xlsx"
|
|
|
|
+ ref={myInput}
|
|
|
|
+ onChange={(e) => handeUpPhoto(e)}
|
|
|
|
+ />
|
|
|
|
+ <div className="pageTitle">
|
|
|
|
+ 工时统计-
|
|
|
|
+ <span>目前节假日只更新到2025年底。2026年之后请联系开发迭代</span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div className="A2top">
|
|
|
|
+ <Button type="primary" onClick={() => setStaffShow(true)}>
|
|
|
|
+ 人员名单
|
|
|
|
+ </Button>
|
|
|
|
+   
|
|
|
|
+ {list.length ? (
|
|
|
|
+ <>
|
|
|
|
+ <Button className="A2btn" onClick={() => setList([])}>
|
|
|
|
+ 清空
|
|
|
|
+ </Button>
|
|
|
|
+   
|
|
|
|
+ <Button type="primary" onClick={deriveFu}>
|
|
|
|
+ 导出表格
|
|
|
|
+ </Button>
|
|
|
|
+ </>
|
|
|
|
+ ) : (
|
|
|
|
+ <Button
|
|
|
|
+ type="primary"
|
|
|
|
+ className="A2btn"
|
|
|
|
+ onClick={() => myInput.current?.click()}
|
|
|
|
+ >
|
|
|
|
+ 上传表格
|
|
|
|
+ </Button>
|
|
|
|
+ )}
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div className="tableBox">
|
|
|
|
+ <MyTable
|
|
|
|
+ yHeight={707}
|
|
|
|
+ list={list}
|
|
|
|
+ columnsTemp={tableTouRes}
|
|
|
|
+ pagingInfo={false}
|
|
|
|
+ rowKey="姓名"
|
|
|
|
+ isNull="0"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ {staffShow ? (
|
|
|
|
+ <div className="StaffBox">
|
|
|
|
+ <A2staff closeFu={() => setStaffShow(false)} />
|
|
</div>
|
|
</div>
|
|
- ))}
|
|
|
|
|
|
+ ) : null}
|
|
</div>
|
|
</div>
|
|
);
|
|
);
|
|
}
|
|
}
|