From 127ed073830f336012785cdefa70678e9c2bd6d5 Mon Sep 17 00:00:00 2001 From: linfeng <2819853134@qq.com> Date: Mon, 24 Mar 2025 20:16:37 +0800 Subject: [PATCH] lin --- apps/web/package.json | 8 +- .../app/main/{dailyPage => daily}/page.tsx | 0 apps/web/src/app/main/home/page.tsx | 4 +- apps/web/src/app/main/layout/MainHeader.tsx | 51 +-- .../src/app/main/layout/NavigationMenu.tsx | 331 ++++++++++++++++-- apps/web/src/app/main/plan/monthplan/page.tsx | 0 apps/web/src/app/main/plan/page.tsx | 0 apps/web/src/app/main/plan/weekplan/page.tsx | 227 ++++++++++++ apps/web/src/app/main/staffpage/page.tsx | 5 +- .../app/main/staffpage/staffmodal/page.tsx | 128 +++---- .../app/main/staffpage/stafftable/page.tsx | 196 +++++++---- .../department/department-children-select.tsx | 15 +- .../train-content-tree-select.tsx | 8 +- .../models/trainPlan/TrainPlanLayout.tsx | 5 +- .../models/trainPlan/TrainPlanWrite.tsx | 3 + apps/web/src/routes/index.tsx | 39 ++- config/nginx/conf.d/web.conf | 4 +- pnpm-lock.yaml | 169 +++++++-- 18 files changed, 959 insertions(+), 234 deletions(-) rename apps/web/src/app/main/{dailyPage => daily}/page.tsx (100%) create mode 100644 apps/web/src/app/main/plan/monthplan/page.tsx create mode 100644 apps/web/src/app/main/plan/page.tsx create mode 100644 apps/web/src/app/main/plan/weekplan/page.tsx create mode 100644 apps/web/src/components/models/trainPlan/TrainPlanWrite.tsx diff --git a/apps/web/package.json b/apps/web/package.json index b484901..7649726 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -23,7 +23,7 @@ "@ag-grid-enterprise/server-side-row-model": "~32.3.2", "@ag-grid-enterprise/set-filter": "~32.3.2", "@ag-grid-enterprise/status-bar": "~32.3.2", - "@ant-design/icons": "^5.4.0", + "@ant-design/icons": "^5.5.2", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -41,11 +41,12 @@ "@trpc/client": "11.0.0-rc.456", "@trpc/react-query": "11.0.0-rc.456", "@trpc/server": "11.0.0-rc.456", + "@types/react-redux": "^7.1.34", "@xyflow/react": "^12.3.6", "ag-grid-community": "~32.3.2", "ag-grid-enterprise": "~32.3.2", "ag-grid-react": "~32.3.2", - "antd": "^5.19.3", + "antd": "^5.23.0", "axios": "^1.7.2", "browser-image-compression": "^2.0.2", "class-variance-authority": "^0.7.1", @@ -64,12 +65,14 @@ "react-dom": "18.2.0", "react-hook-form": "^7.54.2", "react-hot-toast": "^2.4.1", + "react-redux": "^9.2.0", "react-resizable": "^3.0.5", "react-router-dom": "^6.24.1", "superjson": "^2.2.1", "tailwind-merge": "^2.6.0", "use-debounce": "^10.0.4", "uuid": "^10.0.0", + "xlsx": "^0.18.5", "yjs": "^13.6.20", "zod": "^3.23.8" }, @@ -77,6 +80,7 @@ "@eslint/js": "^9.9.0", "@types/react": "18.2.38", "@types/react-dom": "18.2.15", + "@types/xlsx": "^0.0.36", "@vitejs/plugin-react-swc": "^3.5.0", "autoprefixer": "^10.4.20", "eslint": "^9.9.0", diff --git a/apps/web/src/app/main/dailyPage/page.tsx b/apps/web/src/app/main/daily/page.tsx similarity index 100% rename from apps/web/src/app/main/dailyPage/page.tsx rename to apps/web/src/app/main/daily/page.tsx diff --git a/apps/web/src/app/main/home/page.tsx b/apps/web/src/app/main/home/page.tsx index 729e69d..d6c0312 100755 --- a/apps/web/src/app/main/home/page.tsx +++ b/apps/web/src/app/main/home/page.tsx @@ -1,9 +1,9 @@ import React from "react" -export default function HomePage() { +export default function Dashboard() { return (
- 首页 + 数据看板(待开发)
) } \ No newline at end of file diff --git a/apps/web/src/app/main/layout/MainHeader.tsx b/apps/web/src/app/main/layout/MainHeader.tsx index fee3ad5..737fe8f 100644 --- a/apps/web/src/app/main/layout/MainHeader.tsx +++ b/apps/web/src/app/main/layout/MainHeader.tsx @@ -1,43 +1,30 @@ -import { Layout, Menu, Avatar, Button } from 'antd'; -import { UserOutlined, SettingOutlined } from '@ant-design/icons'; +import { Layout, Avatar, Dropdown, Menu } from 'antd'; +import { UserOutlined } from '@ant-design/icons'; import { useNavigate, Outlet, useLocation } from 'react-router-dom'; import NavigationMenu from './NavigationMenu'; +import { Header } from 'antd/es/layout/layout'; +import { useState } from 'react'; const { Sider, Content } = Layout; export default function MainHeader() { - const navigate = useNavigate(); - - return ( - - {/* 左侧导航栏 */} - - {/* 用户头像区域 */} -
-
- } - className=" transition-all duration-300" - /> -
-
- -
- {/* 新增可滚动内容区域 */} - - + + {/* 顶部Header */} +
+ +
+ + {/* 主体布局 */} + + {/* 左侧导航栏 */} + + + + + {/* 内容区域 */} + diff --git a/apps/web/src/app/main/layout/NavigationMenu.tsx b/apps/web/src/app/main/layout/NavigationMenu.tsx index b8c2ef8..b645dbc 100644 --- a/apps/web/src/app/main/layout/NavigationMenu.tsx +++ b/apps/web/src/app/main/layout/NavigationMenu.tsx @@ -1,40 +1,307 @@ +import React, { useEffect, useState } from "react"; import { Menu } from "antd"; +// import { useSelector } from "react-redux"; import { useNavigate, useLocation } from "react-router-dom"; +// import styles from "./index.module.less"; +// import logo from "../../assets/logo.png"; -export default function NavigationMenu() { - const navigate = useNavigate(); - const location = useLocation(); - console.log(location.pathname); - // 导航菜单项配置 - const menuItems = [ - { key: 'staff', label: '人员总览', path: '/' }, // 将path改为根路径 - { key: 'plan', label: '培训计划', path: '/plan' }, - { key: 'day', label: '每日填报', path: '/daily' }, - { key: 'exam', label: '考核成绩', path: '/exam' }, - ]; +function getItem( + label: any, + key: any, + icon: any, + children: any, + type: any, + // permission: any +) { + return { + key, + icon, + children, + label, + type, + // permission, + }; +} +const items = [ + getItem( + "首页概览", + "/", + , + null, + null, + ), + getItem( + "人员总览", + "/staff", + , + null, + null, + ), + getItem( + "训练计划", + "/plan", + , + [ + getItem("周训练计划", "/plan/weekplan", null, null, null), + getItem("月训练计划", "/plan/monthplan", null, null, null), + ], + null, + ), + getItem( + "每日填报", + "/daily", + , + null, + null, + ), + getItem( + "考核成绩", + "/assessment", + , + [ + getItem("岗位", "/assessment/positionassessment", null, null, null), + getItem("共同", "/assessment/commonassessment", null, null, null), + getItem("体育", "/assessment/sportsassessment", null, null, null), + ], + null, + ) +]; + +const NavigationMenu: React.FC = () => { + const location = useLocation(); + const navigate = useNavigate(); + const children2Parent: any = { + "^/plan/weekplan": ["/plan"], + "^/plan/monthplan": ["/plan"], + // 添加考核成绩子路径的匹配规则 + "^/assessment/positionassessment": ["/assessment"], + "^/assessment/commonassessment": ["/assessment"], + "^/assessment/sportsassessment": ["/assessment"] + }; + + // 同时在 useEffect 中更新路径判断逻辑 + useEffect(() => { + if (location.pathname.indexOf("/staff") !== -1) { + setSelectedKeys(["/staff"]); + setOpenKeys(openKeyMerge("/staff")); + } else if ( + // 添加考核成绩路径的判断 + location.pathname.startsWith("/assessment/") || + location.pathname === "/plan/weekplan" || + location.pathname === "/plan/monthplan" + ) { + setSelectedKeys([location.pathname]); + setOpenKeys([location.pathname.split('/').slice(0, 2).join('/')]); + } else { + setSelectedKeys([location.pathname]); + setOpenKeys(openKeyMerge(location.pathname)); + } + }, [location.pathname]); + + const hit = (pathname: string): string[] => { + for (let p in children2Parent) { + if (pathname.search(p) >= 0) { + return children2Parent[p]; + } + } + return []; + }; + + const openKeyMerge = (pathname: string): string[] => { + let newOpenKeys = hit(pathname); + for (let i = 0; i < openKeys.length; i++) { + let isIn = false; + for (let j = 0; j < newOpenKeys.length; j++) { + if (newOpenKeys[j] === openKeys[i]) { + isIn = true; + break; + } + } + if (isIn) { + continue; + } + newOpenKeys.push(openKeys[i]); + } + return newOpenKeys; + }; + + // 选中的菜单 + const [selectedKeys, setSelectedKeys] = useState([ + location.pathname, + ]); + // 展开菜单 + const [openKeys, setOpenKeys] = useState(hit(location.pathname)); + // const permissions = useSelector( + // (state: any) => state.loginUser.value.permissions + // ); + const [activeMenus, setActiveMenus] = useState(items); + + const onClick = (e: any) => { + navigate(e.key); + }; + // console.log(items) + useEffect(() => { + setActiveMenus(items); + // console.log(activeMenus) + },[items]) + // useEffect(() => { + // checkMenuPermissions(items, permissions); + // }, [items, permissions]); + + // const checkMenuPermissions = (items: any, permissions: any) => { + // let menus: any = []; + // if (permissions.length === 0) { + // setActiveMenus(menus); + // return; + // } + + // for (let i in items) { + // let menuItem = items[i]; + // // 一级菜单=>没有子菜单&配置了权限 + // if (menuItem.children === null) { + // if ( + // menuItem.permission !== null && + // typeof permissions[menuItem.permission] === "undefined" + // ) { + // continue; + // } + // menus.push(menuItem); + // continue; + // } + // let children = []; + + // for (let j in menuItem.children) { + // let childrenItem = menuItem.children[j]; + + // if ( + // typeof permissions[childrenItem.permission] !== "undefined" || + // !childrenItem.permission + // ) { + // // 存在权限 + // children.push(childrenItem); + // } + // } + + // if (children.length > 0) { + // menus.push(Object.assign({}, menuItem, { children: children })); + // } + // } + // setActiveMenus(menus); + // }; + + useEffect(() => { + if (location.pathname.indexOf("/staff") !== -1) { + setSelectedKeys(["/staff"]); + setOpenKeys(openKeyMerge("/staff")); // 修正为当前路径的父级 + } else if ( + location.pathname === "/weekplan" || + location.pathname === "/monthplan" + ) { + setSelectedKeys([location.pathname]); + setOpenKeys(["/plan"]); // 直接展开训练计划父菜单 + } else { + setSelectedKeys([location.pathname]); + setOpenKeys(openKeyMerge(location.pathname)); + } + }, [location.pathname]); return ( - <> +
+
{ + window.location.href = "/"; + }} + > + {/* 此处为版权标识,严禁删改 + */} +
+
location.pathname.startsWith(item.path))?.key, - ]} - > - {menuItems.map((item) => ( - navigate(item.path)} - > -
- {item.label} -
-
- ))} -
- + items={activeMenus} + onSelect={(data: any) => { + setSelectedKeys(data.selectedKeys); + }} + onOpenChange={(keys: any) => { + setOpenKeys(keys); + }} + /> +
+
); -} \ No newline at end of file +}; + +export default NavigationMenu; + + + + + + + + + + + + + + + + + + +// import { Menu } from "antd"; +// import { useNavigate, useLocation } from "react-router-dom"; + +// export default function NavigationMenu() { +// const navigate = useNavigate(); +// const location = useLocation(); +// console.log(location.pathname); +// // 导航菜单项配置 +// const menuItems = [ +// { key: 'staff', label: '人员总览', path: '/' }, // 将path改为根路径 +// { key: 'plan', label: '培训计划', path: '/plan' }, +// { key: 'day', label: '每日填报', path: '/daily' }, +// { key: 'exam', label: '考核成绩', path: '/exam' }, +// ]; + +// return ( +// <> +// location.pathname.startsWith(item.path))?.key, +// ]} +// > +// {menuItems.map((item) => ( +// navigate(item.path)} +// > +//
+// {item.label} +//
+//
+// ))} +//
+// +// ); +// } \ No newline at end of file diff --git a/apps/web/src/app/main/plan/monthplan/page.tsx b/apps/web/src/app/main/plan/monthplan/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/src/app/main/plan/page.tsx b/apps/web/src/app/main/plan/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/src/app/main/plan/weekplan/page.tsx b/apps/web/src/app/main/plan/weekplan/page.tsx new file mode 100644 index 0000000..2d870a5 --- /dev/null +++ b/apps/web/src/app/main/plan/weekplan/page.tsx @@ -0,0 +1,227 @@ +import { useState } from 'react' +import * as XLSX from 'xlsx' +import { Table, Select, Pagination } from 'antd' +import type { ColumnsType, ColumnType } from 'antd/es/table' +import { UploadOutlined } from '@ant-design/icons' + +interface TableData { + key: string + [key: string]: any +} + +export default function WeekPlanPage() { + const [data, setData] = useState([]) + const [columns, setColumns] = useState>([]) + const [currentPage, setCurrentPage] = useState(1) + const [trainingStatus, setTrainingStatus] = useState>({}) + const pageSize = 1 // 每页显示一个第一列的唯一值 + + const handleFileUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0] + if (!file) return + + const reader = new FileReader() + reader.onload = (e) => { + const data = new Uint8Array(e.target?.result as ArrayBuffer) + const workbook = XLSX.read(data, { type: 'array' }) + const firstSheet = workbook.Sheets[workbook.SheetNames[0]] + + // 处理合并单元格 + if (firstSheet['!merges']) { + firstSheet['!merges'].forEach(merge => { + // 获取合并区域的起始单元格的值 + const firstCell = XLSX.utils.encode_cell({ r: merge.s.r, c: merge.s.c }) + const firstCellValue = firstSheet[firstCell]?.v + + // 将合并区域内的所有单元格设置为相同的值 + for (let row = merge.s.r; row <= merge.e.r; row++) { + for (let col = merge.s.c; col <= merge.e.c; col++) { + const cell = XLSX.utils.encode_cell({ r: row, c: col }) + if (!firstSheet[cell]) { + firstSheet[cell] = { t: 's', v: firstCellValue } + } + } + } + }) + } + + const jsonData: any[] = XLSX.utils.sheet_to_json(firstSheet, { + header: 1, + defval: '', + blankrows: false, + raw: false + }) + + // 处理表头和数据 + const headers = jsonData[0] + const tableData: TableData[] = jsonData.slice(1).map((row, index) => ({ + key: index.toString(), + ...headers.reduce((acc: any, header: string, idx: number) => { + acc[header] = row[idx] + return acc + }, {}) + })) + + // 创建列配置 + const tableColumns: ColumnsType = headers.map((header: string, index: number) => ({ + title: header, + dataIndex: header, + key: header, + width: 150, + align: 'center', + filterMultiple: true, + filters: Array.from(new Set(tableData.map(item => item[header]))) + .filter(Boolean) + .map(value => ({ text: String(value), value: String(value) })), + onFilter: (value, record) => String(record[header]) === value, + ...(index < headers.length - 1 && { + render: (text, record, index) => ({ + children: text, + props: { + rowSpan: calculateRowSpan(tableData, index, header), + style: { + border: '1px solid #f0f0f0', + borderBottom: '1px solid #f0f0f0' + } + } + }) + }) + })) + // 添加是否参训列 + tableColumns.push({ + title: '是否参训', + dataIndex: 'isTraining', + key: 'isTraining', + width: 120, + align: 'center', + render: (_, record) => ( + +
+ +

