mirror of
https://github.com/PlayEdu/backend
synced 2025-07-20 00:19:44 +08:00
commit
24232ef396
@ -119,8 +119,16 @@ export const LeftMenu: React.FC = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedKeys([location.pathname]);
|
||||
setOpenKeys(openKeyMerge(location.pathname));
|
||||
if (location.pathname.indexOf("/course/user") !== -1) {
|
||||
setSelectedKeys(["/course"]);
|
||||
setOpenKeys(openKeyMerge("/course"));
|
||||
} else if (location.pathname.indexOf("/member/learn") !== -1) {
|
||||
setSelectedKeys(["/member/index"]);
|
||||
setOpenKeys(openKeyMerge("/member/index"));
|
||||
} else {
|
||||
setSelectedKeys([location.pathname]);
|
||||
setOpenKeys(openKeyMerge(location.pathname));
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
|
12
src/compenents/private-route/index.tsx
Normal file
12
src/compenents/private-route/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
import { getToken } from "../../utils/index";
|
||||
import { Navigate } from "react-router-dom";
|
||||
|
||||
interface PropInterface {
|
||||
Component: any;
|
||||
}
|
||||
|
||||
const PrivateRoute: React.FC<PropInterface> = ({ Component }) => {
|
||||
return getToken() ? Component : <Navigate to="/login" replace={true} />;
|
||||
};
|
||||
export default PrivateRoute;
|
@ -30,10 +30,10 @@ export const TreeDepartment = (props: PropInterface) => {
|
||||
setUserTotal(res.data.user_total);
|
||||
if (JSON.stringify(departments) !== "{}") {
|
||||
if (props.showNum) {
|
||||
const new_arr: any = checkNewArr(departments, 0, departCount);
|
||||
const new_arr: any[] = checkNewArr(departments, 0, departCount);
|
||||
setTreeData(new_arr);
|
||||
} else {
|
||||
const new_arr: Option[] = checkArr(departments, 0);
|
||||
const new_arr: any[] = checkArr(departments, 0);
|
||||
setTreeData(new_arr);
|
||||
}
|
||||
} else {
|
||||
@ -87,13 +87,17 @@ export const TreeDepartment = (props: PropInterface) => {
|
||||
for (let i = 0; i < departments[id].length; i++) {
|
||||
if (!departments[departments[id][i].id]) {
|
||||
arr.push({
|
||||
title: departments[id][i].name,
|
||||
title: (
|
||||
<span className="tree-title-elli">{departments[id][i].name}</span>
|
||||
),
|
||||
key: departments[id][i].id,
|
||||
});
|
||||
} else {
|
||||
const new_arr: Option[] = checkArr(departments, departments[id][i].id);
|
||||
const new_arr: any[] = checkArr(departments, departments[id][i].id);
|
||||
arr.push({
|
||||
title: departments[id][i].name,
|
||||
title: (
|
||||
<span className="tree-title-elli">{departments[id][i].name}</span>
|
||||
),
|
||||
key: departments[id][i].id,
|
||||
children: new_arr,
|
||||
});
|
||||
@ -105,16 +109,18 @@ export const TreeDepartment = (props: PropInterface) => {
|
||||
const getNewTitle = (title: any, id: number, counts: any) => {
|
||||
if (counts) {
|
||||
let value = counts[id] || 0;
|
||||
return title + "(" + value + ")";
|
||||
return (
|
||||
<span className="tree-title-elli">{title + "(" + value + ")"}</span>
|
||||
);
|
||||
} else {
|
||||
return title;
|
||||
return <span className="tree-title-elli">{title}</span>;
|
||||
}
|
||||
};
|
||||
|
||||
const onSelect = (selectedKeys: any, info: any) => {
|
||||
let label = "全部" + props.text;
|
||||
if (info) {
|
||||
label = info.node.title;
|
||||
label = info.node.title.props.children;
|
||||
}
|
||||
props.onUpdate(selectedKeys, label);
|
||||
setSelectKey(selectedKeys);
|
||||
@ -123,7 +129,7 @@ export const TreeDepartment = (props: PropInterface) => {
|
||||
const onExpand = (selectedKeys: any, info: any) => {
|
||||
let label = "全部" + props.text;
|
||||
if (info) {
|
||||
label = info.node.title;
|
||||
label = info.node.title.props.children;
|
||||
}
|
||||
props.onUpdate(selectedKeys, label);
|
||||
setSelectKey(selectedKeys);
|
||||
|
@ -23,7 +23,7 @@ code {
|
||||
}
|
||||
|
||||
.w-174px {
|
||||
max-width: 174px;
|
||||
max-width: 134px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@ -457,6 +457,7 @@ textarea.ant-input {
|
||||
margin-bottom: 8px !important;
|
||||
display: flex;
|
||||
align-items: center !important;
|
||||
|
||||
&.ant-tree-treenode-selected {
|
||||
background-color: #fff2f0 !important;
|
||||
border-radius: 6px !important;
|
||||
@ -559,9 +560,11 @@ textarea.ant-input {
|
||||
|
||||
.tree-title-elli {
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
|
||||
.ant-tabs {
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
message,
|
||||
Image,
|
||||
} from "antd";
|
||||
import { course } from "../../api";
|
||||
import { course as Course } from "../../api";
|
||||
import { useParams, useLocation } from "react-router-dom";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import { BackBartment } from "../../compenents";
|
||||
@ -33,7 +33,11 @@ const CourseUserPage = () => {
|
||||
const params = useParams();
|
||||
const result = new URLSearchParams(useLocation().search);
|
||||
const [list, setList] = useState<any>([]);
|
||||
const [users, setUsers] = useState<any>([]);
|
||||
const [course, setCourse] = useState<any>({});
|
||||
const [records, setRecords] = useState<any>({});
|
||||
const [hourCount, setHourCount] = useState<any>({});
|
||||
const [userDepIds, setUserDepIds] = useState<any>({});
|
||||
const [departments, setDepartments] = useState<any>({});
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [size, setSize] = useState(10);
|
||||
@ -55,11 +59,30 @@ const CourseUserPage = () => {
|
||||
preview={false}
|
||||
width={40}
|
||||
height={40}
|
||||
src={users.find((i: any) => i.id === record.user_id).avatar}
|
||||
src={record.avatar}
|
||||
></Image>
|
||||
<span className="ml-8">
|
||||
{users.find((i: any) => i.id === record.user_id).name}
|
||||
</span>
|
||||
<span className="ml-8">{record.name}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "邮箱",
|
||||
render: (_, record: any) => <span>{record.email}</span>,
|
||||
},
|
||||
{
|
||||
title: "部门",
|
||||
render: (_, record: any) => (
|
||||
<div className="float-left">
|
||||
{userDepIds[record.id] &&
|
||||
userDepIds[record.id].map((item: any, index: number) => {
|
||||
return (
|
||||
<span key={index}>
|
||||
{index === userDepIds[record.id].length - 1
|
||||
? departments[item]
|
||||
: departments[item] + "、"}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@ -68,27 +91,72 @@ const CourseUserPage = () => {
|
||||
dataIndex: "progress",
|
||||
render: (_, record: any) => (
|
||||
<span>
|
||||
已完成课时:{record.finished_count} / {record.hour_count}
|
||||
已完成课时:
|
||||
{(records[record.id] && records[record.id].finished_count) ||
|
||||
0} /{" "}
|
||||
{(records[record.id] && records[record.id].hour_count) ||
|
||||
course.class_hour}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "第一次学习时间",
|
||||
dataIndex: "created_at",
|
||||
render: (text: string) => <span>{dateFormat(text)}</span>,
|
||||
render: (_, record: any) => (
|
||||
<>
|
||||
{records[record.id] ? (
|
||||
<span>{dateFormat(records[record.id].created_at)}</span>
|
||||
) : hourCount[record.id] ? (
|
||||
<span>{dateFormat(hourCount[record.id])}</span>
|
||||
) : (
|
||||
<span>-</span>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "学习完成时间",
|
||||
dataIndex: "finished_at",
|
||||
render: (text: string) => <span>{dateFormat(text)}</span>,
|
||||
render: (_, record: any) => (
|
||||
<>
|
||||
{records[record.id] ? (
|
||||
<span>{dateFormat(records[record.id].finished_at)}</span>
|
||||
) : (
|
||||
<span>-</span>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "学习进度",
|
||||
dataIndex: "progress",
|
||||
render: (progress: number) => (
|
||||
<span className={progress >= 10000 ? "c-green" : "c-red"}>
|
||||
{progress / 100}%
|
||||
</span>
|
||||
render: (_, record: any) => (
|
||||
<>
|
||||
{records[record.id] ? (
|
||||
<span
|
||||
className={
|
||||
Math.floor(
|
||||
(records[record.id].finished_count /
|
||||
records[record.id].hour_count) *
|
||||
100
|
||||
) >= 100
|
||||
? "c-green"
|
||||
: "c-red"
|
||||
}
|
||||
>
|
||||
{Math.floor(
|
||||
(records[record.id].finished_count /
|
||||
records[record.id].hour_count) *
|
||||
100
|
||||
)}
|
||||
%
|
||||
</span>
|
||||
) : hourCount[record.id] ? (
|
||||
<span className="c-red">1%</span>
|
||||
) : (
|
||||
<span className="c-red">0%</span>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
@ -99,21 +167,24 @@ const CourseUserPage = () => {
|
||||
|
||||
const getList = () => {
|
||||
setLoading(true);
|
||||
course
|
||||
.courseUser(
|
||||
Number(params.courseId),
|
||||
page,
|
||||
size,
|
||||
"",
|
||||
"",
|
||||
name,
|
||||
email,
|
||||
idCard
|
||||
)
|
||||
Course.courseUser(
|
||||
Number(params.courseId),
|
||||
page,
|
||||
size,
|
||||
"",
|
||||
"",
|
||||
name,
|
||||
email,
|
||||
idCard
|
||||
)
|
||||
.then((res: any) => {
|
||||
setTotal(res.data.total);
|
||||
setList(res.data.data);
|
||||
setUsers(res.data.users);
|
||||
setHourCount(res.data.user_course_hour_user_first_at);
|
||||
setRecords(res.data.user_course_records);
|
||||
setCourse(res.data.course);
|
||||
setDepartments(res.data.departments);
|
||||
setUserDepIds(res.data.user_dep_ids);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
@ -160,12 +231,12 @@ const CourseUserPage = () => {
|
||||
okText: "确认",
|
||||
cancelText: "取消",
|
||||
onOk() {
|
||||
course
|
||||
.destroyCourseUser(Number(params.courseId), selectedRowKeys)
|
||||
.then(() => {
|
||||
Course.destroyCourseUser(Number(params.courseId), selectedRowKeys).then(
|
||||
() => {
|
||||
message.success("清除成功");
|
||||
resetList();
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
onCancel() {
|
||||
console.log("Cancel");
|
||||
|
@ -68,7 +68,7 @@ const DepartmentPage = () => {
|
||||
arr.push({
|
||||
title: (
|
||||
<>
|
||||
<div className="w-174px mr-24">{departments[id][i].name}</div>
|
||||
<div className="tree-title-elli">{departments[id][i].name}</div>
|
||||
<div className="d-flex">
|
||||
<Tooltip placement="top" title="可拖拽排序">
|
||||
<i
|
||||
@ -103,7 +103,7 @@ const DepartmentPage = () => {
|
||||
arr.push({
|
||||
title: (
|
||||
<>
|
||||
<div className="w-174px mr-24">{departments[id][i].name}</div>
|
||||
<div className="tree-title-elli">{departments[id][i].name}</div>
|
||||
<div className="d-flex">
|
||||
<Tooltip placement="top" title="可拖拽排序">
|
||||
<i
|
||||
|
@ -31,6 +31,7 @@ const MemberLearnPage = () => {
|
||||
const [currentCourses, setCurrentCourses] = useState<any>([]);
|
||||
const [openCourses, setOpenCourses] = useState<any>([]);
|
||||
const [records, setRecords] = useState<any>({});
|
||||
const [hourCount, setHourCount] = useState<any>({});
|
||||
const [total2, setTotal2] = useState(0);
|
||||
const [refresh2, setRefresh2] = useState(false);
|
||||
const [uid, setUid] = useState(Number(result.get("id")));
|
||||
@ -153,6 +154,7 @@ const MemberLearnPage = () => {
|
||||
setList2(res.data.departments);
|
||||
setCourses(res.data.dep_courses);
|
||||
setOpenCourses(res.data.open_courses);
|
||||
setHourCount(res.data.user_course_hour_count);
|
||||
setRecords(res.data.user_course_records);
|
||||
if (res.data.departments.length > 0) {
|
||||
let box: any = [];
|
||||
@ -252,6 +254,8 @@ const MemberLearnPage = () => {
|
||||
)}
|
||||
%
|
||||
</span>
|
||||
) : hourCount[record.id] && hourCount[record.id] > 0 ? (
|
||||
<span className="c-red">1%</span>
|
||||
) : (
|
||||
<span className="c-red">0%</span>
|
||||
)}
|
||||
|
@ -66,7 +66,7 @@ const ResourceCategoryPage = () => {
|
||||
arr.push({
|
||||
title: (
|
||||
<>
|
||||
<div className="w-174px mr-24">{categories[id][i].name}</div>
|
||||
<div className="tree-title-elli">{categories[id][i].name}</div>
|
||||
<div className="d-flex">
|
||||
<Tooltip placement="top" title="可拖拽排序">
|
||||
<i
|
||||
@ -101,7 +101,7 @@ const ResourceCategoryPage = () => {
|
||||
arr.push({
|
||||
title: (
|
||||
<>
|
||||
<div className="w-174px mr-24">{categories[id][i].name}</div>
|
||||
<div className="tree-title-elli">{categories[id][i].name}</div>
|
||||
<div className="d-flex">
|
||||
<Tooltip placement="top" title="可拖拽排序">
|
||||
<i
|
||||
|
@ -25,6 +25,7 @@ import SystemAdminrolesPage from "../pages/system/adminroles";
|
||||
import DepartmentPage from "../pages/department";
|
||||
import TestPage from "../pages/test";
|
||||
import ErrorPage from "../pages/error";
|
||||
import PrivateRoute from "../compenents/private-route";
|
||||
|
||||
// const LoginPage = lazy(() => import("../pages/login"));
|
||||
|
||||
@ -63,70 +64,75 @@ const routes: RouteObject[] = [
|
||||
children: [
|
||||
{
|
||||
path: "/",
|
||||
element: <HomePage />,
|
||||
element: <PrivateRoute Component={<HomePage />} />,
|
||||
children: [
|
||||
{
|
||||
path: "/",
|
||||
element: <DashboardPage />,
|
||||
element: <PrivateRoute Component={<DashboardPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/change-password",
|
||||
element: <ChangePasswordPage />,
|
||||
element: <PrivateRoute Component={<ChangePasswordPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/resource-category",
|
||||
element: <ResourceCategoryPage />,
|
||||
element: <PrivateRoute Component={<ResourceCategoryPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/images",
|
||||
element: <ResourceImagesPage />,
|
||||
element: <PrivateRoute Component={<ResourceImagesPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/videos",
|
||||
element: <ResourceVideosPage />,
|
||||
element: <PrivateRoute Component={<ResourceVideosPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/course",
|
||||
element: <CoursePage />,
|
||||
element: <PrivateRoute Component={<CoursePage />} />,
|
||||
},
|
||||
{
|
||||
path: "/course/user/:courseId",
|
||||
element: <CourseUserPage />,
|
||||
element: <PrivateRoute Component={<CourseUserPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/member",
|
||||
element: <KeepAlive />,
|
||||
children: [
|
||||
{ path: "/member/index", element: <MemberPage /> },
|
||||
{
|
||||
path: "/member/index",
|
||||
element: <PrivateRoute Component={<MemberPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/member/import",
|
||||
element: <MemberImportPage />,
|
||||
element: <PrivateRoute Component={<MemberImportPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/member/learn",
|
||||
element: <MemberLearnPage />,
|
||||
element: <PrivateRoute Component={<MemberLearnPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/member/departmentUser",
|
||||
element: <MemberDepartmentProgressPage />,
|
||||
element: (
|
||||
<PrivateRoute Component={<MemberDepartmentProgressPage />} />
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/system/config/index",
|
||||
element: <SystemConfigPage />,
|
||||
element: <PrivateRoute Component={<SystemConfigPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/system/administrator",
|
||||
element: <SystemAdministratorPage />,
|
||||
element: <PrivateRoute Component={<SystemAdministratorPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/system/adminroles",
|
||||
element: <SystemAdminrolesPage />,
|
||||
element: <PrivateRoute Component={<SystemAdminrolesPage />} />,
|
||||
},
|
||||
{
|
||||
path: "/department",
|
||||
element: <DepartmentPage />,
|
||||
element: <PrivateRoute Component={<DepartmentPage />} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user