lin
This commit is contained in:
parent
e98f1dcfa0
commit
127ed07383
|
@ -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",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from "react"
|
||||
|
||||
export default function HomePage() {
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<div >
|
||||
首页
|
||||
数据看板(待开发)
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -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 (
|
||||
<Layout>
|
||||
{/* 顶部Header */}
|
||||
<Header className="flex justify-end items-center bg-white shadow-sm h-16 px-6">
|
||||
<Avatar className='bg-black'></Avatar>
|
||||
</Header>
|
||||
|
||||
{/* 主体布局 */}
|
||||
<Layout className="h-screen">
|
||||
{/* 左侧导航栏 */}
|
||||
<Sider
|
||||
width={280}
|
||||
className=" flex flex-col"
|
||||
>
|
||||
{/* 用户头像区域 */}
|
||||
<div className="p-6 border-b">
|
||||
<div className="relative group flex justify-center">
|
||||
<Avatar
|
||||
size={120}
|
||||
icon={<UserOutlined />}
|
||||
className=" transition-all duration-300"
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<SettingOutlined />}
|
||||
className="!absolute bottom-0 right-6 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
onClick={() => navigate('/profile')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<NavigationMenu></NavigationMenu>
|
||||
<Sider theme="light" width={240} className="shadow-sm">
|
||||
<NavigationMenu />
|
||||
</Sider>
|
||||
{/* 新增可滚动内容区域 */}
|
||||
<Layout className="flex-1">
|
||||
<Content className="overflow-auto">
|
||||
|
||||
{/* 内容区域 */}
|
||||
<Content className="overflow-auto p-6 bg-gray-50">
|
||||
<Outlet />
|
||||
</Content>
|
||||
</Layout>
|
||||
|
|
|
@ -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(
|
||||
"首页概览",
|
||||
"/",
|
||||
<i className={`iconfont icon-icon-home`} />,
|
||||
null,
|
||||
null,
|
||||
),
|
||||
getItem(
|
||||
"人员总览",
|
||||
"/staff",
|
||||
<i className="iconfont icon-icon-category" />,
|
||||
null,
|
||||
null,
|
||||
),
|
||||
getItem(
|
||||
"训练计划",
|
||||
"/plan",
|
||||
<i className="iconfont icon-icon-user" />,
|
||||
[
|
||||
getItem("周训练计划", "/plan/weekplan", null, null, null),
|
||||
getItem("月训练计划", "/plan/monthplan", null, null, null),
|
||||
],
|
||||
null,
|
||||
),
|
||||
getItem(
|
||||
"每日填报",
|
||||
"/daily",
|
||||
<i className="iconfont icon-icon-user" />,
|
||||
null,
|
||||
null,
|
||||
),
|
||||
getItem(
|
||||
"考核成绩",
|
||||
"/assessment",
|
||||
<i className="iconfont icon-icon-user" />,
|
||||
[
|
||||
getItem("岗位", "/assessment/positionassessment", null, null, null),
|
||||
getItem("共同", "/assessment/commonassessment", null, null, null),
|
||||
getItem("体育", "/assessment/sportsassessment", null, null, null),
|
||||
],
|
||||
null,
|
||||
)
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu
|
||||
theme="dark"
|
||||
mode="inline"
|
||||
className="!bg-transparent !border-0 pt-4 [&_.ant-menu-item]:!mt-2"
|
||||
selectedKeys={[ // 改用selectedKeys替代defaultSelectedKeys
|
||||
menuItems.find((item) => location.pathname.startsWith(item.path))?.key,
|
||||
]}
|
||||
>
|
||||
{menuItems.map((item) => (
|
||||
<Menu.Item
|
||||
key={item.key}
|
||||
className="!h-14 !flex !items-center !text-gray-300 hover:!text-white group !rounded-lg mx-4"
|
||||
onClick={() => navigate(item.path)}
|
||||
>
|
||||
<div className="flex items-center justify-center w-full px-4 transition-all duration-300 h-full rounded-lg">
|
||||
<span className="text-xl font-medium">{item.label}</span>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
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<string[]>([
|
||||
location.pathname,
|
||||
]);
|
||||
// 展开菜单
|
||||
const [openKeys, setOpenKeys] = useState<string[]>(hit(location.pathname));
|
||||
// const permissions = useSelector(
|
||||
// (state: any) => state.loginUser.value.permissions
|
||||
// );
|
||||
const [activeMenus, setActiveMenus] = useState<any>(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 (
|
||||
<div className='w-[200px] h-full bg-#fff'>
|
||||
<div
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
cursor: "pointer",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 10,
|
||||
background: "#fff",
|
||||
}}
|
||||
onClick={() => {
|
||||
window.location.href = "/";
|
||||
}}
|
||||
>
|
||||
{/* 此处为版权标识,严禁删改
|
||||
<img src={logo} className="w-[124px] h-[40px]"/> */}
|
||||
</div>
|
||||
<div className='w-[200px] h-[calc(100%-74px)] overflow-y-auto overflow-x-hidden'>
|
||||
<Menu
|
||||
onClick={onClick}
|
||||
style={{
|
||||
width: 200,
|
||||
background: "#ffffff",
|
||||
}}
|
||||
selectedKeys={selectedKeys}
|
||||
openKeys={openKeys}
|
||||
mode="inline"
|
||||
items={activeMenus}
|
||||
onSelect={(data: any) => {
|
||||
setSelectedKeys(data.selectedKeys);
|
||||
}}
|
||||
onOpenChange={(keys: any) => {
|
||||
setOpenKeys(keys);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
// <>
|
||||
// <Menu
|
||||
// theme="dark"
|
||||
// mode="inline"
|
||||
// className="!bg-transparent !border-0 pt-4 [&_.ant-menu-item]:!mt-2"
|
||||
// selectedKeys={[ // 改用selectedKeys替代defaultSelectedKeys
|
||||
// menuItems.find((item) => location.pathname.startsWith(item.path))?.key,
|
||||
// ]}
|
||||
// >
|
||||
// {menuItems.map((item) => (
|
||||
// <Menu.Item
|
||||
// key={item.key}
|
||||
// className="!h-14 !flex !items-center !text-gray-300 hover:!text-white group !rounded-lg mx-4"
|
||||
// onClick={() => navigate(item.path)}
|
||||
// >
|
||||
// <div className="flex items-center justify-center w-full px-4 transition-all duration-300 h-full rounded-lg">
|
||||
// <span className="text-xl font-medium">{item.label}</span>
|
||||
// </div>
|
||||
// </Menu.Item>
|
||||
// ))}
|
||||
// </Menu>
|
||||
// </>
|
||||
// );
|
||||
// }
|
|
@ -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<TableData[]>([])
|
||||
const [columns, setColumns] = useState<ColumnsType<TableData>>([])
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [trainingStatus, setTrainingStatus] = useState<Record<string, string>>({})
|
||||
const pageSize = 1 // 每页显示一个第一列的唯一值
|
||||
|
||||
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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<TableData> = 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) => (
|
||||
<Select
|
||||
value={trainingStatus[record.key] || undefined}
|
||||
onChange={(value) => {
|
||||
setTrainingStatus(prev => ({
|
||||
...prev,
|
||||
[record.key]: value
|
||||
}))
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
options={[
|
||||
{ value: '参训', label: '参训' },
|
||||
{ value: '不参训', label: '不参训' }
|
||||
]}
|
||||
placeholder="请选择"
|
||||
/>
|
||||
)
|
||||
})
|
||||
setColumns(tableColumns)
|
||||
setData(tableData)
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
}
|
||||
|
||||
// 计算行合并
|
||||
const calculateRowSpan = (data: TableData[], rowIndex: number, column: string) => {
|
||||
if (rowIndex === 0) {
|
||||
let count = 1
|
||||
for (let i = 1; i < data.length; i++) {
|
||||
if (data[i][column] === data[rowIndex][column]) {
|
||||
count++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
if (rowIndex > 0 && data[rowIndex][column] === data[rowIndex - 1][column]) {
|
||||
return 0
|
||||
}
|
||||
let count = 1
|
||||
for (let i = rowIndex + 1; i < data.length; i++) {
|
||||
if (data[i][column] === data[rowIndex][column]) {
|
||||
count++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// 修改分页数据获取逻辑
|
||||
const getPageData = () => {
|
||||
const firstColumn = columns[0] as ColumnType<TableData>
|
||||
const firstColumnName = firstColumn?.dataIndex as string
|
||||
if (!firstColumnName) return []
|
||||
|
||||
// 获取第一列的所有唯一值
|
||||
const uniqueValues = Array.from(
|
||||
new Set(data.map(item => item[firstColumnName]))
|
||||
).filter(Boolean)
|
||||
|
||||
// 获取当前页应该显示的值
|
||||
const currentValue = uniqueValues[(currentPage - 1)]
|
||||
|
||||
// 返回第一列等于当前值的所有行
|
||||
return data.filter(item => item[firstColumnName] === currentValue)
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
const handleSave = () => {
|
||||
const saveData = data.map(item => ({
|
||||
...item,
|
||||
isTraining: trainingStatus[item.key] || ''
|
||||
}))
|
||||
console.log('要保存的数据:', saveData)
|
||||
// 这里添加保存到后端的逻辑
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="w-full mb-6 p-8 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50 hover:bg-gray-100 transition-colors cursor-pointer relative">
|
||||
<input
|
||||
type="file"
|
||||
accept=".xlsx,.xls"
|
||||
onChange={handleFileUpload}
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
/>
|
||||
<div className="flex flex-col items-center justify-center text-gray-600">
|
||||
<UploadOutlined className="text-3xl mb-2" />
|
||||
<p className="text-lg font-medium">点击或拖拽文件到此处上传</p>
|
||||
<p className="text-sm text-gray-500">支持 .xlsx, .xls 格式的Excel文件</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{data.length > 0 && (
|
||||
<>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={getPageData()}
|
||||
pagination={false}
|
||||
bordered
|
||||
style={{
|
||||
border: '1px solid #f0f0f0'
|
||||
}}
|
||||
className="!border-collapse [&_th]:!border [&_td]:!border [&_th]:!border-solid [&_td]:!border-solid [&_th]:!border-[#f0f0f0] [&_td]:!border-[#f0f0f0]"
|
||||
/>
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
<Pagination
|
||||
className="mt-4"
|
||||
current={currentPage}
|
||||
total={Array.from(new Set(data.map(item =>
|
||||
item[(columns[0] as ColumnType<TableData>)?.dataIndex as string]
|
||||
))).filter(Boolean).length}
|
||||
pageSize={1}
|
||||
onChange={(page) => {
|
||||
setCurrentPage(page)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
// 添加防抖的搜索处理函数
|
||||
|
|
|
@ -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]);
|
||||
|
||||
const { form, formValue, setVisible, visible, editingRecord } = useMainContext()
|
||||
});
|
||||
// useEffect(() => {
|
||||
// traincontents?.forEach((situation)=>{
|
||||
// console.log(situation.id);
|
||||
// });
|
||||
// }, [traincontents]);
|
||||
|
||||
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,
|
||||
},
|
||||
data: {
|
||||
const staffData = {
|
||||
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,
|
||||
department: values.deptId ? {
|
||||
connect: { id: values.deptId }
|
||||
} : undefined,
|
||||
position: values.positionId ? {
|
||||
connect: { id: values.positionId }
|
||||
} : undefined,
|
||||
absent: values.absent,
|
||||
trainSituations: {
|
||||
trainSituations: values.trainSituations?.length > 0 ? {
|
||||
create: values.trainSituations.map((situation) => ({
|
||||
trainContent: { connect: { id: situation.trainContentId } },
|
||||
mustTrainTime: situation.mustTrainTime,
|
||||
// 其他必填字段...
|
||||
trainContent: {
|
||||
connect: { id: situation.trainContentId }
|
||||
},
|
||||
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 (
|
||||
<>
|
||||
{/* 模态框样式更新 */}
|
||||
<Modal
|
||||
title="编辑员工信息"
|
||||
visible={visible}
|
||||
|
@ -104,21 +113,18 @@ export default function StaffModal() {
|
|||
<Form.Item
|
||||
name={"username"}
|
||||
label="姓名"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<Input className="rounded-lg" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={"deptId"}
|
||||
label="部门"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<DepartmentSelect></DepartmentSelect>
|
||||
<DepartmentChildrenSelect></DepartmentChildrenSelect>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={"positionId"}
|
||||
name={["position", "type"]}
|
||||
label="职务"
|
||||
// labelClassName="text-gray-300"
|
||||
>
|
||||
<Input className="rounded-lg" />
|
||||
</Form.Item>
|
||||
|
@ -139,11 +145,11 @@ export default function StaffModal() {
|
|||
<div key={key} className="flex space-x-2">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'trainContent']}
|
||||
name={[name, 'trainContentId']} // 从 trainContent 改为 trainContentId
|
||||
label="培训内容"
|
||||
className="flex-1"
|
||||
>
|
||||
<TrainContentTreeSelect></TrainContentTreeSelect>
|
||||
<TrainContentTreeSelect/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
|
@ -162,7 +168,7 @@ export default function StaffModal() {
|
|||
</Form.List>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Button onClick={() =>{console.log(traincontents);}}>TEST</Button>
|
||||
{/* <Button onClick={() =>{console.log(traincontents);}}>TEST</Button> */}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -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) => (
|
||||
<Select
|
||||
// defaultValue={record.absent ? "是" : "否"}
|
||||
style={{ width: 120 }}
|
||||
onChange={async (value) => {
|
||||
try {
|
||||
await update.mutateAsync({
|
||||
where: { id: record.id },
|
||||
data: { absent: value }
|
||||
});
|
||||
toast.success("状态更新成功");
|
||||
} catch (error) {
|
||||
toast.error("状态更新失败");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Select.Option value="是">是</Select.Option>
|
||||
<Select.Option value="否">否</Select.Option>
|
||||
</Select>
|
||||
)
|
||||
render: (absent) => absent === null ? "未知" : absent ? "否" : "是"
|
||||
},
|
||||
{
|
||||
title: "应时",
|
||||
dataIndex: "trainSituation",
|
||||
key: "trainSituation",
|
||||
title: "培训情况",
|
||||
dataIndex: "trainSituations",
|
||||
key: "trainSituations",
|
||||
render: (situations) => {
|
||||
if (!situations?.length) return "无培训记录";
|
||||
|
||||
return (
|
||||
<div>
|
||||
{situations.slice(0, 2).map((s) => (
|
||||
<div key={s.id} className="mb-1">
|
||||
<span>{s.trainContent?.title}: </span>
|
||||
<span>已完成 {s.alreadyTrainTime}/{s.mustTrainTime} 小时</span>
|
||||
</div>
|
||||
))}
|
||||
{situations.length > 2 && (
|
||||
<span
|
||||
className="text-blue-500 cursor-pointer hover:underline"
|
||||
onClick={() => showTrainingDetails(situations)}
|
||||
>
|
||||
还有 {situations.length - 2} 条记录
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
render: (_, record) => (
|
||||
<div className="flex space-x-2">
|
||||
<Button
|
||||
type="primary"
|
||||
key={record.id}
|
||||
onClick={() => handleEdit(record)}>编辑</Button>
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
onClick={async () => {
|
||||
if (confirm("确定要删除该员工吗?")) {
|
||||
try {
|
||||
await softDeleteByIds.mutateAsync({
|
||||
ids: [record.id],
|
||||
});
|
||||
toast.success("删除成功");
|
||||
} catch (error) {
|
||||
// 添加更详细的错误日志
|
||||
console.error('删除员工时出错:', error);
|
||||
toast.error("删除失败");
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
];
|
||||
|
@ -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() {
|
|||
(
|
||||
<Table
|
||||
key={"username"}
|
||||
columns={colnums}
|
||||
columns={columns}
|
||||
dataSource={staffs}
|
||||
className="table-auto w-full border-collapse border border-gray-300"
|
||||
tableLayout="fixed"
|
||||
pagination={{
|
||||
position: ["bottomCenter"],
|
||||
className: "flex justify-center mt-4",
|
||||
pageSize: 12,
|
||||
pageSize: 10,
|
||||
|
||||
}}
|
||||
onRow={(record) => ({
|
||||
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");
|
||||
}
|
||||
},
|
||||
})}
|
||||
>
|
||||
<thead className="bg-gray-200">
|
||||
<thead >
|
||||
<tr>
|
||||
{colnums.map((column) => (
|
||||
{columns.map((column) => (
|
||||
<th
|
||||
key={column.key}
|
||||
className="px-4 py-2 border border-gray-300 text-center font-bold"
|
||||
>
|
||||
{column.title}
|
||||
</th>
|
||||
|
@ -135,14 +183,14 @@ export default function StaffTable() {
|
|||
{staffs?.map((record) => (
|
||||
<tr
|
||||
key={record.id}
|
||||
className="border border-gray-300"
|
||||
>
|
||||
{colnums.map((column) => (
|
||||
<td
|
||||
key={column.key}
|
||||
className="px-4 py-2 border border-gray-300 text-center"
|
||||
>
|
||||
{column.render?.(record[column.dataIndex], record) || record[column.dataIndex]}
|
||||
{columns.map((column) => (
|
||||
// 使用提取的函数处理嵌套字段
|
||||
<td key={column.key}>
|
||||
{column.render?.(
|
||||
getNestedValue(record, column.dataIndex),
|
||||
record
|
||||
) || getNestedValue(record, column.dataIndex)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
|
@ -151,8 +199,28 @@ export default function StaffTable() {
|
|||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
<Modal
|
||||
title="培训详情"
|
||||
open={isTrainingModalVisible}
|
||||
onCancel={() => setIsTrainingModalVisible(false)}
|
||||
footer={null}
|
||||
width={600}
|
||||
>
|
||||
<div className="max-h-96 overflow-y-auto">
|
||||
{selectedTrainings.map((training) => (
|
||||
<div key={training.id} className="mb-4 p-4 border rounded">
|
||||
<h3 className="font-bold mb-2">{training.trainContent?.title}</h3>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>必修时长: {training.mustTrainTime} 小时</div>
|
||||
<div>已完成时长: {training.alreadyTrainTime} 小时</div>
|
||||
<div>完成率: {((training.alreadyTrainTime / training.mustTrainTime) * 100).toFixed(1)}%</div>
|
||||
{/* <div>得分: {training.score}</div> */}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -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}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import TrainPlanCreateForm from "./TrainPlanCreateForm";
|
||||
|
||||
import TrainPlanWrite from "./TrainPlanWrite";
|
||||
|
||||
export default function DailyLayout(){
|
||||
return (
|
||||
<div className="w-full h-[calc(100vh-100px)]">
|
||||
<TrainPlanCreateForm></TrainPlanCreateForm>
|
||||
<TrainPlanWrite></TrainPlanWrite>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default function TrainPlanWrite(){
|
||||
return <div>TrainPlanWrite</div>
|
||||
}
|
|
@ -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:<StaffMessage></StaffMessage>,
|
||||
element:<Dashboard></Dashboard>,
|
||||
},
|
||||
{
|
||||
path: "/staff",
|
||||
|
@ -54,8 +56,39 @@ export const routes: CustomRouteObject[] = [
|
|||
},
|
||||
{
|
||||
path:"/plan",
|
||||
children:[
|
||||
{
|
||||
path:"weekplan",
|
||||
element:<WeekPlanPage></WeekPlanPage>
|
||||
},
|
||||
{
|
||||
path:"monthplan",
|
||||
element:<DailyPage></DailyPage>
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path:"/daily",
|
||||
element:<DailyPage></DailyPage>
|
||||
},
|
||||
{
|
||||
path:"/assessment",
|
||||
children:[
|
||||
{
|
||||
path:"positionassessment",
|
||||
element:<DailyPage></DailyPage>
|
||||
},
|
||||
{
|
||||
path:"commonassessment",
|
||||
element:<DailyPage></DailyPage>
|
||||
},
|
||||
{
|
||||
path:"sportsassessment",
|
||||
element:<DailyPage></DailyPage>
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
169
pnpm-lock.yaml
169
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
|
||||
|
|
Loading…
Reference in New Issue