This commit is contained in:
禺狨 2023-06-26 17:53:21 +08:00
parent f862bd5b85
commit 994b973dab
8 changed files with 578 additions and 18 deletions

View File

@ -6,4 +6,5 @@
color: rgba(0, 0, 0, 0.3);
line-height: 12px;
margin: 30px 0px;
text-align: center;
}

View File

@ -82,3 +82,32 @@ code {
.adm-progress-bar-text {
color: #ff4d4f !important;
}
.adm-tabs-header {
border-bottom: none !important;
}
.adm-tabs-tab {
color: rgba(0, 0, 0, 0.45);
}
.adm-tabs-tab-active {
font-weight: 500;
color: rgba(0, 0, 0, 0.88);
}
.adm-dropdown-item {
justify-content: left !important;
.adm-dropdown-item-title {
padding: 8px 0px !important;
.adm-dropdown-item-title-text {
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.45);
}
}
}
.adm-popup-body {
border-radius: 0px 0px 16px 16px;
}

View File

@ -0,0 +1,67 @@
.item {
width: 100%;
height: 75px;
display: flex;
flex-direction: row;
align-items: center;
.info {
flex: 1;
height: 75px;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
width: 100%;
height: 42px;
font-size: 15px;
font-weight: 400;
color: rgba(0, 0, 0, 0.88);
line-height: 21px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.status-content {
width: 100%;
display: flex;
align-items: center;
.type {
width: 46px;
height: 24px;
background: rgba(255, 77, 79, 0.1);
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #ff4d4f;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
}
.active-type {
width: 46px;
height: 24px;
background: rgba(#ff9900, 0.1);
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #ff9900;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
}
.no-pro {
height: 12px;
font-size: 12px;
font-weight: 400;
color: rgba(0, 0, 0, 0.45);
line-height: 12px;
margin-left: 10px;
}
}
}
}

View File

@ -0,0 +1,86 @@
import React from "react";
import { Image, ProgressBar } from "antd-mobile";
import { useNavigate } from "react-router-dom";
import styles from "./courses-model.module.scss";
import mediaIcon from "../../../assets/images/commen/icon-medal.png";
interface PropInterface {
id: number;
title: string;
thumb: string;
isRequired: number;
progress: number;
}
export const CoursesModel: React.FC<PropInterface> = ({
id,
title,
thumb,
isRequired,
progress,
}) => {
const navigate = useNavigate();
return (
<div
className={styles["item"]}
onClick={() => {
navigate(`/course/${id}`);
}}
>
<Image
width={100}
height={75}
style={{ borderRadius: 8, marginRight: 15 }}
src={thumb}
/>
<div className={styles["info"]}>
<div className={styles["title"]}>{title}</div>
<div className={styles["status-content"]}>
{isRequired === 1 && <div className={styles["type"]}></div>}
{isRequired === 0 && (
<div className={styles["active-type"]}></div>
)}
{progress == 0 && (
<>
<ProgressBar
percent={0}
style={{
flex: 1,
"--fill-color": "#FF4D4F",
"--track-color": "#F6F6F6",
"--track-width": "8px",
"--text-width": "27px",
}}
/>
<span className={styles["no-pro"]}></span>
</>
)}
{progress > 0 && progress < 100 && (
<ProgressBar
percent={progress}
text
style={{
flex: 1,
"--fill-color": "#FF4D4F",
"--track-color": "#F6F6F6",
"--track-width": "8px",
"--text-width": "27px",
}}
/>
)}
{progress >= 100 && (
<div className={styles["success"]}>
<Image
width={20}
height={20}
src={mediaIcon}
style={{ marginRight: 5 }}
/>
<span>!</span>
</div>
)}
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,133 @@
.tabs-box {
width: 100%;
float: left;
height: 40px;
background-color: #ffffff;
box-sizing: border-box;
padding: 0px 8px;
position: fixed;
top: 0;
z-index: 10;
}
.category-box {
width: 100%;
float: left;
height: auto;
box-sizing: border-box;
padding: 20px;
}
.category-content {
width: 100%;
float: left;
height: auto;
background-color: #ffffff;
box-sizing: border-box;
padding: 20px 20px 0px 20px;
text-align: left;
position: fixed;
top: 40px;
z-index: 10;
}
.category-box {
width: 100%;
height: 330px;
box-sizing: border-box;
padding: 10px 20px 20px 20px;
overflow-x: hidden;
overflow-y: auto;
.category-item {
width: 100%;
float: left;
height: auto;
.category-tit {
width: 100%;
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.88);
line-height: 30px;
margin-bottom: 15px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.active-category-item {
width: 100%;
float: left;
height: auto;
.category-tit {
width: 100%;
font-size: 14px;
font-weight: 400;
color: #ff4d4f;
line-height: 30px;
margin-bottom: 15px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.child-item {
width: 100%;
float: left;
height: auto;
box-sizing: border-box;
padding-left: 20px;
.category-tit {
width: 100%;
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.88);
line-height: 30px;
margin-bottom: 15px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.active-child-item {
width: 100%;
float: left;
height: auto;
box-sizing: border-box;
padding-left: 20px;
.category-tit {
width: 100%;
font-size: 14px;
font-weight: 400;
color: #ff4d4f;
line-height: 30px;
margin-bottom: 15px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.list-box {
width: 100%;
height: auto;
box-sizing: border-box;
padding: 96px 20px 55px 20px;
text-align: left;
overflow-x: hidden;
overflow-y: auto;
.item {
width: 100%;
height: auto;
box-sizing: border-box;
padding-top: 30px;
padding-bottom: 30px;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:first-child {
padding-top: 20px;
}
}
}

View File

@ -1,29 +1,280 @@
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { Dropdown, SpinLoading, Tabs } from "antd-mobile";
import { DropdownRef } from "antd-mobile/es/components/dropdown";
import { user } from "../../api/index";
import styles from "./index.module.scss";
import { useSelector } from "react-redux";
import { Footer, TabBarFooter } from "../../components";
import { Footer, TabBarFooter, Empty } from "../../components";
import { CoursesModel } from "./compenents/courses-model";
const IndexPage = () => {
const ref = useRef<DropdownRef>(null);
const systemConfig = useSelector((state: any) => state.systemConfig.value);
const [loading, setLoading] = useState<boolean>(false);
const [tabKey, setTabKey] = useState(0);
const departments = useSelector(
(state: any) => state.loginUser.value.departments
);
const [tabKey, setTabKey] = useState("0");
const [coursesList, setCoursesList] = useState<any>([]);
const [categories, setCategories] = useState<any>([]);
const [categoryId, setCategoryId] = useState<number>(0);
const [categoryText, setCategoryText] = useState<string>("所有分类");
const [learnCourseRecords, setLearnCourseRecords] = useState<any>({});
const [learnCourseHourCount, setLearnCourseHourCount] = useState<any>({});
const currentDepId = useSelector(
(state: any) => state.loginUser.value.currentDepId
);
const items = [
{
key: "0",
label: `全部`,
},
{
key: "1",
label: `必修课`,
},
{
key: "2",
label: `选修课`,
},
{
key: "3",
label: `已学完`,
},
{
key: "4",
label: `未学完`,
},
];
useEffect(() => {
document.title = systemConfig.systemName || "首页";
}, [systemConfig]);
useEffect(() => {
getParams();
}, []);
useEffect(() => {
if (currentDepId === 0) {
return;
}
getData();
}, [tabKey, currentDepId, categoryId]);
const getData = () => {
setLoading(true);
user.courses(currentDepId, categoryId).then((res: any) => {
const records = res.data.learn_course_records;
setLearnCourseRecords(records);
setLearnCourseHourCount(res.data.user_course_hour_count);
if (Number(tabKey) === 0) {
setCoursesList(res.data.courses);
} else if (Number(tabKey) === 1) {
const arr: any = [];
res.data.courses.map((item: any) => {
if (item.is_required === 1) {
arr.push(item);
}
});
setCoursesList(arr);
} else if (Number(tabKey) === 2) {
const arr: any = [];
res.data.courses.map((item: any) => {
if (item.is_required === 0) {
arr.push(item);
}
});
setCoursesList(arr);
} else if (Number(tabKey) === 3) {
const arr: any = [];
res.data.courses.map((item: any) => {
if (records[item.id] && records[item.id].progress >= 10000) {
arr.push(item);
}
});
setCoursesList(arr);
} else if (Number(tabKey) === 4) {
const arr: any = [];
res.data.courses.map((item: any) => {
if (
!records[item.id] ||
(records[item.id] && records[item.id].progress < 10000)
) {
arr.push(item);
}
});
setCoursesList(arr);
}
setLoading(false);
});
};
const getParams = () => {
user.coursesCategories().then((res: any) => {
const categories = res.data.categories;
if (JSON.stringify(categories) !== "{}") {
const new_arr: any[] = checkArr(categories, 0);
new_arr.unshift({
key: 0,
title: "所有分类",
});
setCategories(new_arr);
}
});
};
const checkArr = (categories: any[], id: number) => {
const arr = [];
for (let i = 0; i < categories[id].length; i++) {
if (!categories[categories[id][i].id]) {
arr.push({
title: categories[id][i].name,
key: categories[id][i].id,
});
} else {
const new_arr: any[] = checkArr(categories, categories[id][i].id);
arr.push({
title: categories[id][i].name,
key: categories[id][i].id,
children: new_arr,
});
}
}
return arr;
};
const renderChildCategory = (data: any) => {
return (
<>
{data.map((item: any) => (
<div
key={item.key}
className={
item.key === categoryId
? styles["active-child-item"]
: styles["child-item"]
}
>
<div
className={styles["category-tit"]}
onClick={() => {
setCategoryId(item.key);
setCategoryText(item.title);
ref.current?.close();
}}
>
{item.title}
</div>
{item.children &&
item.children.length > 0 &&
renderChildCategory(item.children)}
</div>
))}
</>
);
};
return (
<div className="main-body">
<div className="content">
<Footer></Footer>
<div className={styles["tabs-box"]}>
<Tabs
activeKey={tabKey}
onChange={(key: any) => {
setTabKey(key);
}}
style={{
"--fixed-active-line-width": "20px",
"--active-line-height": "3px",
"--active-title-color": "rgba(0,0,0,0.88)",
"--title-font-size": "16px",
}}
>
{items.map((item) => (
<Tabs.Tab title={item.label} key={item.key} />
))}
</Tabs>
</div>
<div className={styles["category-content"]}>
<Dropdown ref={ref}>
<Dropdown.Item key="sorter" title={categoryText}>
<div className={styles["category-box"]}>
{categories.map((item: any) => (
<div
key={item.key}
className={
item.key === categoryId
? styles["active-category-item"]
: styles["category-item"]
}
>
<div
className={styles["category-tit"]}
onClick={() => {
setCategoryId(item.key);
setCategoryText(item.title);
ref.current?.close();
}}
>
{item.title}
</div>
{item.children &&
item.children.length > 0 &&
renderChildCategory(item.children)}
</div>
))}
</div>
</Dropdown.Item>
</Dropdown>
</div>
<div className={styles["list-box"]}>
{loading && (
<div
style={{ width: "100%", display: "flex", justifyContent: "center" }}
>
<SpinLoading color="primary" />
</div>
)}
{!loading && coursesList.length === 0 && <Empty></Empty>}
{!loading && coursesList.length > 0 && (
<>
{coursesList.map((item: any) => (
<div className={styles["item"]} key={item.id}>
{learnCourseRecords[item.id] && (
<CoursesModel
id={item.id}
title={item.title}
thumb={item.thumb}
isRequired={item.is_required}
progress={Math.floor(
learnCourseRecords[item.id].progress / 100
)}
></CoursesModel>
)}
{!learnCourseRecords[item.id] &&
learnCourseHourCount[item.id] &&
learnCourseHourCount[item.id] > 0 && (
<CoursesModel
id={item.id}
title={item.title}
thumb={item.thumb}
isRequired={item.is_required}
progress={1}
></CoursesModel>
)}
{!learnCourseRecords[item.id] &&
!learnCourseHourCount[item.id] && (
<CoursesModel
id={item.id}
title={item.title}
thumb={item.thumb}
isRequired={item.is_required}
progress={0}
></CoursesModel>
)}
</div>
))}
<Footer></Footer>
</>
)}
</div>
<TabBarFooter></TabBarFooter>
</div>

View File

@ -30,7 +30,7 @@
height: 75px;
display: flex;
align-items: center;
margin-bottom: 20px;
margin-bottom: 30px;
.item-info {
flex: 1;
height: 75px;

View File

@ -1,12 +1,5 @@
import { useState, useEffect } from "react";
import {
Button,
Toast,
Input,
Image,
ProgressBar,
SpinLoading,
} from "antd-mobile";
import { Image, ProgressBar, SpinLoading } from "antd-mobile";
import styles from "./index.module.scss";
import { useNavigate } from "react-router-dom";
import { course } from "../../api/index";