课时记录

main
king 1 year ago
parent ed9599ac8b
commit 33eaa1adcb
  1. 4
      .env
  2. 14
      src/api/course.ts
  3. 1
      src/api/index.ts
  4. 7
      src/api/manage.ts
  5. 4
      src/api/user.ts
  6. 6
      src/components/custom-page-container/custom-page-container.tsx
  7. 31
      src/pages/business/curHistory/curHistory.tsx
  8. 2
      src/pages/business/history/history.tsx
  9. 57
      src/pages/business/hourHistory/hourHistory.module.scss
  10. 90
      src/pages/business/hourHistory/hourHistory.tsx
  11. 12
      src/pages/business/videoInfo/components/catalogue.tsx
  12. 3
      src/pages/business/videoInfo/videoInfo.tsx
  13. 8
      src/pages/manage/addStudent/addStudent.tsx
  14. 4
      src/pages/manage/depAdmin/depAdmin.tsx
  15. 4
      types/user.d.ts

@ -1,4 +1,4 @@
#TARO_APP_API=https://yjx.dev.yaojiankang.top
TARO_APP_API=https://playedu.yaojiankang.top
TARO_APP_API=https://yjx.dev.yaojiankang.top
#TARO_APP_API=https://playedu.yaojiankang.top
TARO_APP_LGOIN=true

@ -0,0 +1,14 @@
import {request} from "@/api/request";
interface HourHistorys {
course: Curriculum
data: Record<number, HourHistory[]>
hour: Hour
}
export const courseApi = {
/** 课时学习记录 */
hourHistory(courseId: string, hourId: string) {
return request<HourHistorys>(`/api/v1/course/${courseId}/hour/${hourId}/info`, "GET")
}
}

@ -4,3 +4,4 @@ export * from './login'
export * from './meeting'
export * from './public'
export * from './manage'
export * from './course'

@ -117,11 +117,4 @@ export const ManageApi = {
buy(data_list: number[]) {
return request(`/api/v1/course/buy?data_list=${data_list}`, "POST")
},
/** 学员学习记录 */
userRecord(curId: number) {
return request<CurLearningRecord>(`/api/v1/course/${curId}/user/index?page=1&size=10000`, "GET")
},
offline(data: Offline) {
return request('/wechat/link', "GET", data)
}
}