点击或拖拽文件到此处上传

+

支持 .xlsx, .xls 格式的Excel文件

+
+ + + {data.length > 0 && ( + <> + +
+ +
+ + item[(columns[0] as ColumnType)?.dataIndex as string] + ))).filter(Boolean).length} + pageSize={1} + onChange={(page) => { + setCurrentPage(page) + }} + /> + + )} + + ) +} \ No newline at end of file diff --git a/apps/web/src/app/main/staffpage/page.tsx b/apps/web/src/app/main/staffpage/page.tsx index 97e2d3d..2c407d2 100644 --- a/apps/web/src/app/main/staffpage/page.tsx +++ b/apps/web/src/app/main/staffpage/page.tsx @@ -8,20 +8,21 @@ import { useMainContext } from "../layout/MainProvider"; import StaffTable from "./stafftable/page"; import StaffModal from "./staffmodal/page"; export default function StaffMessage() { - const {form, formValue, setFormValue, setVisible, setSearchValue} = useMainContext(); + const {form, formValue, setFormValue, setVisible, setSearchValue, editingRecord} = useMainContext(); useEffect(()=>{ setFormValue( { username: "", deptId: "", absent: false, - position: "", + positionId: "", trainSituations: "", } ) },[]) const handleNew = () => { form.setFieldsValue(formValue); + console.log(editingRecord); setVisible(true); } // 添加防抖的搜索处理函数 diff --git a/apps/web/src/app/main/staffpage/staffmodal/page.tsx b/apps/web/src/app/main/staffpage/staffmodal/page.tsx index 8852217..9aef8ff 100644 --- a/apps/web/src/app/main/staffpage/staffmodal/page.tsx +++ b/apps/web/src/app/main/staffpage/staffmodal/page.tsx @@ -2,12 +2,12 @@ import { api, useStaff } from "@nice/client"; import { useMainContext } from "../../layout/MainProvider"; import toast from "react-hot-toast"; import { Button, Form, Input, Modal, Select } from "antd"; -import DepartmentSelect from "@web/src/components/models/department/department-select"; import { useEffect } from "react"; +import TrainContentTreeSelect from "@web/src/components/models/trainContent/train-content-tree-select"; +import DepartmentChildrenSelect from "@web/src/components/models/department/department-children-select"; export default function StaffModal() { const { data: traincontents} = api.trainSituation.findMany.useQuery({ - select:{ id: true, trainContent:{ @@ -18,79 +18,88 @@ export default function StaffModal() { } }, } + }); - useEffect(() => { - traincontents?.forEach((situation)=>{ - console.log(situation.trainContent.title); - }); - }, [traincontents]); + // useEffect(() => { + // traincontents?.forEach((situation)=>{ + // console.log(situation.id); + // }); + // }, [traincontents]); - const { form, formValue, setVisible, visible, editingRecord } = useMainContext() + const { form, formValue, setVisible, visible, editingRecord, setEditingRecord } = useMainContext() const { create, update } = useStaff(); const handleOk = async () => { const values = await form.getFieldsValue(); - console.log(values.username); try { - if (editingRecord && editingRecord.id) { - const result = await update.mutateAsync( - { - where: { - id: editingRecord.id, + const staffData = { + username: values.username, + showname: values.username, + department: values.deptId ? { + connect: { id: values.deptId } + } : undefined, + position: values.positionId ? { + connect: { id: values.positionId } + } : undefined, + absent: values.absent, + trainSituations: values.trainSituations?.length > 0 ? { + create: values.trainSituations.map((situation) => ({ + trainContent: { + connect: { id: situation.trainContentId } }, - data: { - username: values.username, - deptId: values.deptId, - position: values.position, - absent: values.absent, - trainSituations: values.trainSituations ? { - upsert: values.trainSituations.map((situation) => ({ - where: { id: situation.id || "" }, - update: { - mustTrainTime: situation.mustTrainTime, - trainContent: { connect: { id: situation.trainContentId } }, - // 其他字段... - }, - })) - } : undefined, - updatedAt: new Date() - } as any - } - ); - // console.log(result); - } else { - await create.mutateAsync( - { - data: { - username: values.username, - deptId: values.deptId, - createdAt: new Date(), - showname: values.username, - absent: values.absent, - trainSituations: { - create: values.trainSituations.map((situation) => ({ - trainContent: { connect: { id: situation.trainContentId } }, - mustTrainTime: situation.mustTrainTime, - // 其他必填字段... - })) - } + mustTrainTime: parseFloat(situation.mustTrainTime) || 0, + alreadyTrainTime: 0, + score: 0 + })) + } : undefined + }; + + if (editingRecord?.id) { + await update.mutateAsync({ + where: { id: editingRecord.id }, + data: { + ...staffData, + trainSituations: { + deleteMany: {}, + create: values.trainSituations?.map((situation) => ({ + trainContent: { + connect: { id: situation.trainContentId } + }, + mustTrainTime: parseFloat(situation.mustTrainTime) || 0, + alreadyTrainTime: situation.alreadyTrainTime || 0, + // score: situation.score || 0 + })) || [] } } - ); + }); + } else { + await create.mutateAsync({ + data: staffData + }); } + toast.success("保存成功"); setVisible(false); + setEditingRecord(null); + form.resetFields(); } catch (error) { + console.error("保存失败:", error); toast.error("保存失败"); - throw error; } }; const handleCancel = () => { setVisible(false); + setEditingRecord(null); + form.resetFields(); }; + + useEffect(() => { + if (visible&&editingRecord) { + form.setFieldsValue(editingRecord); + } + }, [visible,editingRecord]); return ( <> - {/* 模态框样式更新 */} - + @@ -139,11 +145,11 @@ export default function StaffModal() {
- + - + {/* */} ) } \ No newline at end of file diff --git a/apps/web/src/app/main/staffpage/stafftable/page.tsx b/apps/web/src/app/main/staffpage/stafftable/page.tsx index d140185..2783e70 100644 --- a/apps/web/src/app/main/staffpage/stafftable/page.tsx +++ b/apps/web/src/app/main/staffpage/stafftable/page.tsx @@ -1,25 +1,60 @@ -import { Button, Select, Table } from "antd" +import { Button, Select, Table, Modal } from "antd" import { StaffDto } from "@nice/common"; import { useStaff, api } from "@nice/client"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import React from "react"; import { useMainContext } from "../../layout/MainProvider"; +import { render } from "react-dom"; + +// 提取处理嵌套字段的函数 +const getNestedValue = (record: any, dataIndex: string | string[]) => { + if (Array.isArray(dataIndex)) { + return dataIndex.reduce((obj, key) => obj?.[key], record); + } + return record[dataIndex]; +}; export default function StaffTable() { const{form, setVisible,searchValue} = useMainContext() const { data: staffs, isLoading } = api.staff.findMany.useQuery({ where: { + deletedAt: null, username: { contains: searchValue } + }, + include: { + department: true, + position: true, + trainSituations: { + include: { + trainContent: { + select: { + id: true, + title: true + } + } + } + } } }); - // console.log(staffs.map((staff) => staff.absent)); - const { create, update } = useStaff(); + useEffect(() => { + console.log(staffs); + }, [staffs]); + const { softDeleteByIds } = useStaff(); const {editingRecord, setEditingRecord} = useMainContext(); - const colnums = [ + const [isTrainingModalVisible, setIsTrainingModalVisible] = useState(false); + const [selectedTrainings, setSelectedTrainings] = useState([]); + + const showTrainingDetails = (situations) => { + setSelectedTrainings(situations); + setIsTrainingModalVisible(true); + }; + + // 修正拼写错误 + const columns = [ { title: "姓名", dataIndex: "username", @@ -27,52 +62,81 @@ export default function StaffTable() { }, { title: "部门", - dataIndex: "deptId", + dataIndex: ["department", "name"], key: "deptId", + render: (_, record) => record.department?.name || "无部门" }, { title: "职务", - dataIndex: "positionId", - key: "positionId", + dataIndex: ["position", "type"], + key: "position", + render: (_, record) => record.position?.type || "无职务" }, { title: "在位", dataIndex: "absent", key: "absent", - render: (_, record) => ( - - ) + render: (absent) => absent === null ? "未知" : absent ? "否" : "是" }, { - title: "应时", - dataIndex: "trainSituation", - key: "trainSituation", + title: "培训情况", + dataIndex: "trainSituations", + key: "trainSituations", + render: (situations) => { + if (!situations?.length) return "无培训记录"; + + return ( +
+ {situations.slice(0, 2).map((s) => ( +
+ {s.trainContent?.title}: + 已完成 {s.alreadyTrainTime}/{s.mustTrainTime} 小时 +
+ ))} + {situations.length > 2 && ( + showTrainingDetails(situations)} + > + 还有 {situations.length - 2} 条记录 + + )} +
+ ); + } }, { title: "操作", key: "action", render: (_, record) => ( - +
+ + +
), } ]; @@ -84,7 +148,7 @@ export default function StaffTable() { }, [editingRecord]); const handleEdit = (record) => { setEditingRecord(record); - form.setFieldsValue(editingRecord); + form.setFieldsValue(record); // 修正为设置当前记录的值 setVisible(true); }; return ( @@ -94,37 +158,21 @@ export default function StaffTable() { (
({ - onMouseEnter: () => { - const row = document.querySelector(`tr[data-row-key="${record.id}"]`); - if (row) { - row.classList.add("bg-gray-100"); - } - }, - onMouseLeave: () => { - const row = document.querySelector(`tr[data-row-key="${record.id}"]`); - if (row) { - row.classList.remove("bg-gray-100"); - } - }, - })} > - + - {colnums.map((column) => ( + {columns.map((column) => ( @@ -135,14 +183,14 @@ export default function StaffTable() { {staffs?.map((record) => ( - {colnums.map((column) => ( - ))} @@ -151,8 +199,28 @@ export default function StaffTable() {
{column.title}
- {column.render?.(record[column.dataIndex], record) || record[column.dataIndex]} + {columns.map((column) => ( + // 使用提取的函数处理嵌套字段 + + {column.render?.( + getNestedValue(record, column.dataIndex), + record + ) || getNestedValue(record, column.dataIndex)}
) } + + setIsTrainingModalVisible(false)} + footer={null} + width={600} + > +
+ {selectedTrainings.map((training) => ( +
+

{training.trainContent?.title}

+
+
必修时长: {training.mustTrainTime} 小时
+
已完成时长: {training.alreadyTrainTime} 小时
+
完成率: {((training.alreadyTrainTime / training.mustTrainTime) * 100).toFixed(1)}%
+ {/*
得分: {training.score}
*/} +
+
+ ))} +
+
); - - } diff --git a/apps/web/src/components/models/department/department-children-select.tsx b/apps/web/src/components/models/department/department-children-select.tsx index abe6975..ba6de6a 100644 --- a/apps/web/src/components/models/department/department-children-select.tsx +++ b/apps/web/src/components/models/department/department-children-select.tsx @@ -2,18 +2,25 @@ import { useAuth } from "@web/src/providers/auth-provider" import { api } from "@nice/client" import { Select } from "antd" import { Department } from "packages/common/dist" +import { useEffect } from "react" -export default function DepartmentChildrenSelect() { +interface Props { + value?: string | null + onChange?: (value: string | null) => void +} +export default function DepartmentChildrenSelect({value, onChange}: Props) { const { user, isAuthenticated } = useAuth() + + // const { data: depts, isLoading: deptsLoading } + // const { user, isAuthenticated } = useAuth() const { data: depts, isLoading: deptsLoading } - : { data: Department[], isLoading: boolean } = isAuthenticated ? api.department.getChildSimpleTree.useQuery({ rootId: user.deptId }) : { data: null, isLoading: false } const deptSelectOptions = depts?.map((dept) => ({ - label: dept.name, + label: dept.title, value: dept.id })) return ( @@ -21,6 +28,8 @@ export default function DepartmentChildrenSelect() { placeholder="请选择单位" optionFilterProp="label" options={deptSelectOptions} + value={value} + onChange={onChange} /> ) } \ No newline at end of file diff --git a/apps/web/src/components/models/trainContent/train-content-tree-select.tsx b/apps/web/src/components/models/trainContent/train-content-tree-select.tsx index 7e0e9d1..ff88a6b 100644 --- a/apps/web/src/components/models/trainContent/train-content-tree-select.tsx +++ b/apps/web/src/components/models/trainContent/train-content-tree-select.tsx @@ -4,7 +4,11 @@ import { useAuth } from "@web/src/providers/auth-provider" import { TreeSelect } from "antd" import { useMemo } from "react" -export default function TrainContentTreeSelect() { +interface Props { + value?: string | null + onChange?: (value: string | null) => void +} +export default function TrainContentTreeSelect({ value, onChange }: Props) { const { user, isAuthenticated } = useAuth() const { data: trainContents, isLoading: trainContentsLoading } : { data: TrainContent[], isLoading: boolean } @@ -48,6 +52,8 @@ export default function TrainContentTreeSelect() { treeData={treeData} placeholder="请选择学科或课程" treeDefaultExpandAll={false} + value={value} + onChange={onChange} //onChange={onChange} /> ) diff --git a/apps/web/src/components/models/trainPlan/TrainPlanLayout.tsx b/apps/web/src/components/models/trainPlan/TrainPlanLayout.tsx index ba0af0d..142e045 100644 --- a/apps/web/src/components/models/trainPlan/TrainPlanLayout.tsx +++ b/apps/web/src/components/models/trainPlan/TrainPlanLayout.tsx @@ -1,9 +1,10 @@ -import TrainPlanCreateForm from "./TrainPlanCreateForm"; + +import TrainPlanWrite from "./TrainPlanWrite"; export default function DailyLayout(){ return (
- +
) } \ No newline at end of file diff --git a/apps/web/src/components/models/trainPlan/TrainPlanWrite.tsx b/apps/web/src/components/models/trainPlan/TrainPlanWrite.tsx new file mode 100644 index 0000000..c3375c6 --- /dev/null +++ b/apps/web/src/components/models/trainPlan/TrainPlanWrite.tsx @@ -0,0 +1,3 @@ +export default function TrainPlanWrite(){ + return
TrainPlanWrite
+} \ No newline at end of file diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 625e052..df11ea0 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -10,7 +10,9 @@ import LoginPage from "../app/login"; import HomePage from "../app/main/home/page"; import StaffMessage from "../app/main/staffpage/page"; import MainLayout from "../app/main/layout/MainLayout"; -import DailyPage from "../app/main/dailyPage/page"; +import DailyPage from "../app/main/daily/page"; +import Dashboard from "../app/main/home/page"; +import WeekPlanPage from "../app/main/plan/weekplan/page"; interface CustomIndexRouteObject extends IndexRouteObject { name?: string; breadcrumb?: string; @@ -46,7 +48,7 @@ export const routes: CustomRouteObject[] = [ children: [ { index: true, - element:, + element:, }, { path: "/staff", @@ -54,8 +56,39 @@ export const routes: CustomRouteObject[] = [ }, { path:"/plan", + children:[ + { + path:"weekplan", + element: + }, + { + path:"monthplan", + element: + } + ] + }, + { + path:"/daily", element: - } + }, + { + path:"/assessment", + children:[ + { + path:"positionassessment", + element: + }, + { + path:"commonassessment", + element: + }, + { + path:"sportsassessment", + element: + } + ] + }, + ], }, diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index 8f5da09..2dd9d9e 100755 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -2,7 +2,7 @@ server { # 监听80端口 listen 80; # 服务器域名/IP地址,使用环境变量 - server_name 192.168.43.206; + server_name 192.168.252.77; # 基础性能优化配置 # 启用tcp_nopush以优化数据发送 @@ -100,7 +100,7 @@ server { # 仅供内部使用 internal; # 代理到认证服务 - proxy_pass http://192.168.43.206:3001/auth/file; + proxy_pass http://192.168.252.77:3000/auth/file; # 请求优化:不传递请求体 proxy_pass_request_body off; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0efc82c..ec71909 100755 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -264,7 +264,7 @@ importers: specifier: ~32.3.2 version: 32.3.3 '@ant-design/icons': - specifier: ^5.4.0 + specifier: ^5.5.2 version: 5.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@dnd-kit/core': specifier: ^6.3.1 @@ -317,6 +317,9 @@ importers: '@trpc/server': specifier: 11.0.0-rc.456 version: 11.0.0-rc.456 + '@types/react-redux': + specifier: ^7.1.34 + version: 7.1.34 '@xyflow/react': specifier: ^12.3.6 version: 12.3.6(@types/react@18.2.38)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -330,7 +333,7 @@ importers: specifier: ~32.3.2 version: 32.3.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) antd: - specifier: ^5.19.3 + specifier: ^5.23.0 version: 5.23.0(date-fns@2.30.0)(luxon@3.5.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) axios: specifier: ^1.7.2 @@ -386,6 +389,9 @@ importers: react-hot-toast: specifier: ^2.4.1 version: 2.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react-redux: + specifier: ^9.2.0 + version: 9.2.0(@types/react@18.2.38)(react@18.2.0) react-resizable: specifier: ^3.0.5 version: 3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -404,6 +410,9 @@ importers: uuid: specifier: ^10.0.0 version: 10.0.0 + xlsx: + specifier: ^0.18.5 + version: 0.18.5 yjs: specifier: ^13.6.20 version: 13.6.21 @@ -420,6 +429,9 @@ importers: '@types/react-dom': specifier: 18.2.15 version: 18.2.15 + '@types/xlsx': + specifier: ^0.0.36 + version: 0.0.36 '@vitejs/plugin-react-swc': specifier: ^3.5.0 version: 3.7.2(@swc/helpers@0.5.15)(vite@5.4.11(@types/node@20.17.12)(less@4.2.2)(terser@5.37.0)) @@ -1919,79 +1931,67 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -2459,61 +2459,51 @@ packages: resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.30.1': resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.30.1': resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.30.1': resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.30.1': resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.30.1': resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.30.1': resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.30.1': resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.30.1': resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.30.1': resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} @@ -2872,28 +2862,24 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [glibc] '@swc/core-linux-arm64-musl@1.10.6': resolution: {integrity: sha512-hB2xZFmXCKf2iJF5y2z01PSuLqEoUP3jIX/XlIHN+/AIP7PkSKsValE63LnjlnWPnSEI0IxUyRE3T3FzWE/fQQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [musl] '@swc/core-linux-x64-gnu@1.10.6': resolution: {integrity: sha512-PRGPp0I22+oJ8RMGg8M4hXYxEffH3ayu0WoSDPOjfol1F51Wj1tfTWN4wVa2RibzJjkBwMOT0KGLGb/hSEDDXQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [glibc] '@swc/core-linux-x64-musl@1.10.6': resolution: {integrity: sha512-SoNBxlA86lnoV9vIz/TCyakLkdRhFSHx6tFMKNH8wAhz1kKYbZfDmpYoIzeQqdTh0tpx8e/Zu1zdK4smovsZqQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [musl] '@swc/core-win32-arm64-msvc@1.10.6': resolution: {integrity: sha512-6L5Y2E+FVvM+BtoA+mJFjf/SjpFr73w2kHBxINxwH8/PkjAjkePDr5m0ibQhPXV61bTwX49+1otzTY85EsUW9Q==} @@ -3068,6 +3054,9 @@ packages: '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/hoist-non-react-statics@3.3.6': + resolution: {integrity: sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==} + '@types/http-errors@2.0.4': resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} @@ -3143,6 +3132,9 @@ packages: '@types/react-dom@18.2.15': resolution: {integrity: sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==} + '@types/react-redux@7.1.34': + resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==} + '@types/react@18.2.38': resolution: {integrity: sha512-cBBXHzuPtQK6wNthuVMV6IjHAFkdl/FOPFIlkd81/Cd1+IqkHu/A+w4g43kaQQoYHik/ruaQBDL72HyCy1vuMw==} @@ -3170,12 +3162,19 @@ packages: '@types/supertest@6.0.2': resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} '@types/ws@8.5.13': resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} + '@types/xlsx@0.0.36': + resolution: {integrity: sha512-mvfrKiKKMErQzLMF8ElYEH21qxWCZtN59pHhWGmWCWFJStYdMWjkDSAy6mGowFxHXaXZWe5/TW7pBUiWclIVOw==} + deprecated: This is a stub types definition for xlsx (https://github.com/sheetjs/js-xlsx). xlsx provides its own type definitions, so you don't need @types/xlsx installed! + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -3515,6 +3514,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + ag-charts-community@10.3.3: resolution: {integrity: sha512-LUlbVS+4sX1UHqZNuE9m3LISr7D4FEO/Y88fwit1fYPTk2l1ZJG/82gFnm5bQ+iG4QgNIwRa2TWm7w2MLnyJlA==} @@ -3879,6 +3882,10 @@ packages: caniuse-lite@1.0.30001690: resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==} + cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + chainsaw@0.1.0: resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} @@ -3989,6 +3996,10 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -4815,6 +4826,10 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -5005,6 +5020,9 @@ packages: hls.js@1.5.18: resolution: {integrity: sha512-znxR+2jecWluu/0KOBqUcvVyAB5tLff10vjMGrpAlz1eFY+ZhF1bY3r82V+Bk7WJdk03iTjtja9KFFz5BrqjSA==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -6664,6 +6682,18 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -6731,6 +6761,9 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -7053,6 +7086,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -7686,10 +7723,18 @@ packages: engines: {node: '>= 8'} hasBin: true + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + wrap-ansi@5.1.0: resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} engines: {node: '>=6'} @@ -7737,6 +7782,11 @@ packages: utf-8-validate: optional: true + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + xml2js@0.6.2: resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} @@ -10598,6 +10648,11 @@ snapshots: dependencies: '@types/node': 20.17.12 + '@types/hoist-non-react-statics@3.3.6': + dependencies: + '@types/react': 18.2.38 + hoist-non-react-statics: 3.3.2 + '@types/http-errors@2.0.4': {} '@types/istanbul-lib-coverage@2.0.6': {} @@ -10667,6 +10722,13 @@ snapshots: dependencies: '@types/react': 18.3.18 + '@types/react-redux@7.1.34': + dependencies: + '@types/hoist-non-react-statics': 3.3.6 + '@types/react': 18.2.38 + hoist-non-react-statics: 3.3.2 + redux: 4.2.1 + '@types/react@18.2.38': dependencies: '@types/prop-types': 15.7.14 @@ -10707,12 +10769,18 @@ snapshots: '@types/methods': 1.1.4 '@types/superagent': 8.1.9 + '@types/use-sync-external-store@0.0.6': {} + '@types/uuid@10.0.0': {} '@types/ws@8.5.13': dependencies: '@types/node': 20.17.12 + '@types/xlsx@0.0.36': + dependencies: + xlsx: 0.18.5 + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.33': @@ -11209,6 +11277,8 @@ snapshots: acorn@8.14.0: {} + adler-32@1.3.1: {} + ag-charts-community@10.3.3: dependencies: ag-charts-locale: 10.3.3 @@ -11684,6 +11754,11 @@ snapshots: caniuse-lite@1.0.30001690: {} + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + chainsaw@0.1.0: dependencies: traverse: 0.3.9 @@ -11785,6 +11860,8 @@ snapshots: co@4.6.0: {} + codepage@1.15.0: {} + collect-v8-coverage@1.0.2: {} color-convert@1.9.3: @@ -12747,6 +12824,8 @@ snapshots: forwarded@0.2.0: {} + frac@1.1.2: {} + fraction.js@4.3.7: {} framer-motion@11.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): @@ -12925,6 +13004,10 @@ snapshots: hls.js@1.5.18: {} + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + hosted-git-info@2.8.9: {} hosted-git-info@4.1.0: @@ -14789,6 +14872,14 @@ snapshots: react-is@18.3.1: {} + react-redux@9.2.0(@types/react@18.2.38)(react@18.2.0): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 18.2.0 + use-sync-external-store: 1.4.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.38 + react-refresh@0.14.2: {} react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): @@ -14869,6 +14960,10 @@ snapshots: dependencies: redis-errors: 1.2.0 + redux@4.2.1: + dependencies: + '@babel/runtime': 7.26.0 + reflect-metadata@0.2.2: {} regenerator-runtime@0.14.1: {} @@ -15256,6 +15351,10 @@ snapshots: sprintf-js@1.0.3: {} + ssf@0.11.2: + dependencies: + frac: 1.1.2 + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -15894,8 +15993,12 @@ snapshots: dependencies: isexe: 2.0.0 + wmf@1.0.2: {} + word-wrap@1.2.5: {} + word@0.3.0: {} + wrap-ansi@5.1.0: dependencies: ansi-styles: 3.2.1 @@ -15931,6 +16034,16 @@ snapshots: ws@8.18.0: {} + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + xml2js@0.6.2: dependencies: sax: 1.4.1