mirror of
https://github.com/PlayEdu/backend
synced 2025-09-11 03:04:11 +08:00
commit
5d834ab2d6
@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^1.9.3",
|
"@reduxjs/toolkit": "^1.9.3",
|
||||||
|
"ahooks": "^3.7.6",
|
||||||
"antd": "^5.3.2",
|
"antd": "^5.3.2",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"echarts": "^5.4.2",
|
"echarts": "^5.4.2",
|
||||||
|
Binary file not shown.
@ -109,6 +109,10 @@ export function learnCourses(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function learnAllCourses(id: number) {
|
||||||
|
return client.get(`/backend/v1/user/${id}/all-courses`, {});
|
||||||
|
}
|
||||||
|
|
||||||
export function departmentProgress(
|
export function departmentProgress(
|
||||||
id: number,
|
id: number,
|
||||||
page: number,
|
page: number,
|
||||||
@ -121,3 +125,25 @@ export function departmentProgress(
|
|||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function learnCoursesProgress(
|
||||||
|
id: number,
|
||||||
|
courseId: number,
|
||||||
|
params: any
|
||||||
|
) {
|
||||||
|
return client.get(`/backend/v1/user/${id}/learn-course/${courseId} `, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroyAllUserLearned(id: number, courseId: number) {
|
||||||
|
return client.destroy(`/backend/v1/user/${id}/learn-course/${courseId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function destroyUserLearned(
|
||||||
|
id: number,
|
||||||
|
courseId: number,
|
||||||
|
hourId: number
|
||||||
|
) {
|
||||||
|
return client.destroy(
|
||||||
|
`/backend/v1/user/${id}/learn-course/${courseId}/hour/${hourId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -54,6 +54,7 @@ export const CreateResourceCategory = (props: PropInterface) => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setName(e.target.value);
|
setName(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
@ -8,7 +8,7 @@ export const Footer: React.FC = () => {
|
|||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
backgroundColor: "#F6F6F6",
|
backgroundColor: "#F6F6F6",
|
||||||
height: 232,
|
height: 166,
|
||||||
paddingTop: 80,
|
paddingTop: 80,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
}}
|
}}
|
||||||
|
29
src/compenents/keep-alive/index.tsx
Normal file
29
src/compenents/keep-alive/index.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useUpdate } from "ahooks";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { useLocation, useOutlet } from "react-router-dom";
|
||||||
|
|
||||||
|
function KeepAlive() {
|
||||||
|
const componentList = useRef(new Map());
|
||||||
|
const outLet = useOutlet();
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const forceUpdate = useUpdate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!componentList.current.has(pathname)) {
|
||||||
|
componentList.current.set(pathname, outLet);
|
||||||
|
}
|
||||||
|
forceUpdate();
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{Array.from(componentList.current).map(([key, component]) => (
|
||||||
|
<div key={key} style={{ display: pathname === key ? "block" : "none" }}>
|
||||||
|
{component}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default KeepAlive;
|
@ -50,7 +50,7 @@ const items = [
|
|||||||
"user",
|
"user",
|
||||||
<i className="iconfont icon-icon-user" />,
|
<i className="iconfont icon-icon-user" />,
|
||||||
[
|
[
|
||||||
getItem("学员", "/member", null, null, null),
|
getItem("学员", "/member/index", null, null, null),
|
||||||
getItem("部门", "/department", null, null, null),
|
getItem("部门", "/department", null, null, null),
|
||||||
],
|
],
|
||||||
null
|
null
|
||||||
|
@ -5,6 +5,7 @@ import { resourceCategory } from "../../api/index";
|
|||||||
interface Option {
|
interface Option {
|
||||||
key: string | number;
|
key: string | number;
|
||||||
title: any;
|
title: any;
|
||||||
|
|
||||||
children?: Option[];
|
children?: Option[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,3 +18,20 @@
|
|||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checked {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: #ff4d4f;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 2px solid #ff4d4f;
|
||||||
|
position: absolute;
|
||||||
|
left: 5px;
|
||||||
|
top: 5px;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
import { resource, resourceCategory } from "../../api";
|
import { resource, resourceCategory } from "../../api";
|
||||||
import styles from "./index.module.less";
|
import styles from "./index.module.less";
|
||||||
import { CreateResourceCategory } from "../create-rs-category";
|
import { CreateResourceCategory } from "../create-rs-category";
|
||||||
import { CloseOutlined } from "@ant-design/icons";
|
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";
|
||||||
import { UploadImageSub } from "./upload-image-sub";
|
import { UploadImageSub } from "./upload-image-sub";
|
||||||
import { TreeCategory } from "../../compenents";
|
import { TreeCategory } from "../../compenents";
|
||||||
|
|
||||||
@ -49,6 +49,7 @@ export const UploadImageButton = (props: PropsInterface) => {
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [size, setSize] = useState(15);
|
const [size, setSize] = useState(15);
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
|
const [selected, setSelected] = useState<string>("");
|
||||||
|
|
||||||
// 获取图片列表
|
// 获取图片列表
|
||||||
const getImageList = () => {
|
const getImageList = () => {
|
||||||
@ -97,13 +98,24 @@ export const UploadImageButton = (props: PropsInterface) => {
|
|||||||
open={true}
|
open={true}
|
||||||
width={820}
|
width={820}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
|
onOk={() => {
|
||||||
|
if (!selected) {
|
||||||
|
message.error("请选择图片后确定");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.onSelected(selected);
|
||||||
|
setShowModal(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Row style={{ width: 752, minHeight: 520, marginTop: 24 }}>
|
<Row style={{ width: 752, minHeight: 520, marginTop: 24 }}>
|
||||||
<Col span={7}>
|
<Col span={7}>
|
||||||
<TreeCategory
|
<TreeCategory
|
||||||
type="no-cate"
|
type="no-cate"
|
||||||
text={"图片"}
|
text={"图片"}
|
||||||
onUpdate={(keys: any) => setCategoryIds(keys)}
|
onUpdate={(keys: any) => {
|
||||||
|
setSelected("");
|
||||||
|
setCategoryIds(keys);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={17}>
|
<Col span={17}>
|
||||||
@ -129,10 +141,21 @@ export const UploadImageButton = (props: PropsInterface) => {
|
|||||||
className="image-item"
|
className="image-item"
|
||||||
style={{ backgroundImage: `url(${item.url})` }}
|
style={{ backgroundImage: `url(${item.url})` }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.onSelected(item.url);
|
setSelected(item.url);
|
||||||
setShowModal(false);
|
|
||||||
}}
|
}}
|
||||||
></div>
|
>
|
||||||
|
{selected.indexOf(item.url) !== -1 && (
|
||||||
|
<i
|
||||||
|
className={styles.checked}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setSelected("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckOutlined />
|
||||||
|
</i>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{imageList.length > 0 && (
|
{imageList.length > 0 && (
|
||||||
|
@ -614,6 +614,8 @@ textarea.ant-input {
|
|||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-color: #f6f6f6;
|
background-color: #f6f6f6;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,14 +45,14 @@ const ChangePasswordPage = () => {
|
|||||||
name="old_password"
|
name="old_password"
|
||||||
rules={[{ required: true, message: "请输入原密码!" }]}
|
rules={[{ required: true, message: "请输入原密码!" }]}
|
||||||
>
|
>
|
||||||
<Input.Password placeholder="请输入原密码" />
|
<Input.Password placeholder="请输入原密码" allowClear />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="新密码"
|
label="新密码"
|
||||||
name="new_password"
|
name="new_password"
|
||||||
rules={[{ required: true, message: "请输入新密码!" }]}
|
rules={[{ required: true, message: "请输入新密码!" }]}
|
||||||
>
|
>
|
||||||
<Input.Password placeholder="请输入新密码" />
|
<Input.Password placeholder="请输入新密码" allowClear />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
|
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
|
||||||
<Button type="primary" htmlType="submit">
|
<Button type="primary" htmlType="submit">
|
||||||
|
@ -470,6 +470,7 @@ export const CourseCreate: React.FC<PropInterface> = ({
|
|||||||
<Input
|
<Input
|
||||||
style={{ width: 424 }}
|
style={{ width: 424 }}
|
||||||
placeholder="请在此处输入课程名称"
|
placeholder="请在此处输入课程名称"
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -683,6 +684,7 @@ export const CourseCreate: React.FC<PropInterface> = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setChapterName(index, e.target.value);
|
setChapterName(index, e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
placeholder="请在此处输入章节名称"
|
placeholder="请在此处输入章节名称"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
@ -451,6 +451,7 @@ export const CourseHourUpdate: React.FC<PropInterface> = ({
|
|||||||
saveChapterName(index, e.target.value);
|
saveChapterName(index, e.target.value);
|
||||||
}}
|
}}
|
||||||
placeholder="请在此处输入章节名称"
|
placeholder="请在此处输入章节名称"
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className="mr-16"
|
className="mr-16"
|
||||||
|
@ -224,6 +224,7 @@ export const CourseUpdate: React.FC<PropInterface> = ({
|
|||||||
rules={[{ required: true, message: "请在此处输入课程名称!" }]}
|
rules={[{ required: true, message: "请在此处输入课程名称!" }]}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
|
allowClear
|
||||||
style={{ width: 424 }}
|
style={{ width: 424 }}
|
||||||
placeholder="请在此处输入课程名称"
|
placeholder="请在此处输入课程名称"
|
||||||
/>
|
/>
|
||||||
|
@ -51,6 +51,7 @@ const CoursePage = () => {
|
|||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
const [dep_ids, setDepIds] = useState<any>([]);
|
const [dep_ids, setDepIds] = useState<any>([]);
|
||||||
const [selLabel, setLabel] = useState<string>("全部分类");
|
const [selLabel, setLabel] = useState<string>("全部分类");
|
||||||
|
const [selDepLabel, setDepLabel] = useState<string>("全部部门");
|
||||||
const [course_category_ids, setCourseCategoryIds] = useState<any>({});
|
const [course_category_ids, setCourseCategoryIds] = useState<any>({});
|
||||||
const [course_dep_ids, setCourseDepIds] = useState<any>({});
|
const [course_dep_ids, setCourseDepIds] = useState<any>({});
|
||||||
const [categories, setCategories] = useState<any>({});
|
const [categories, setCategories] = useState<any>({});
|
||||||
@ -95,7 +96,7 @@ const CoursePage = () => {
|
|||||||
text={"部门"}
|
text={"部门"}
|
||||||
onUpdate={(keys: any, title: any) => {
|
onUpdate={(keys: any, title: any) => {
|
||||||
setDepIds(keys);
|
setDepIds(keys);
|
||||||
setLabel(title);
|
setDepLabel(title);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -282,11 +283,16 @@ const CoursePage = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取视频列表
|
// 获取列表
|
||||||
const getList = () => {
|
const getList = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let categoryIds = category_ids.join(",");
|
let categoryIds = "";
|
||||||
let depIds = dep_ids.join(",");
|
let depIds = "";
|
||||||
|
if (tabKey === 1) {
|
||||||
|
categoryIds = category_ids.join(",");
|
||||||
|
} else {
|
||||||
|
depIds = dep_ids.join(",");
|
||||||
|
}
|
||||||
course
|
course
|
||||||
.courseList(page, size, "", "", title, depIds, categoryIds)
|
.courseList(page, size, "", "", title, depIds, categoryIds)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
@ -314,7 +320,7 @@ const CoursePage = () => {
|
|||||||
// 加载列表
|
// 加载列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getList();
|
getList();
|
||||||
}, [category_ids, dep_ids, refresh, page, size]);
|
}, [category_ids, dep_ids, refresh, page, size, tabKey]);
|
||||||
|
|
||||||
const paginationProps = {
|
const paginationProps = {
|
||||||
current: page, //当前页码
|
current: page, //当前页码
|
||||||
@ -348,7 +354,7 @@ const CoursePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="right-box">
|
<div className="right-box">
|
||||||
<div className="playedu-main-title float-left mb-24">
|
<div className="playedu-main-title float-left mb-24">
|
||||||
线上课 | {selLabel}
|
线上课 | {tabKey === 1 ? selLabel : selDepLabel}
|
||||||
</div>
|
</div>
|
||||||
<div className="float-left j-b-flex mb-24">
|
<div className="float-left j-b-flex mb-24">
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
@ -370,6 +376,7 @@ const CoursePage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setTitle(e.target.value);
|
setTitle(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入名称关键字"
|
placeholder="请输入名称关键字"
|
||||||
/>
|
/>
|
||||||
@ -399,8 +406,8 @@ const CoursePage = () => {
|
|||||||
rowKey={(record) => record.id}
|
rowKey={(record) => record.id}
|
||||||
/>
|
/>
|
||||||
<CourseCreate
|
<CourseCreate
|
||||||
cateIds={category_ids}
|
cateIds={tabKey === 1 ? category_ids : []}
|
||||||
depIds={dep_ids}
|
depIds={tabKey === 2 ? dep_ids : []}
|
||||||
open={createVisible}
|
open={createVisible}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setCreateVisible(false);
|
setCreateVisible(false);
|
||||||
|
@ -149,13 +149,13 @@ const CourseUserPage = () => {
|
|||||||
// 删除学员
|
// 删除学员
|
||||||
const delItem = () => {
|
const delItem = () => {
|
||||||
if (selectedRowKeys.length === 0) {
|
if (selectedRowKeys.length === 0) {
|
||||||
message.error("请选择学员后再清除");
|
message.error("请选择学员后再重置");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
confirm({
|
confirm({
|
||||||
title: "操作确认",
|
title: "操作确认",
|
||||||
icon: <ExclamationCircleFilled />,
|
icon: <ExclamationCircleFilled />,
|
||||||
content: "确认清除选中学员学习记录?",
|
content: "确认重置选中学员学习记录?",
|
||||||
centered: true,
|
centered: true,
|
||||||
okText: "确认",
|
okText: "确认",
|
||||||
cancelText: "取消",
|
cancelText: "取消",
|
||||||
@ -190,7 +190,7 @@ const CourseUserPage = () => {
|
|||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<PerButton
|
<PerButton
|
||||||
type="primary"
|
type="primary"
|
||||||
text="清除学习记录"
|
text="重置学习记录"
|
||||||
class="mr-16"
|
class="mr-16"
|
||||||
icon={null}
|
icon={null}
|
||||||
p="course"
|
p="course"
|
||||||
@ -206,6 +206,7 @@ const CourseUserPage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setName(e.target.value);
|
setName(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入姓名关键字"
|
placeholder="请输入姓名关键字"
|
||||||
/>
|
/>
|
||||||
@ -217,6 +218,7 @@ const CourseUserPage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setEmail(e.target.value);
|
setEmail(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入学员邮箱"
|
placeholder="请输入学员邮箱"
|
||||||
/>
|
/>
|
||||||
|
@ -199,7 +199,7 @@ const DashboardPage = () => {
|
|||||||
<div
|
<div
|
||||||
className={styles["link-mode"]}
|
className={styles["link-mode"]}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/member");
|
navigate("/member/index");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
@ -516,7 +516,7 @@ const DashboardPage = () => {
|
|||||||
<div className={styles["usage-guide"]}>
|
<div className={styles["usage-guide"]}>
|
||||||
<img className={styles["banner"]} src={banner} alt="" />
|
<img className={styles["banner"]} src={banner} alt="" />
|
||||||
<Link
|
<Link
|
||||||
to="https://www.playedu.xyz/docs/docs/intro/"
|
to="https://www.playedu.xyz/docs/docs/guide/"
|
||||||
target="blank"
|
target="blank"
|
||||||
className={styles["link"]}
|
className={styles["link"]}
|
||||||
>
|
>
|
||||||
|
@ -147,7 +147,11 @@ export const DepartmentCreate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入部门名称!" }]}
|
rules={[{ required: true, message: "请输入部门名称!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入部门名称" />
|
<Input
|
||||||
|
style={{ width: 200 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入部门名称"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -194,7 +194,7 @@ const DepartmentPage = () => {
|
|||||||
type="link"
|
type="link"
|
||||||
style={{ paddingLeft: 4, paddingRight: 4 }}
|
style={{ paddingLeft: 4, paddingRight: 4 }}
|
||||||
danger
|
danger
|
||||||
onClick={() => navigate("/member")}
|
onClick={() => navigate("/member/index")}
|
||||||
>
|
>
|
||||||
({res.data.users.length}个学员),
|
({res.data.users.length}个学员),
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -73,7 +73,6 @@ const LoginPage = () => {
|
|||||||
|
|
||||||
navigate("/", { replace: true });
|
navigate("/", { replace: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message.error("登录出现错误");
|
|
||||||
console.error("错误信息", e);
|
console.error("错误信息", e);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
fetchImageCaptcha(); //刷新图形验证码
|
fetchImageCaptcha(); //刷新图形验证码
|
||||||
@ -129,6 +128,7 @@ const LoginPage = () => {
|
|||||||
style={{ width: 400, height: 54 }}
|
style={{ width: 400, height: 54 }}
|
||||||
placeholder="请输入管理员邮箱账号"
|
placeholder="请输入管理员邮箱账号"
|
||||||
onKeyUp={(e) => keyUp(e)}
|
onKeyUp={(e) => keyUp(e)}
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="login-box d-flex mt-50">
|
<div className="login-box d-flex mt-50">
|
||||||
@ -137,6 +137,7 @@ const LoginPage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setPassword(e.target.value);
|
setPassword(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 400, height: 54 }}
|
style={{ width: 400, height: 54 }}
|
||||||
placeholder="请输入密码"
|
placeholder="请输入密码"
|
||||||
/>
|
/>
|
||||||
@ -149,6 +150,7 @@ const LoginPage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setCaptchaVal(e.target.value);
|
setCaptchaVal(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
onKeyUp={(e) => keyUp(e)}
|
onKeyUp={(e) => keyUp(e)}
|
||||||
/>
|
/>
|
||||||
<div className={styles["captcha-box"]}>
|
<div className={styles["captcha-box"]}>
|
||||||
|
@ -8,6 +8,7 @@ import { ValidataCredentials } from "../../../utils/index";
|
|||||||
|
|
||||||
interface PropInterface {
|
interface PropInterface {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
depIds: any;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +18,11 @@ interface Option {
|
|||||||
children?: Option[];
|
children?: Option[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MemberCreate: React.FC<PropInterface> = ({ open, onCancel }) => {
|
export const MemberCreate: React.FC<PropInterface> = ({
|
||||||
|
open,
|
||||||
|
depIds,
|
||||||
|
onCancel,
|
||||||
|
}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [departments, setDepartments] = useState<any>([]);
|
const [departments, setDepartments] = useState<any>([]);
|
||||||
@ -39,10 +44,10 @@ export const MemberCreate: React.FC<PropInterface> = ({ open, onCancel }) => {
|
|||||||
password: "",
|
password: "",
|
||||||
avatar: memberDefaultAvatar,
|
avatar: memberDefaultAvatar,
|
||||||
idCard: "",
|
idCard: "",
|
||||||
dep_ids: [],
|
dep_ids: depIds,
|
||||||
});
|
});
|
||||||
setAvatar(memberDefaultAvatar);
|
setAvatar(memberDefaultAvatar);
|
||||||
}, [form, open]);
|
}, [form, open, depIds]);
|
||||||
|
|
||||||
const getParams = () => {
|
const getParams = () => {
|
||||||
department.departmentList().then((res: any) => {
|
department.departmentList().then((res: any) => {
|
||||||
@ -154,7 +159,11 @@ export const MemberCreate: React.FC<PropInterface> = ({ open, onCancel }) => {
|
|||||||
name="email"
|
name="email"
|
||||||
rules={[{ required: true, message: "请输入登录邮箱!" }]}
|
rules={[{ required: true, message: "请输入登录邮箱!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 274 }} placeholder="请输入学员登录邮箱" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 274 }}
|
||||||
|
placeholder="请输入学员登录邮箱"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="登录密码"
|
label="登录密码"
|
||||||
@ -162,6 +171,7 @@ export const MemberCreate: React.FC<PropInterface> = ({ open, onCancel }) => {
|
|||||||
rules={[{ required: true, message: "请输入登录密码!" }]}
|
rules={[{ required: true, message: "请输入登录密码!" }]}
|
||||||
>
|
>
|
||||||
<Input.Password
|
<Input.Password
|
||||||
|
allowClear
|
||||||
style={{ width: 274 }}
|
style={{ width: 274 }}
|
||||||
placeholder="请输入登录密码"
|
placeholder="请输入登录密码"
|
||||||
/>
|
/>
|
||||||
@ -182,7 +192,11 @@ export const MemberCreate: React.FC<PropInterface> = ({ open, onCancel }) => {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="身份证号" name="idCard">
|
<Form.Item label="身份证号" name="idCard">
|
||||||
<Input style={{ width: 274 }} placeholder="请填写学员身份证号" />
|
<Input
|
||||||
|
style={{ width: 274 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请填写学员身份证号"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
0
src/pages/member/compenents/progress.module.scss
Normal file
0
src/pages/member/compenents/progress.module.scss
Normal file
252
src/pages/member/compenents/progress.tsx
Normal file
252
src/pages/member/compenents/progress.tsx
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import styles from "./progrss.module.less";
|
||||||
|
import { Table, Modal, message } from "antd";
|
||||||
|
import { PerButton, DurationText } from "../../../compenents";
|
||||||
|
import { user as member } from "../../../api/index";
|
||||||
|
import type { ColumnsType } from "antd/es/table";
|
||||||
|
import { dateFormat } from "../../../utils/index";
|
||||||
|
import { ExclamationCircleFilled } from "@ant-design/icons";
|
||||||
|
const { confirm } = Modal;
|
||||||
|
|
||||||
|
interface DataType {
|
||||||
|
id: React.Key;
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
created_at: string;
|
||||||
|
duration: number;
|
||||||
|
finished_duration: number;
|
||||||
|
is_finished: boolean;
|
||||||
|
finished_at: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PropInterface {
|
||||||
|
open: boolean;
|
||||||
|
uid: number;
|
||||||
|
id: number;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MemberLearnProgressDialog: React.FC<PropInterface> = ({
|
||||||
|
open,
|
||||||
|
uid,
|
||||||
|
id,
|
||||||
|
onCancel,
|
||||||
|
}) => {
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [list, setList] = useState<any>([]);
|
||||||
|
const [records, setRecords] = useState<any>({});
|
||||||
|
const [refresh, setRefresh] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
}, [uid, id, open, refresh]);
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
member.learnCoursesProgress(uid, id, {}).then((res: any) => {
|
||||||
|
setList(res.data.hours);
|
||||||
|
setRecords(res.data.learn_records);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const column: ColumnsType<DataType> = [
|
||||||
|
{
|
||||||
|
title: "课时标题",
|
||||||
|
dataIndex: "title",
|
||||||
|
|
||||||
|
render: (title: string) => (
|
||||||
|
<>
|
||||||
|
<span>{title}</span>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "总时长",
|
||||||
|
width: 120,
|
||||||
|
dataIndex: "duration",
|
||||||
|
render: (duration: number) => (
|
||||||
|
<>
|
||||||
|
<DurationText duration={duration}></DurationText>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "已学习时长",
|
||||||
|
width: 120,
|
||||||
|
dataIndex: "finished_duration",
|
||||||
|
render: (_, record: any) => (
|
||||||
|
<>
|
||||||
|
{records && records[record.id] ? (
|
||||||
|
<span>
|
||||||
|
<DurationText
|
||||||
|
duration={records[record.id].finished_duration || 0}
|
||||||
|
></DurationText>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>-</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "是否学完",
|
||||||
|
width: 100,
|
||||||
|
dataIndex: "is_finished",
|
||||||
|
render: (_, record: any) => (
|
||||||
|
<>
|
||||||
|
{records &&
|
||||||
|
records[record.id] &&
|
||||||
|
records[record.id].is_finished === 1 ? (
|
||||||
|
<span className="c-green">已学完</span>
|
||||||
|
) : (
|
||||||
|
<span className="c-red">未学完</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "开始时间",
|
||||||
|
width: 150,
|
||||||
|
dataIndex: "created_at",
|
||||||
|
render: (_, record: any) => (
|
||||||
|
<>
|
||||||
|
{records && records[record.id] ? (
|
||||||
|
<span>{dateFormat(records[record.id].created_at)}</span>
|
||||||
|
) : (
|
||||||
|
<span>-</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "学完时间",
|
||||||
|
width: 150,
|
||||||
|
dataIndex: "finished_at",
|
||||||
|
render: (_, record: any) => (
|
||||||
|
<>
|
||||||
|
{records && records[record.id] ? (
|
||||||
|
<span>{dateFormat(records[record.id].finished_at)}</span>
|
||||||
|
) : (
|
||||||
|
<span>-</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
key: "action",
|
||||||
|
fixed: "right",
|
||||||
|
width: 70,
|
||||||
|
render: (_, record: any) => (
|
||||||
|
<>
|
||||||
|
{records && records[record.id] ? (
|
||||||
|
<PerButton
|
||||||
|
type="link"
|
||||||
|
text="重置"
|
||||||
|
class="b-link c-red"
|
||||||
|
icon={null}
|
||||||
|
p="user-learn-destroy"
|
||||||
|
onClick={() => {
|
||||||
|
clearSingleProgress(records[record.id].hour_id);
|
||||||
|
}}
|
||||||
|
disabled={null}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span>-</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const clearProgress = () => {
|
||||||
|
confirm({
|
||||||
|
title: "操作确认",
|
||||||
|
icon: <ExclamationCircleFilled />,
|
||||||
|
content: "确认重置此课程下所有课时的学习记录?",
|
||||||
|
centered: true,
|
||||||
|
okText: "确认",
|
||||||
|
cancelText: "取消",
|
||||||
|
onOk() {
|
||||||
|
member.destroyAllUserLearned(uid, id).then((res: any) => {
|
||||||
|
message.success("操作成功");
|
||||||
|
setRefresh(!refresh);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log("Cancel");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSingleProgress = (hour_id: number) => {
|
||||||
|
if (hour_id === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
confirm({
|
||||||
|
title: "操作确认",
|
||||||
|
icon: <ExclamationCircleFilled />,
|
||||||
|
content: "确认重置此课时的学习记录?",
|
||||||
|
centered: true,
|
||||||
|
okText: "确认",
|
||||||
|
cancelText: "取消",
|
||||||
|
onOk() {
|
||||||
|
member.destroyUserLearned(uid, id, hour_id).then((res: any) => {
|
||||||
|
message.success("操作成功");
|
||||||
|
setRefresh(!refresh);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log("Cancel");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title="课时学习进度"
|
||||||
|
centered
|
||||||
|
forceRender
|
||||||
|
open={open}
|
||||||
|
width={1000}
|
||||||
|
onOk={() => onCancel()}
|
||||||
|
onCancel={() => onCancel()}
|
||||||
|
maskClosable={false}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<div className="d-flex mt-24">
|
||||||
|
<PerButton
|
||||||
|
type="primary"
|
||||||
|
text="重置学习记录"
|
||||||
|
class="c-white"
|
||||||
|
icon={null}
|
||||||
|
p="user-learn-destroy"
|
||||||
|
onClick={() => {
|
||||||
|
clearProgress();
|
||||||
|
}}
|
||||||
|
disabled={null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="d-flex mt-24"
|
||||||
|
style={{ maxHeight: 800, overflowY: "auto" }}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
columns={column}
|
||||||
|
dataSource={list}
|
||||||
|
loading={loading}
|
||||||
|
rowKey={(record) => record.id}
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -181,18 +181,27 @@ export const MemberUpdate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入学员姓名!" }]}
|
rules={[{ required: true, message: "请输入学员姓名!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 274 }} placeholder="请填写学员姓名" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 274 }}
|
||||||
|
placeholder="请填写学员姓名"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="登录邮箱"
|
label="登录邮箱"
|
||||||
name="email"
|
name="email"
|
||||||
rules={[{ required: true, message: "请输入登录邮箱!" }]}
|
rules={[{ required: true, message: "请输入登录邮箱!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 274 }} placeholder="请输入学员登录邮箱" />
|
<Input
|
||||||
|
style={{ width: 274 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入学员登录邮箱"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="登录密码" name="password">
|
<Form.Item label="登录密码" name="password">
|
||||||
<Input.Password
|
<Input.Password
|
||||||
style={{ width: 274 }}
|
style={{ width: 274 }}
|
||||||
|
allowClear
|
||||||
placeholder="请输入登录密码"
|
placeholder="请输入登录密码"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -212,7 +221,11 @@ export const MemberUpdate: React.FC<PropInterface> = ({
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="身份证号" name="idCard">
|
<Form.Item label="身份证号" name="idCard">
|
||||||
<Input style={{ width: 274 }} placeholder="请填写学员身份证号" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 274 }}
|
||||||
|
placeholder="请填写学员身份证号"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,12 +9,14 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
message,
|
message,
|
||||||
Table,
|
Table,
|
||||||
|
Select,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
import { BackBartment, DurationText } from "../../compenents";
|
import { BackBartment, DurationText } from "../../compenents";
|
||||||
import { dateFormat } from "../../utils/index";
|
import { dateFormat } from "../../utils/index";
|
||||||
import { user as member } from "../../api/index";
|
import { user as member } from "../../api/index";
|
||||||
const { Column, ColumnGroup } = Table;
|
const { Column, ColumnGroup } = Table;
|
||||||
|
import * as XLSX from "xlsx";
|
||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
id: React.Key;
|
id: React.Key;
|
||||||
@ -40,8 +42,20 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
const [name, setName] = useState<string>("");
|
const [name, setName] = useState<string>("");
|
||||||
const [email, setEmail] = useState<string>("");
|
const [email, setEmail] = useState<string>("");
|
||||||
const [id_card, setIdCard] = useState<string>("");
|
const [id_card, setIdCard] = useState<string>("");
|
||||||
|
const [showMode, setShowMode] = useState<string>("all");
|
||||||
const [did, setDid] = useState(Number(result.get("id")));
|
const [did, setDid] = useState(Number(result.get("id")));
|
||||||
const [title, setTitle] = useState(String(result.get("title")));
|
const [title, setTitle] = useState(String(result.get("title")));
|
||||||
|
const [exportLoading, setExportLoading] = useState(false);
|
||||||
|
const modes = [
|
||||||
|
{ label: "全部", value: "all" },
|
||||||
|
{ label: "不显示公开课", value: "only_dep" },
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDid(Number(result.get("id")));
|
||||||
|
setTitle(String(result.get("title")));
|
||||||
|
resetData();
|
||||||
|
}, [result.get("id"), result.get("title")]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData();
|
getData();
|
||||||
@ -59,6 +73,7 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email,
|
||||||
id_card: id_card,
|
id_card: id_card,
|
||||||
|
show_mode: showMode,
|
||||||
})
|
})
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
setList(res.data.data);
|
setList(res.data.data);
|
||||||
@ -81,6 +96,7 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
setName("");
|
setName("");
|
||||||
setEmail("");
|
setEmail("");
|
||||||
setIdCard("");
|
setIdCard("");
|
||||||
|
setShowMode("all");
|
||||||
setPage(1);
|
setPage(1);
|
||||||
setSize(10);
|
setSize(10);
|
||||||
setList([]);
|
setList([]);
|
||||||
@ -125,14 +141,80 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportExcel = () => {
|
||||||
|
if (exportLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setExportLoading(true);
|
||||||
|
let filter = {
|
||||||
|
sort_field: "",
|
||||||
|
sort_algo: "",
|
||||||
|
name: name,
|
||||||
|
email: email,
|
||||||
|
id_card: id_card,
|
||||||
|
show_mode: showMode,
|
||||||
|
};
|
||||||
|
member.departmentProgress(did, page, total, filter).then((res: any) => {
|
||||||
|
if (res.data.total === 0) {
|
||||||
|
message.error("数据为空");
|
||||||
|
setExportLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let filename = title + "学习进度.xlsx";
|
||||||
|
let sheetName = "sheet1";
|
||||||
|
let data = [];
|
||||||
|
let arr = ["学员"];
|
||||||
|
courses.map((item: any) => {
|
||||||
|
arr.push(item.title);
|
||||||
|
});
|
||||||
|
arr.push("总计课时");
|
||||||
|
data.push(arr);
|
||||||
|
|
||||||
|
res.data.data.forEach((item: any) => {
|
||||||
|
let arr = [item.name];
|
||||||
|
courses.map((it: any) => {
|
||||||
|
if (records && records[item.id] && records[item.id][it.id]) {
|
||||||
|
if (records && records[item.id][it.id].is_finished === 1) {
|
||||||
|
arr.push("已学完");
|
||||||
|
} else {
|
||||||
|
arr.push(
|
||||||
|
records &&
|
||||||
|
records[item.id][it.id].finished_count + " / " + it.class_hour
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arr.push(0 + " / " + it.class_hour);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
arr.push(getFinishedHours(records[item.id]) + " / " + totalHour);
|
||||||
|
data.push(arr);
|
||||||
|
});
|
||||||
|
|
||||||
|
const jsonWorkSheet = XLSX.utils.json_to_sheet(data);
|
||||||
|
const workBook: XLSX.WorkBook = {
|
||||||
|
SheetNames: [sheetName],
|
||||||
|
Sheets: {
|
||||||
|
[sheetName]: jsonWorkSheet,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
XLSX.writeFile(workBook, filename);
|
||||||
|
setExportLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="playedu-main-body">
|
<div className="playedu-main-body">
|
||||||
<div className="float-left mb-24">
|
<div className="float-left mb-24">
|
||||||
<BackBartment title={title + "学习进度"} />
|
<BackBartment title={title + "学习进度"} />
|
||||||
</div>
|
</div>
|
||||||
<div className="float-left j-b-flex mb-24">
|
<div className="float-left j-b-flex mb-24">
|
||||||
<div className="d-flex helper-text ">
|
<div className="d-flex">
|
||||||
(以下表格内数字对应的是表头课程的“已学完课时数/总课时数”)
|
<Button type="default" onClick={() => exportExcel()}>
|
||||||
|
批量导出表格
|
||||||
|
</Button>
|
||||||
|
<div className="helper-text ml-24">
|
||||||
|
(以下表格内数字对应的是表头课程的“已学完课时数/总课时数”)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<div className="d-flex mr-24 ">
|
<div className="d-flex mr-24 ">
|
||||||
@ -142,30 +224,32 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setName(e.target.value);
|
setName(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入姓名关键字"
|
placeholder="请输入姓名关键字"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex mr-24">
|
{/* <div className="d-flex mr-24">
|
||||||
<Typography.Text>邮箱:</Typography.Text>
|
<Typography.Text>邮箱:</Typography.Text>
|
||||||
<Input
|
<Input
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setEmail(e.target.value);
|
setEmail(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入邮箱"
|
placeholder="请输入邮箱"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="d-flex mr-24">
|
<div className="d-flex mr-24">
|
||||||
<Typography.Text>身份证号:</Typography.Text>
|
<Typography.Text>模式:</Typography.Text>
|
||||||
<Input
|
<Select
|
||||||
value={id_card}
|
|
||||||
onChange={(e) => {
|
|
||||||
setIdCard(e.target.value);
|
|
||||||
}}
|
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入身份证号"
|
allowClear
|
||||||
|
placeholder="请选择"
|
||||||
|
value={showMode}
|
||||||
|
onChange={(value: string) => setShowMode(value)}
|
||||||
|
options={modes}
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
@ -198,7 +282,7 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
title="学员"
|
title="学员"
|
||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
key="name"
|
key="name"
|
||||||
width={100}
|
width={150}
|
||||||
render={(_, record: any) => (
|
render={(_, record: any) => (
|
||||||
<>
|
<>
|
||||||
<Image
|
<Image
|
||||||
@ -218,12 +302,12 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
ellipsis={true}
|
ellipsis={true}
|
||||||
dataIndex="id"
|
dataIndex="id"
|
||||||
key={item.id}
|
key={item.id}
|
||||||
width={100}
|
width={168}
|
||||||
render={(_, record: any) => (
|
render={(_, record: any) => (
|
||||||
<>
|
<>
|
||||||
{records[record.id] && records[record.id][item.id] ? (
|
{records[record.id] && records[record.id][item.id] ? (
|
||||||
records[record.id][item.id].is_finished === 1 ? (
|
records[record.id][item.id].is_finished === 1 ? (
|
||||||
<span>已完成</span>
|
<span>已学完</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
@ -243,10 +327,10 @@ const MemberDepartmentProgressPage = () => {
|
|||||||
))}
|
))}
|
||||||
<Column
|
<Column
|
||||||
fixed="right"
|
fixed="right"
|
||||||
title="所有课程总课时"
|
title="总计课时"
|
||||||
dataIndex="id"
|
dataIndex="id"
|
||||||
key="id"
|
key="id"
|
||||||
width={100}
|
width={150}
|
||||||
render={(_, record: any) => (
|
render={(_, record: any) => (
|
||||||
<>
|
<>
|
||||||
<span>{getFinishedHours(records[record.id])}</span> /{" "}
|
<span>{getFinishedHours(records[record.id])}</span> /{" "}
|
||||||
|
@ -58,6 +58,7 @@ const MemberImportPage = () => {
|
|||||||
user
|
user
|
||||||
.storeBatch(2, data)
|
.storeBatch(2, data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
setErrorData([]);
|
||||||
message.success("导入成功!");
|
message.success("导入成功!");
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
})
|
})
|
||||||
@ -92,9 +93,9 @@ const MemberImportPage = () => {
|
|||||||
{errorData &&
|
{errorData &&
|
||||||
errorData.map((item: any, index: number) => {
|
errorData.map((item: any, index: number) => {
|
||||||
return (
|
return (
|
||||||
<span key={index} className="c-red mb-10">
|
<div key={index} className="c-red mb-10">
|
||||||
{item}
|
{item}
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -142,7 +142,7 @@ const MemberPage = () => {
|
|||||||
<Space size="small">
|
<Space size="small">
|
||||||
<Link
|
<Link
|
||||||
style={{ textDecoration: "none" }}
|
style={{ textDecoration: "none" }}
|
||||||
to={`/member/learn?id=${record.id}`}
|
to={`/member/learn?id=${record.id}&name=${record.name}`}
|
||||||
>
|
>
|
||||||
<PerButton
|
<PerButton
|
||||||
type="link"
|
type="link"
|
||||||
@ -320,6 +320,7 @@ const MemberPage = () => {
|
|||||||
}}
|
}}
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入姓名关键字"
|
placeholder="请输入姓名关键字"
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex mr-24">
|
<div className="d-flex mr-24">
|
||||||
@ -331,6 +332,7 @@ const MemberPage = () => {
|
|||||||
}}
|
}}
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入邮箱账号"
|
placeholder="请输入邮箱账号"
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
@ -359,6 +361,7 @@ const MemberPage = () => {
|
|||||||
/>
|
/>
|
||||||
<MemberCreate
|
<MemberCreate
|
||||||
open={createVisible}
|
open={createVisible}
|
||||||
|
depIds={dep_ids}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setCreateVisible(false);
|
setCreateVisible(false);
|
||||||
setRefresh(!refresh);
|
setRefresh(!refresh);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import styles from "./learn.module.less";
|
import styles from "./learn.module.less";
|
||||||
import { Row, Image, Table } from "antd";
|
import { Row, Image, Table, Button, Select } from "antd";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { BackBartment, DurationText } from "../../compenents";
|
import { BackBartment, DurationText } from "../../compenents";
|
||||||
import { dateFormat } from "../../utils/index";
|
import { dateFormat } from "../../utils/index";
|
||||||
import { user as member } from "../../api/index";
|
import { user as member } from "../../api/index";
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import type { ColumnsType } from "antd/es/table";
|
import type { ColumnsType } from "antd/es/table";
|
||||||
import { duration } from "moment";
|
import { MemberLearnProgressDialog } from "./compenents/progress";
|
||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
id: React.Key;
|
id: React.Key;
|
||||||
@ -21,22 +21,29 @@ interface DataType {
|
|||||||
|
|
||||||
const MemberLearnPage = () => {
|
const MemberLearnPage = () => {
|
||||||
let chartRef = useRef(null);
|
let chartRef = useRef(null);
|
||||||
|
const navigate = useNavigate();
|
||||||
const result = new URLSearchParams(useLocation().search);
|
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 [loading2, setLoading2] = useState<boolean>(false);
|
||||||
const [page2, setPage2] = useState(1);
|
|
||||||
const [size2, setSize2] = useState(10);
|
|
||||||
const [list2, setList2] = useState<any>([]);
|
const [list2, setList2] = useState<any>([]);
|
||||||
const [courses, setCourses] = useState<any>({});
|
const [courses, setCourses] = useState<any>({});
|
||||||
|
const [deps, setDeps] = useState<any>([]);
|
||||||
|
const [depValue, setDepValue] = useState<number>(0);
|
||||||
|
const [currentCourses, setCurrentCourses] = useState<any>([]);
|
||||||
|
const [openCourses, setOpenCourses] = useState<any>([]);
|
||||||
|
const [records, setRecords] = useState<any>({});
|
||||||
const [total2, setTotal2] = useState(0);
|
const [total2, setTotal2] = useState(0);
|
||||||
const [refresh2, setRefresh2] = useState(false);
|
const [refresh2, setRefresh2] = useState(false);
|
||||||
const [uid, setUid] = useState(Number(result.get("id")));
|
const [uid, setUid] = useState(Number(result.get("id")));
|
||||||
|
const [userName, setUserName] = useState<string>(String(result.get("name")));
|
||||||
|
const [visiable, setVisiable] = useState(false);
|
||||||
|
const [courseId, setcourseId] = useState<number>(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setUid(Number(result.get("id")));
|
||||||
|
setUserName(String(result.get("name")));
|
||||||
|
setLoading2(false);
|
||||||
|
setRefresh2(!refresh2);
|
||||||
|
}, [result.get("id"), result.get("name")]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getZxtData();
|
getZxtData();
|
||||||
@ -46,12 +53,22 @@ const MemberLearnPage = () => {
|
|||||||
}, [uid]);
|
}, [uid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getLearnHours();
|
getLearnCourses();
|
||||||
}, [refresh, page, size]);
|
}, [refresh2, uid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getLearnCourses();
|
if (depValue === 0) {
|
||||||
}, [refresh2, page2, size2]);
|
return;
|
||||||
|
}
|
||||||
|
let arr = [...courses[depValue]];
|
||||||
|
let arr2 = [...openCourses];
|
||||||
|
if (arr2.length > 0) {
|
||||||
|
var data = arr.concat(arr2);
|
||||||
|
setCurrentCourses(data);
|
||||||
|
} else {
|
||||||
|
setCurrentCourses(arr);
|
||||||
|
}
|
||||||
|
}, [depValue]);
|
||||||
|
|
||||||
const getZxtData = () => {
|
const getZxtData = () => {
|
||||||
member.learnStats(uid).then((res: any) => {
|
member.learnStats(uid).then((res: any) => {
|
||||||
@ -127,125 +144,34 @@ const MemberLearnPage = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = () => {
|
const getLearnCourses = () => {
|
||||||
if (loading2) {
|
if (loading2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoading2(true);
|
setLoading2(true);
|
||||||
member
|
member.learnAllCourses(uid).then((res: any) => {
|
||||||
.learnCourses(uid, page2, size2, {
|
setList2(res.data.departments);
|
||||||
sort_field: "",
|
setCourses(res.data.dep_courses);
|
||||||
sort_algo: "",
|
setOpenCourses(res.data.open_courses);
|
||||||
is_finished: "",
|
setRecords(res.data.user_course_records);
|
||||||
})
|
if (res.data.departments.length > 0) {
|
||||||
.then((res: any) => {
|
let box: any = [];
|
||||||
setList2(res.data.data);
|
res.data.departments.map((item: any) => {
|
||||||
setCourses(res.data.courses);
|
box.push({
|
||||||
setTotal2(res.data.total);
|
label: item.name,
|
||||||
setLoading2(false);
|
value: String(item.id),
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
setDepValue(Number(box[0].value));
|
||||||
|
setDeps(box);
|
||||||
|
} else {
|
||||||
|
setDepValue(0);
|
||||||
|
setDeps([]);
|
||||||
|
}
|
||||||
|
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> = [
|
const column2: ColumnsType<DataType> = [
|
||||||
{
|
{
|
||||||
title: "课程名称",
|
title: "课程名称",
|
||||||
@ -253,13 +179,13 @@ const MemberLearnPage = () => {
|
|||||||
render: (_, record: any) => (
|
render: (_, record: any) => (
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<Image
|
<Image
|
||||||
src={courses[record.course_id].thumb}
|
src={record.thumb}
|
||||||
preview={false}
|
preview={false}
|
||||||
width={80}
|
width={80}
|
||||||
height={60}
|
height={60}
|
||||||
style={{ borderRadius: 6 }}
|
style={{ borderRadius: 6 }}
|
||||||
/>
|
/>
|
||||||
<span className="ml-8">{courses[record.course_id].title}</span>
|
<span className="ml-8">{record.title}</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -269,7 +195,9 @@ const MemberLearnPage = () => {
|
|||||||
render: (_, record: any) => (
|
render: (_, record: any) => (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
已完成课时:{record.finished_count} / {record.hour_count}
|
已完成课时:
|
||||||
|
{(records[record.id] && records[record.id].finished_count) ||
|
||||||
|
0} / {record.class_hour}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
@ -277,38 +205,93 @@ const MemberLearnPage = () => {
|
|||||||
{
|
{
|
||||||
title: "第一次学习时间",
|
title: "第一次学习时间",
|
||||||
dataIndex: "created_at",
|
dataIndex: "created_at",
|
||||||
render: (text: string) => <span>{dateFormat(text)}</span>,
|
render: (_, record: any) => (
|
||||||
|
<>
|
||||||
|
{records[record.id] ? (
|
||||||
|
<span>{dateFormat(records[record.id].created_at)}</span>
|
||||||
|
) : (
|
||||||
|
<span>-</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "学习完成时间",
|
title: "学习完成时间",
|
||||||
dataIndex: "finished_at",
|
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: "学习进度",
|
title: "学习进度",
|
||||||
dataIndex: "is_finished",
|
dataIndex: "is_finished",
|
||||||
render: (_, record: any) => (
|
render: (_, record: any) => (
|
||||||
<>
|
<>
|
||||||
<span
|
{records[record.id] ? (
|
||||||
className={
|
<span
|
||||||
Math.floor((record.finished_count / record.hour_count) * 100) >=
|
className={
|
||||||
100
|
Math.floor(
|
||||||
? "c-green"
|
(records[record.id].finished_count /
|
||||||
: "c-red"
|
records[record.id].hour_count) *
|
||||||
}
|
100
|
||||||
>
|
) >= 100
|
||||||
{Math.floor((record.finished_count / record.hour_count) * 100)}%
|
? "c-green"
|
||||||
</span>
|
: "c-red"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{Math.floor(
|
||||||
|
(records[record.id].finished_count /
|
||||||
|
records[record.id].hour_count) *
|
||||||
|
100
|
||||||
|
)}
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="c-red">0%</span>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
key: "action",
|
||||||
|
fixed: "right",
|
||||||
|
width: 100,
|
||||||
|
render: (_, record: any) => (
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
className="b-link c-red"
|
||||||
|
onClick={() => {
|
||||||
|
setcourseId(record.id);
|
||||||
|
setVisiable(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
明细
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row className="playedu-main-top mb-24">
|
<Row className="playedu-main-top mb-24">
|
||||||
|
<MemberLearnProgressDialog
|
||||||
|
open={visiable}
|
||||||
|
uid={uid}
|
||||||
|
id={courseId}
|
||||||
|
onCancel={() => {
|
||||||
|
setVisiable(false);
|
||||||
|
setRefresh2(!refresh2);
|
||||||
|
}}
|
||||||
|
></MemberLearnProgressDialog>
|
||||||
<div className="float-left mb-24">
|
<div className="float-left mb-24">
|
||||||
<BackBartment title="学员学习" />
|
<BackBartment title={userName + "的学习明细"} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["charts"]}>
|
<div className={styles["charts"]}>
|
||||||
<div
|
<div
|
||||||
@ -321,27 +304,28 @@ const MemberLearnPage = () => {
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="float-left mt-24">
|
<div className="float-left mt-24">
|
||||||
|
{list2.length > 1 && (
|
||||||
|
<div className="d-flex mb-24">
|
||||||
|
<span>切换部门:</span>
|
||||||
|
<Select
|
||||||
|
style={{ width: 160 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请选择部门"
|
||||||
|
value={String(depValue)}
|
||||||
|
onChange={(value: string) => setDepValue(Number(value))}
|
||||||
|
options={deps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Table
|
<Table
|
||||||
columns={column2}
|
columns={column2}
|
||||||
dataSource={list2}
|
dataSource={currentCourses}
|
||||||
loading={loading2}
|
loading={loading2}
|
||||||
pagination={paginationProps2}
|
pagination={false}
|
||||||
rowKey={(record) => record.id}
|
rowKey={(record) => record.id}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</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> */}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -147,7 +147,11 @@ export const ResourceCategoryCreate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入分类名称!" }]}
|
rules={[{ required: true, message: "请输入分类名称!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入分类名称" />
|
<Input
|
||||||
|
style={{ width: 200 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入分类名称"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -169,7 +169,11 @@ export const ResourceCategoryUpdate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入分类名称!" }]}
|
rules={[{ required: true, message: "请输入分类名称!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入分类名称" />
|
<Input
|
||||||
|
style={{ width: 200 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入分类名称"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,14 +125,22 @@ export const SystemAdministratorCreate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入管理员姓名!" }]}
|
rules={[{ required: true, message: "请输入管理员姓名!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入管理员姓名" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="请输入管理员姓名"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="邮箱"
|
label="邮箱"
|
||||||
name="email"
|
name="email"
|
||||||
rules={[{ required: true, message: "请输入学员邮箱!" }]}
|
rules={[{ required: true, message: "请输入学员邮箱!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入学员邮箱" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="请输入学员邮箱"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="密码"
|
label="密码"
|
||||||
@ -140,6 +148,7 @@ export const SystemAdministratorCreate: React.FC<PropInterface> = ({
|
|||||||
rules={[{ required: true, message: "请输入登录密码!" }]}
|
rules={[{ required: true, message: "请输入登录密码!" }]}
|
||||||
>
|
>
|
||||||
<Input.Password
|
<Input.Password
|
||||||
|
allowClear
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
placeholder="请输入登录密码"
|
placeholder="请输入登录密码"
|
||||||
/>
|
/>
|
||||||
|
@ -133,18 +133,27 @@ export const SystemAdministratorUpdate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入管理员姓名!" }]}
|
rules={[{ required: true, message: "请输入管理员姓名!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入管理员姓名" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="请输入管理员姓名"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="邮箱"
|
label="邮箱"
|
||||||
name="email"
|
name="email"
|
||||||
rules={[{ required: true, message: "请输入学员邮箱!" }]}
|
rules={[{ required: true, message: "请输入学员邮箱!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 200 }} placeholder="请输入学员邮箱" />
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="请输入学员邮箱"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="密码" name="password">
|
<Form.Item label="密码" name="password">
|
||||||
<Input.Password
|
<Input.Password
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
|
allowClear
|
||||||
placeholder="请输入登录密码"
|
placeholder="请输入登录密码"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@ -283,6 +283,7 @@ const SystemAdministratorPage = () => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setName(e.target.value);
|
setName(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
allowClear
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
placeholder="请输入管理员姓名"
|
placeholder="请输入管理员姓名"
|
||||||
/>
|
/>
|
||||||
|
@ -180,6 +180,7 @@ export const SystemAdminrolesCreate: React.FC<PropInterface> = ({
|
|||||||
<Input
|
<Input
|
||||||
style={{ width: 424 }}
|
style={{ width: 424 }}
|
||||||
placeholder="请在此处输入角色名称"
|
placeholder="请在此处输入角色名称"
|
||||||
|
allowClear
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="操作权限" name="action_ids">
|
<Form.Item label="操作权限" name="action_ids">
|
||||||
|
@ -191,7 +191,11 @@ export const SystemAdminrolesUpdate: React.FC<PropInterface> = ({
|
|||||||
name="name"
|
name="name"
|
||||||
rules={[{ required: true, message: "请输入角色名!" }]}
|
rules={[{ required: true, message: "请输入角色名!" }]}
|
||||||
>
|
>
|
||||||
<Input style={{ width: 424 }} placeholder="请输入角色名" />
|
<Input
|
||||||
|
style={{ width: 424 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入角色名"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="操作权限" name="action_ids">
|
<Form.Item label="操作权限" name="action_ids">
|
||||||
<TreeSelect
|
<TreeSelect
|
||||||
|
@ -31,7 +31,7 @@ const SystemConfigPage = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDetail();
|
getDetail();
|
||||||
}, []);
|
}, [tabKey]);
|
||||||
|
|
||||||
const getDetail = () => {
|
const getDetail = () => {
|
||||||
appConfig.appConfig().then((res: any) => {
|
appConfig.appConfig().then((res: any) => {
|
||||||
@ -244,14 +244,22 @@ const SystemConfigPage = () => {
|
|||||||
label="网站标题"
|
label="网站标题"
|
||||||
name="system.name"
|
name="system.name"
|
||||||
>
|
>
|
||||||
<Input style={{ width: 274 }} placeholder="请填写网站标题" />
|
<Input
|
||||||
|
style={{ width: 274 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请填写网站标题"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
style={{ marginBottom: 30 }}
|
style={{ marginBottom: 30 }}
|
||||||
label="网站页脚"
|
label="网站页脚"
|
||||||
name="system.pc_index_footer_msg"
|
name="system.pc_index_footer_msg"
|
||||||
>
|
>
|
||||||
<Input style={{ width: 274 }} placeholder="请填写网站页脚" />
|
<Input
|
||||||
|
style={{ width: 274 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="请填写网站页脚"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{/* <Form.Item
|
{/* <Form.Item
|
||||||
style={{ marginBottom: 30 }}
|
style={{ marginBottom: 30 }}
|
||||||
@ -315,7 +323,11 @@ const SystemConfigPage = () => {
|
|||||||
<Form.Item style={{ marginBottom: 30 }} label="跑马灯内容">
|
<Form.Item style={{ marginBottom: 30 }} label="跑马灯内容">
|
||||||
<Space align="baseline" style={{ height: 32 }}>
|
<Space align="baseline" style={{ height: 32 }}>
|
||||||
<Form.Item name="player.bullet_secret_text">
|
<Form.Item name="player.bullet_secret_text">
|
||||||
<Input style={{ width: 274 }} placeholder="自定义跑马灯内容" />
|
<Input
|
||||||
|
style={{ width: 274 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="自定义跑马灯内容"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={nameChecked}
|
checked={nameChecked}
|
||||||
|
@ -4,6 +4,7 @@ import { login, system } from "../api";
|
|||||||
|
|
||||||
import InitPage from "../pages/init";
|
import InitPage from "../pages/init";
|
||||||
import { getToken } from "../utils";
|
import { getToken } from "../utils";
|
||||||
|
import KeepAlive from "../compenents/keep-alive";
|
||||||
|
|
||||||
import LoginPage from "../pages/login";
|
import LoginPage from "../pages/login";
|
||||||
import HomePage from "../pages/home";
|
import HomePage from "../pages/home";
|
||||||
@ -94,19 +95,22 @@ const routes: RouteObject[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/member",
|
path: "/member",
|
||||||
element: <MemberPage />,
|
element: <KeepAlive />,
|
||||||
},
|
children: [
|
||||||
{
|
{ path: "/member/index", element: <MemberPage /> },
|
||||||
path: "/member/import",
|
{
|
||||||
element: <MemberImportPage />,
|
path: "/member/import",
|
||||||
},
|
element: <MemberImportPage />,
|
||||||
{
|
},
|
||||||
path: "/member/learn",
|
{
|
||||||
element: <MemberLearnPage />,
|
path: "/member/learn",
|
||||||
},
|
element: <MemberLearnPage />,
|
||||||
{
|
},
|
||||||
path: "/member/departmentUser",
|
{
|
||||||
element: <MemberDepartmentProgressPage />,
|
path: "/member/departmentUser",
|
||||||
|
element: <MemberDepartmentProgressPage />,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/system/config/index",
|
path: "/system/config/index",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user