@ -37,7 +37,7 @@ interface LearningRecord {
interface CourseRecord {
course: Curriculum
data: Record<number, userRecord>
data: Record<number, HourHistory>
}
interface HourCourse {
@ -71,7 +71,7 @@ export const userApi = {
},
/** 学习记录 */
record(category_id: number) {
return request<Record<number, userRecord>>(`/api/v1/course/${category_id}/record`, "GET")
return request<Record<number, HourHistory>>(`/api/v1/course/${category_id}/record`, "GET")
},
courseRecord(course_id: string) {
return request<CourseRecord>(`/api/v1/course/${course_id}/info`, "GET")

@ -1,6 +1,7 @@
import {PageContainer, PageContainerProps, View} from "@tarojs/components";
import {CSSProperties, FC, useCallback, useEffect, useState} from "react";
import styles from './custom-page-container.module.scss'
import Taro from "@tarojs/taro";
const PageContainerInner: FC<PageContainerProps> = (props) => {
const [visible, setVisible] = useState(props.show)
@ -55,6 +56,11 @@ const PageContainerInner: FC<PageContainerProps> = (props) => {
}
}, [props.show])
Taro.useDidHide(() => {
props.onClickOverlay?.(null as any)
setVisible(false)
})
return (
<View
className={styles.customPageContainer}

@ -5,30 +5,35 @@ import {useState} from "react";
import {userApi} from "@/api";
import {formatDateTime, formatMinute} from "@/utils/time";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
import Empty from "@/components/empty/empty";
const CurHistory = () => {
const [show, setShow] = useState(false)
const {course_id} = Taro.getCurrentInstance().router?.params as unknown as { course_id: string }
const [data, setData] = useState<userRecord[]>([])
const [data, setData] = useState<HourHistory[]>([])
const [course, setCourse] = useState<Curriculum | null>(null)
const [hours, setHours] = useState<Hour[] | null>(null)
const [durations, setDuration] = useState<Record<number, number> | null>(null)
const [time, setTime] = useState<Record<number, { start: number, end: number }> | null>(null)
Taro.useLoad(() => {
// Taro.setNavigationBarTitle({title: name})
userApi.courseRecord(course_id).then(res => {
Taro.setNavigationBarTitle({title: res.course.title})
setData(Object.values(res.data))
setCourse(res.course)
})
})
async function setHour(unique_ident: number) {
try {
const res = await userApi.hourCourse(course_id, unique_ident)
setHours(Object.values(res.courseHour))
setDuration(res.duration)
setTime(res.date)
setShow(true)
} catch (e) {
}
}
function percent(duration: number): number {
@ -64,8 +69,13 @@ const CurHistory = () => {
</View>
<View className={styles.record}>
{data.map((d, index) => <View key={d.id} onClick={() => setHour(d.unique_ident)} className={styles.recordItem}>
{
data.length ? <View className={styles.record}>
{
data.map((d, index) => <View
key={d.id}
onClick={() => setHour(d.unique_ident)}
className={styles.recordItem}>
<View className='text-hover-dark'>{index + 1}</View>
<View>{getCurTime(d.start_at, d.end_at)} </View>
<View>{formatMinute(d.duration)}</View>
@ -80,8 +90,12 @@ const CurHistory = () => {
color='#45D4A8'
activeColor='#45D4A8'
/>
</View>)}
</View>)
}
</View>
: <Empty name='暂无学习记录'/>
}
<CustomPageContainer
show={show}
@ -89,7 +103,8 @@ const CurHistory = () => {
position='bottom'
onClickOverlay={() => setShow(false)}>
<View className={styles.hourRecord}>
{hours?.map(d => <View key={d.id}>
{hours?.length ?
hours?.map(d => <View key={d.id}>
<View className={styles.recordItem}>
<View>{d.title}</View>
<View>{getTime(d.id)}</View>
@ -104,8 +119,8 @@ const CurHistory = () => {
activeColor={'#45D4A8'}
/>
</View>
</View>
)}
</View>)
: <Empty name='暂无学习记录'/>}
</View>
</CustomPageContainer>
</View>

@ -8,7 +8,7 @@ import {formatMinute} from "@/utils/time";
import Empty from "@/components/empty/empty";
const History = () => {
const [data, setData] = useState<userRecord[]>([])
const [data, setData] = useState<HourHistory[]>([])
async function getData(id: number) {
const res = await userApi.record(id)

@ -0,0 +1,57 @@
.page {
padding: 30px !important;
box-sizing: border-box;
}
.cur {
background: #fff;
overflow: hidden;
border-radius: 16px;
font-weight: 500;
position: relative;
}
.classHour {
background: rgba(#000, .6);
position: absolute;
top: 250rpx;
width: 100%;
font-size: 25rpx;
line-height: 50rpx;
text-align: center;
color: #fff;
font-weight: 100;
left: 0;
}
.title {
padding: 10rpx;
height: 100rpx;
}
.image {
width: 100%;
height: 300rpx;
display: block;
background: #ddd;
}
.progressBox {
background: #fff;
padding: 20rpx;
margin-top: 30rpx;
border-radius: 30rpx;
}
.recordItem {
padding: 24px 0 38rpx;
border-bottom: 1px solid #F5F8F7;
font-size: 26rpx;
color: #606563;
line-height: 1.75;
}
.progress {
color: #45D4A8;
}

@ -1,9 +1,91 @@
import {View} from "@tarojs/components";
import {FC} from "react";
import {Image, Progress, View} from "@tarojs/components";
import {FC, useEffect, useState} from "react";
import Taro from "@tarojs/taro";
import {courseApi} from "@/api";
import styles from './hourHistory.module.scss'
import Empty from "@/components/empty/empty";
import {formatDateTime} from "@/utils/time";
const HourHistory: FC = () => {
const {courseId, hourId} = Taro.getCurrentInstance().router?.params as { courseId: string, hourId: string }
const [cur, setCur] = useState<Curriculum | null>(null)
const [hour, setHour] = useState<Hour | null>(null)
const [times, setTimes] = useState<HourHistory[]>([])
useEffect(() => {
courseApi.hourHistory(courseId, hourId).then(res => {
if (res) {
setHour(res.hour)
setCur(res.course)
const data = Object.values(res.data).map(d => {
if (d.length === 1) {
return d[0]
}
const duration = d.reduce((pre, cur) => {
return pre + cur.duration
}, 0)
return ({
...d[0],
duration,
end_at: d[d.length - 1].end_at
})
})
setTimes(data)
}
})
}, [])
const HourHistory:FC = () => {
return (
<View>sd</View>
<View className={styles.page}>
<View className={styles.cur}>
<Image className={styles.image} src={cur?.thumb || ''} mode='center'/>
<View className={styles.classHour}>{cur?.class_hour}/{cur?.finished_hour_count || 0}</View>
<View className={styles.title}>
<View>{cur?.title}</View>
<View>{hour?.title}</View>
</View>
</View>
{
times.length ?
<View className={styles.progressBox}>
{
times.map(d => <View key={d.id} className={styles.recordItem}>
<View>{formatDateTime(new Date(d.start_at), 'MM-dd')}</View>
<Progress
className={styles.progress}
percent={d.duration / (hour?.duration || 0)}
showInfo
borderRadius={10}
active
duration={10}
strokeWidth={10}
color='#45D4A8'
activeColor='#45D4A8'
/>
</View>)
}
</View>
: <Empty name='暂无数据'/>
}
{/*{times.length ? times.map((d, index) =>*/}
{/* <View key={index} className={styles.category}>*/}
{/* <View className={styles.thumb}>*/}
{/* <Image src={d.course.thumb} className={styles.image}/>*/}
{/* <View className={styles.count}>共{d.total_hour_count}节/已学{d.finished_count}节</View>*/}
{/* </View>*/}
{/* <View className={styles.text}>*/}
{/* <View>{d.course.title}</View>*/}
{/* <View className={styles.describe}>*/}
{/* <Text className='mr-4'>观看{formatMinute(d.duration)}</Text>*/}
{/* <Text>学习进度:{(d.finished_count / d.total_hour_count * 100).toFixed(0)}%</Text>*/}
{/* </View>*/}
{/* </View>*/}
{/* </View>) : <Empty name='无历史记录'/>}*/}
</View>
)
}

@ -13,7 +13,8 @@ import hourRecord from '@/static/img/hourRecord.png'
interface Props {
data: CourseDepData | null
setHors: (is_complete: boolean, id: number) => void
id: number
id: number //课程
playId: number | null
}
const tabList = [
@ -23,12 +24,12 @@ const tabList = [
]
const Catalogue: FC<Props> = ({data, setHors, id}) => {
const Catalogue: FC<Props> = ({data, setHors, id, playId}) => {
const [current, setCurrent] = useState(1)
const [show, setShow] = useState(false)
function jumCurHistory() {
// Taro.navigateTo({url: `/pages/business/curHistory/curHistory`})
Taro.navigateTo({url: `/pages/business/hourHistory/hourHistory?courseId=${id}&hourId=${playId}`})
}
function tabChange({tab}: OnChangOpt) {
@ -133,10 +134,13 @@ const Catalogue: FC<Props> = ({data, setHors, id}) => {
<Image src={curRecord} className='image'/>
</View>
<View onClick={jumCurHistory}>
{
playId != null && <View onClick={jumCurHistory}>
<Image src={hourRecord} className='image'/>
</View>
}
</View>
<MyButton onClick={() => setShow(false)} type='default' fillet></MyButton>

@ -109,9 +109,8 @@ const VideoInfo: FC = () => {
<Text>{data?.learn_hour_records.length || 0}/{data?.course.class_hour}</Text>
</View>
</View>
<Catalogue data={data} setHors={setHors} id={id}/>
<Catalogue data={data} setHors={setHors} id={id} playId={playId}/>
</View>
</>
)
}

@ -19,7 +19,6 @@ const AddStudent = () => {
const [department, setDepartment] = useState<Department[]>([])
const [depIds, setDepIds] = useState<number[]>([])
const params = getCurrentInstance()?.router?.params as { id?: number }
const [disable, setDisable] = useState(false)
const {company} = Profile.useContainer()
@ -34,12 +33,11 @@ const AddStudent = () => {
const value: Student = event.detail.value
for (const [key, value1] of Object.entries(value)) {
if (!value1 && !['id_card', 'password'].includes(key)) {
Taro.showToast({title: "请填写完整", icon: 'error'})
Taro.showToast({title: "请认真填写", icon: 'error'})
return
}
}
Taro.showLoading()
setDisable(true)
try {
if (params.id) {
await ManageApi.putUser(params.id, {...value, dep_ids: depIds, company_id: company?.id || 0})
@ -54,12 +52,10 @@ const AddStudent = () => {
} catch (e) {
}
Taro.hideLoading()
setDisable(true)
}
const formatDep = useCallback(() => {
console.log(depIds, 12)
const selected = department.filter(d => depIds.includes(d.id)).map(d => d.title)
const top4 = selected.splice(0, 3).join('、')
return top4 + (selected.length ? "+" + selected.length : '')
@ -117,7 +113,7 @@ const AddStudent = () => {
</View>
</View>
<Button className='add button' formType='submit' disabled={disable}></Button>
<Button className='add button' formType='submit'></Button>
</Form>
</View>
)

@ -80,6 +80,8 @@ const DepAdmin: FC = () => {
del(item.name, item.id)
break
}
},
fail() {
}
})
}
@ -126,6 +128,8 @@ const DepAdmin: FC = () => {
setRoleType(user)
break
}
},
fail() {
}
})
}

4
types/user.d.ts vendored

@ -34,6 +34,7 @@ interface Department {
users: null
}
/** 公司 */
interface Company {
id: number
company_name: string
@ -76,7 +77,8 @@ interface CueStats {
}
interface userRecord {
/** 课时学习记录 */
interface HourHistory {
id: number;
duration: number;
course: Curriculum;

Loading…
Cancel
Save