学员学习

This commit is contained in:
禺狨 2023-04-19 11:17:19 +08:00
parent 46e352fac3
commit f5ad3a4eb2
5 changed files with 387 additions and 2 deletions

View File

@ -78,3 +78,33 @@ export function storeBatch(startLine: number, users: string[][]) {
users: users, users: users,
}); });
} }
export function learnStats(id: number) {
return client.get(`/backend/v1/user/${id}/learn-stats`, {});
}
export function learnHours(
id: number,
page: number,
size: number,
params: object
) {
return client.get(`/backend/v1/user/${id}/learn-hours`, {
page,
size,
...params,
});
}
export function learnCourses(
id: number,
page: number,
size: number,
params: object
) {
return client.get(`/backend/v1/user/${id}/learn-courses`, {
page,
size,
...params,
});
}

View File

@ -14,7 +14,7 @@ import type { ColumnsType } from "antd/es/table";
import { PlusOutlined, ExclamationCircleFilled } from "@ant-design/icons"; import { PlusOutlined, ExclamationCircleFilled } from "@ant-design/icons";
import { user } from "../../api/index"; import { user } from "../../api/index";
import { dateFormat } from "../../utils/index"; import { dateFormat } from "../../utils/index";
import { Link } from "react-router-dom"; import { Link, Navigate } from "react-router-dom";
import { TreeDepartment, PerButton } from "../../compenents"; import { TreeDepartment, PerButton } from "../../compenents";
import { MemberCreate } from "./compenents/create"; import { MemberCreate } from "./compenents/create";
import { MemberUpdate } from "./compenents/update"; import { MemberUpdate } from "./compenents/update";
@ -99,6 +99,21 @@ const MemberPage = () => {
width: 160, width: 160,
render: (_, record: any) => ( render: (_, record: any) => (
<Space size="small"> <Space size="small">
<Link
style={{ textDecoration: "none" }}
to={`/member/learn?id=${record.id}`}
>
<PerButton
type="link"
text="学习"
class="b-link c-red"
icon={null}
p="user-learn"
onClick={() => null}
disabled={null}
/>
</Link>
<div className="form-column"></div>
<PerButton <PerButton
type="link" type="link"
text="编辑" text="编辑"

View File

@ -0,0 +1,15 @@
.large-title {
width: 100%;
height: 28px;
font-size: 18px;
font-weight: 600;
color: rgba(0, 0, 0, 0.88);
line-height: 28px;
}
.charts {
width: 100%;
height: 320px;
box-sizing: border-box;
padding-top: 24px;
}

320
src/pages/member/learn.tsx Normal file
View File

@ -0,0 +1,320 @@
import { useState, useEffect, useRef } from "react";
import styles from "./learn.module.less";
import { Row, Col, Image, message, Table } from "antd";
import { useNavigate, useLocation } from "react-router-dom";
import { BackBartment, DurationText } from "../../compenents";
import { dateFormat } from "../../utils/index";
import { user as member } from "../../api/index";
import * as echarts from "echarts";
import type { ColumnsType } from "antd/es/table";
interface DataType {
id: React.Key;
title: string;
type: string;
created_at: string;
total_duration: number;
finished_duration: number;
is_finished: boolean;
}
const MemberLearnPage = () => {
let chartRef = useRef(null);
const result = new URLSearchParams(useLocation().search);
const [loading, setLoading] = useState<boolean>(false);
const [page, setPage] = useState(1);
const [size, setSize] = useState(10);
const [list, setList] = useState<any>([]);
const [hours, setHours] = useState<any>({});
const [total, setTotal] = useState(0);
const [refresh, setRefresh] = useState(false);
const [loading2, setLoading2] = useState<boolean>(false);
const [page2, setPage2] = useState(1);
const [size2, setSize2] = useState(10);
const [list2, setList2] = useState<any>([]);
const [courses, setCourses] = useState<any>({});
const [total2, setTotal2] = useState(0);
const [refresh2, setRefresh2] = useState(false);
const [uid, setUid] = useState(Number(result.get("id")));
useEffect(() => {
getZxtData();
return () => {
window.onresize = null;
};
}, [uid]);
useEffect(() => {
getLearnHours();
}, [refresh, page, size]);
useEffect(() => {
getLearnCourses();
}, [refresh2, page2, size2]);
const getZxtData = () => {
member.learnStats(uid).then((res: any) => {
renderView(res.data);
});
};
const renderView = (params: any) => {
const timeData: any = [];
const valueData: any = [];
params.map((item: any) => {
timeData.push(item.key);
valueData.push(item.value / 1000);
});
let dom: any = chartRef.current;
let myChart = echarts.init(dom);
myChart.setOption({
tooltip: {
trigger: "axis",
},
legend: {
data: ["每日学习时长"],
x: "right",
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: timeData,
},
yAxis: {
type: "value",
},
series: [
{
name: "每日学习时长(秒)",
type: "line",
data: valueData,
},
],
});
window.onresize = () => {
myChart.resize();
};
};
const getLearnHours = () => {
if (loading) {
return;
}
setLoading(true);
member
.learnHours(uid, page, size, {
sort_field: "",
sort_algo: "",
is_finished: "",
})
.then((res: any) => {
setList(res.data.data);
setHours(res.data.hours);
setTotal(res.data.total);
setLoading(false);
});
};
const getLearnCourses = () => {
if (loading2) {
return;
}
setLoading2(true);
member
.learnCourses(uid, page2, size2, {
sort_field: "",
sort_algo: "",
is_finished: "",
})
.then((res: any) => {
setList2(res.data.data);
setCourses(res.data.courses);
setTotal2(res.data.total);
setLoading2(false);
});
};
const paginationProps = {
current: page, //当前页码
pageSize: size,
total: total, // 总条数
onChange: (page: number, pageSize: number) =>
handlePageChange(page, pageSize), //改变页码的函数
showSizeChanger: true,
};
const handlePageChange = (page: number, pageSize: number) => {
setPage(page);
setSize(pageSize);
};
const paginationProps2 = {
current: page2, //当前页码
pageSize: size2,
total: total2, // 总条数
onChange: (page: number, pageSize: number) =>
handlePageChange2(page, pageSize), //改变页码的函数
showSizeChanger: true,
};
const handlePageChange2 = (page: number, pageSize: number) => {
setPage2(page);
setSize2(pageSize);
};
const columns: ColumnsType<DataType> = [
{
title: "课时标题",
dataIndex: "title",
render: (_, record: any) => (
<>
<span>{hours[record.hour_id].title}</span>
</>
),
},
{
title: "课时类型",
dataIndex: "type",
render: (_, record: any) => (
<>
<span>{hours[record.hour_id].type}</span>
</>
),
},
{
title: "总时长",
dataIndex: "total_duration",
render: (_, record: any) => (
<>
<DurationText duration={record.total_duration}></DurationText>
</>
),
},
{
title: "已学习时长",
dataIndex: "finished_duration",
render: (_, record: any) => (
<>
<DurationText duration={record.finished_duration || 0}></DurationText>
</>
),
},
{
title: "状态",
dataIndex: "is_finished",
render: (_, record: any) => (
<>
{record.is_finished === 1 ? <span></span> : <span></span>}
</>
),
},
{
title: "时间",
dataIndex: "created_at",
render: (text: string) => <span>{dateFormat(text)}</span>,
},
];
const column2: ColumnsType<DataType> = [
{
title: "课程",
dataIndex: "title",
render: (_, record: any) => (
<div className="d-flex">
<Image
src={courses[record.course_id].thumb}
preview={false}
width={80}
height={60}
style={{ borderRadius: 6 }}
/>
<span className="ml-8">{courses[record.course_id].title}</span>
</div>
),
},
{
title: "总课时",
dataIndex: "total_duration",
render: (_, record: any) => (
<>
<span>{record.hour_count}</span>
</>
),
},
{
title: "已学习课时",
dataIndex: "finished_duration",
render: (_, record: any) => (
<>
<span>{record.finished_count}</span>
</>
),
},
{
title: "状态",
dataIndex: "is_finished",
render: (_, record: any) => (
<>
{record.is_finished === 1 ? <span></span> : <span></span>}
</>
),
},
{
title: "时间",
dataIndex: "created_at",
render: (text: string) => <span>{dateFormat(text)}</span>,
},
];
return (
<>
<Row className="playedu-main-top mb-24">
<div className="float-left mb-24">
<BackBartment title="学员学习" />
</div>
<div className={styles["large-title"]}></div>
<div className={styles["charts"]}>
<div
ref={chartRef}
style={{
width: "100% !important",
height: 300,
position: "relative",
}}
></div>
</div>
</Row>
<div className="playedu-main-top mb-24">
<div className={styles["large-title"]}></div>
<div className="float-left mt-24">
<Table
columns={columns}
dataSource={list}
loading={loading}
pagination={paginationProps}
rowKey={(record) => record.id}
/>
</div>
</div>
<div className="playedu-main-top">
<div className={styles["large-title"]}>线</div>
<div className="float-left mt-24">
<Table
columns={column2}
dataSource={list2}
loading={loading2}
pagination={paginationProps2}
rowKey={(record) => record.id}
/>
</div>
</div>
</>
);
};
export default MemberLearnPage;

View File

@ -16,6 +16,7 @@ import CoursePage from "../pages/course/index";
import CourseUserPage from "../pages/course/user"; import CourseUserPage from "../pages/course/user";
import MemberPage from "../pages/member"; import MemberPage from "../pages/member";
import MemberImportPage from "../pages/member/import"; import MemberImportPage from "../pages/member/import";
import MemberLearnPage from "../pages/member/learn";
import SystemConfigPage from "../pages/system/config"; import SystemConfigPage from "../pages/system/config";
import SystemAdministratorPage from "../pages/system/administrator"; import SystemAdministratorPage from "../pages/system/administrator";
import SystemAdminrolesPage from "../pages/system/adminroles"; import SystemAdminrolesPage from "../pages/system/adminroles";
@ -32,7 +33,7 @@ if (getToken()) {
try { try {
let configRes: any = await system.getSystemConfig(); let configRes: any = await system.getSystemConfig();
let userRes: any = await login.getUser(); let userRes: any = await login.getUser();
resolve({ resolve({
default: ( default: (
<InitPage configData={configRes.data} loginData={userRes.data} /> <InitPage configData={configRes.data} loginData={userRes.data} />
@ -98,6 +99,10 @@ const routes: RouteObject[] = [
path: "/member/import", path: "/member/import",
element: <MemberImportPage />, element: <MemberImportPage />,
}, },
{
path: "/member/learn",
element: <MemberLearnPage />,
},
{ {
path: "/system/config/index", path: "/system/config/index",
element: <SystemConfigPage />, element: <SystemConfigPage />,