解决不正当操作流程

main
king 2 years ago
parent 38dd48c99e
commit dc1a72ce9a
  1. 4
      src/api/manage.ts
  2. 16
      src/api/user.ts
  3. 16
      src/components/lineChart/lineChart.module.scss
  4. 42
      src/components/lineChart/lineChart.tsx
  5. 11
      src/components/video/video.tsx
  6. 35
      src/pages/business/history/components/CategoryTabs.tsx
  7. 32
      src/pages/business/history/history.tsx
  8. 11
      src/pages/business/videoInfo/components/course.tsx
  9. 27
      src/pages/business/videoInfo/videoInfo.tsx
  10. 79
      src/pages/manage/addCur/addCur.tsx
  11. 10
      src/pages/manage/addStudent/addStudent.tsx
  12. 2
      src/pages/manage/depAdmin/depAdmin.tsx
  13. 2
      src/pages/manage/depCur/depCur.module.scss
  14. 9
      src/pages/manage/depCur/depCur.tsx
  15. 3
      src/pages/manage/meetings/meetings.tsx
  16. 18
      src/pages/manage/selectDep/selectDep.tsx
  17. 7
      src/pages/manage/spotMeeting/spotMeeting.tsx
  18. 19
      src/pages/manage/userInfo/userInfo.tsx
  19. 29
      types/user.d.ts

@ -105,8 +105,8 @@ export const ManageApi = {
return request<DepCurData>(`/api/v1/department/${data.id}/courses?page=${data.page}&size=${data.size}`, "GET")
},
/** 未添加课程 */
optionalCur(dep_id: number, category_id: number) {
return request<Curriculum[]>(`/api/v1/department/${dep_id}/optional?category_id=${category_id}`, "GET")
optionalCur(dep_id: number) {
return request<Curriculum[]>(`/api/v1//choose/${dep_id}/index`, "GET")
},
addCur(data: AddCurProps) {
return request('/api/v1/course/user', "POST", data)

@ -26,10 +26,6 @@ interface CheckoutData {
company: Company
}
interface DepListData {
departments: Department[]
user: User
}
interface LearningRecord {
user_course_records: LearnRecord[]
@ -64,10 +60,6 @@ export const userApi = {
putName(id: number, name: string) {
return request<User>(`/api/v1/auth/login/${id}`, "PUT", {name})
},
/** 所属部门 */
depList() {
return request<DepListData>(`/api/v1/user/detail`, "GET")
},
code(catch_key: string) {
return request<{ code: Code }>(`/api/v1/auth/login/code`, "POST", {openid: catch_key})
},
@ -75,8 +67,8 @@ export const userApi = {
return request<LearningRecord>(`/api/v1/user/record/course`, "GET", courseId ? {courseId} : {})
},
/** 学习记录 */
record(category_id: number) {
return request<Record<number, HourHistory>>(`/api/v1/course/${category_id}/record`, "GET")
record() {
return request<{course:HourHistory[],durations:Record<number, number>}>(`/api/v1/category/history`, "GET")
},
courseRecord(course_id: string) {
return request<CourseRecord>(`/api/v1/course/${course_id}/info`, "GET")
@ -92,6 +84,8 @@ export const userApi = {
},
/**获取指定学员指定时间学习记录 */
statistics(user_id: string, data: StatisticsParam) {
return request<{data:Record<number, number>}>(`/api/v1/statistics/statistics/${user_id}?start_time=${data.start_time}&end_time=${data.end_time}`, "GET")
return request<{
data: Record<number, number>
}>(`/api/v1/statistics/statistics/${user_id}?start_time=${data.start_time}&end_time=${data.end_time}`, "GET")
}
}

@ -4,6 +4,22 @@
justify-content: left;
flex-wrap: nowrap;
height: 420px;
position: relative;
margin-top: 30rpx;
}
.empty {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 10rpx;
color: #00D6AC;
display: flex;
justify-content: center;
align-items: center;
background: rgba(#fff, .9);
}
.columnBox {

@ -13,31 +13,39 @@ interface Props {
const height = 180
const LineChart: FC<Props> = ({data}) => {
const [maxHeight, setMaxHeight] = useState(0)
const [maxHeight, setMaxHeight] = useState<lineData>({time: '', value: 0})
const [lineChartList, setLineChartList] = useState(data)
useEffect(() => {
setLineChartList(data)
setMaxHeight(data.reduce((pre, cur) => {
return Math.max(pre, cur.value)
}, 0))
if (cur.value > pre.value) {
return cur
}
return pre
}, maxHeight))
}, [data])
return (
<ScrollView scrollX>
<View className={style.lineChart}>
{
lineChartList.map(d => <View key={d.time}>
<View className={style.columnBox} style={{width: "100px"}}>
<View className={style.line} style={{height: height - 10 - (d.value / maxHeight * height) + "px"}}></View>
<View>{d.value}</View>
<View className={style.column} style={{height: d.value / maxHeight * height + "px"}}> </View>
<View>{d.time}</View>
</View>
</View>)
}
</View>
</ScrollView>
<>
<ScrollView scrollX={!!maxHeight.value}>
<View>{maxHeight.time}{maxHeight.value}</View>
<View className={style.lineChart}>
{!maxHeight.value && <View className={style.empty}></View>}
{
lineChartList.map(d => <View key={d.time}>
<View className={style.columnBox} style={{width: "100px"}}>
<View className={style.line}
style={{height: height - 10 - (d.value / maxHeight.value * height) + "px"}}></View>
<View>{d.value}</View>
<View className={style.column} style={{height: d.value / maxHeight.value * height + "px"}}> </View>
<View>{d.time}</View>
</View>
</View>)
}
</View>
</ScrollView>
</>
)
}

@ -13,7 +13,8 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
try {
video = Taro.createVideoContext('myVideo')
} catch (e) {}
} catch (e) {
}
function onTimeUpdate(event: BaseEventOrig<VideoProps.onTimeUpdateEventDetail>) {
// if (opt.preview) return;
@ -57,10 +58,12 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
unique_ident.put(Number(currentTime.toFixed(2)), Date.now())
})
Taro.useDidShow(()=>{
if(!video){
Taro.useDidShow(() => {
if (!video) {
video = Taro.createVideoContext('myVideo')
video.play()
if (currentTime && currentTime < opt.duration - deviation) {
video.play()
}
}
})

@ -1,35 +0,0 @@
import {FC, useState} from "react";
import {View} from "@tarojs/components";
import {publicApi} from "@/api/public";
import Taro from "@tarojs/taro";
import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs";
interface Props {
changeTabs: (id: number) => void
}
const CategoryTabs: FC<Props> = ({changeTabs}) => {
const [tabs, setTabs] = useState<TabList[]>([])
async function getData() {
const res = await publicApi.category()
const data = Object.values(res.categories).reduce((pre, cur) => {
pre.push(...cur)
return pre
}, []).map<TabList>(d => ({title: d.name, value: d.id}))
setTabs(data)
changeTabs(data[0].value as number)
}
function onChange(data: OnChangOpt) {
changeTabs(data.tab?.value as number)
}
Taro.useLoad(getData)
return (
<View className='bg-white'>
<Tabs tabList={tabs} onChange={onChange}/>
</View>
)
}
export default CategoryTabs

@ -1,40 +1,44 @@
import {Image, Text, View} from "@tarojs/components";
import CategoryTabs from "@/pages/business/history/components/CategoryTabs";
import styles from './history.module.scss'
import Taro from "@tarojs/taro";
import {userApi} from "@/api";
import {useState} from "react";
import {useEffect, useState} from "react";
import {formatMinute} from "@/utils/time";
import Empty from "@/components/empty/empty";
import {userApi} from "@/api";
const History = () => {
const [data, setData] = useState<HourHistory[]>([])
const [durations, setDurations] = useState<Record<number, number>>({})
async function getData(id: number) {
const res = await userApi.record(id)
setData(Object.values(res))
async function getData() {
const res = await userApi.record()
setData(res.course)
setDurations(res.durations)
}
function jump(course_id: number) {
Taro.navigateTo({url: `/pages/business/curHistory/curHistory?course_id=${course_id}`})
}
useEffect(() => {
getData()
}, [])
return (
<View>
<CategoryTabs changeTabs={getData}/>
<View className='mt-3'>
{data.length ? data.map((d, index) =>
<View key={index} className={styles.category} onClick={() => jump(d.course_id)}>
<View key={index} className={styles.category} onClick={() => jump(d.userCourseRecord.course_id)}>
<View className={styles.thumb}>
<Image src={d.course.thumb} className={styles.image}/>
<View className={styles.count}>{d.total_hour_count}/{d.finished_count}</View>
<Image src={d.thumb} className={styles.image}/>
<View
className={styles.count}>{d.userCourseRecord.hour_count}/{d.userCourseRecord.finished_count}</View>
</View>
<View className={styles.text}>
<View>{d.course.title}</View>
<View>{d.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>
<Text className='mr-4'>{formatMinute(durations[d.id])}</Text>
<Text>{(d.userCourseRecord.finished_count / d.userCourseRecord.hour_count * 100).toFixed(0)}%</Text>
</View>
</View>
</View>) : <Empty name='无观看记录'/>}

@ -107,14 +107,7 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
setValidate(false)
setNewRecord([])
setTime(0)
if (process.env.TARO_ENV === 'h5') {
setTimeout(() => {
setIndex(0)
}, 200)
} else {
setIndex(0)
}
setIndex(0)
}
/** 再来一次 */
@ -219,7 +212,7 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
style={{height: "60vh"}}
snapToEdge
current={index}
onChange={(e) => setIndex(e.detail.current)}>
onChange={(e) => setIndex(e.detail.current ?? index)}>
{examAll?.[time]?.map((d) =>
<SwiperItem key={d.id}>
<ScrollView style='height:70vh' scrollY>

@ -7,6 +7,7 @@ import Course from "./components/course";
import Taro from "@tarojs/taro";
import eventsIndex from "@/hooks/eventsIndex";
import {formatMinute} from "@/utils/time";
import unique_ident from "@/hooks/unique_ident";
const VideoInfo: FC = () => {
const {id, depId} = Taro.getCurrentInstance()?.router?.params as any
@ -15,20 +16,20 @@ const VideoInfo: FC = () => {
const [preview, setPreview] = useState(false)
const [playing, setPlaying] = useState(false)
const getData = useCallback(async () => {
const getData = useCallback(async (playing: boolean) => {
const res = await curriculum.courseDep(id, depId)
if (res) {
setData(res)
}
if (playId) { // 用于自动播放 判断当前课程是否完成
currentVideo(res)
if (playId != null) { // 用于自动播放 判断当前课程是否完成
currentVideo(res, playing)
}
}, [playing])
}, [playing, playId])
const curEnd = useCallback(() => {
const curEnd = () => {
setPlaying(false)
getData().then()
}, [data, playing, playId])
getData(false).then()
}
function setHors(is_complete: boolean, play_id: number) {
setPlaying(true)
@ -37,7 +38,7 @@ const VideoInfo: FC = () => {
}
useEffect(() => {
getData()
getData(false)
}, [id])
@ -71,7 +72,7 @@ const VideoInfo: FC = () => {
/**
*
*/
const currentVideo = useCallback((data: CourseDepData) => {
const currentVideo = useCallback((data: CourseDepData, playing: boolean) => {
const courseHourRecordsFinish = data?.learn_hour_records.find(d => d.id === playId)?.courseHourRecordsFinish
if (typeof courseHourRecordsFinish === 'number') {
if (courseHourRecordsFinish === 1) {
@ -89,12 +90,16 @@ const VideoInfo: FC = () => {
})
}
}
}, [playId, data])
}, [playId, data, playing])
Taro.useDidShow(() => {
data && getData()
data && getData(playing)
})
Taro.useUnload(() => {
unique_ident.del()
})
return (
<>
<View className='content'>

@ -1,9 +1,7 @@
import {CustomWrapper, View} from "@tarojs/components";
import {View} from "@tarojs/components";
import {getCurrentInstance} from "@tarojs/runtime";
import {ManageApi} from "@/api/manage";
import React, {FC, useEffect, useState} from "react";
import Tabs, {TabList} from "@/components/tabs/tabs";
import {Category, publicApi} from "@/api/public";
import Taro from "@tarojs/taro";
import VideoCover from "@/components/videoCover/videoCover";
import Empty from "@/components/empty/empty";
@ -16,50 +14,24 @@ interface AddProps {
const AddCur = () => {
const {id} = getCurrentInstance()?.router?.params as { id: string }
const [category, setCategory] = useState<TabList[]>([])
const [categoryId, setCategoryId] = useState<number>(0)
const [dataMap, setDataMap] = useState<Map<number, Curriculum[]>>(new Map())
const [data, setData] = useState<Curriculum[]>([])
async function getCategory() {
async function getData() {
try {
const data: (TabList)[] = []
const {categories} = await publicApi.category()
Object.values(categories).map(d => {
data.push(...d.map<TabList<Category>>(c => ({title: c.name, value: c.id})))
})
setCategory(data)
setCategoryId(data[0].value as number)
const res = await ManageApi.optionalCur(Number(id))
if (res.length) {
setData(res)
}
} catch (e) {
}
}
async function getData() {
if (categoryId !== null) {
const res = await ManageApi.optionalCur(Number(id), categoryId)
const map = new Map(dataMap)
const data = map.get(categoryId)
if (!data) {
map.delete(categoryId)
map.set(categoryId, res)
setDataMap(map)
} else {
res.forEach(d => {
const index = data.findIndex(c => c.id === d.id)
if (index === -1) {
data.push(d)
} else {
data.splice(index, 1, d)
}
})
map.delete(categoryId)
map.set(categoryId, data)
setDataMap(map)
}
}
}
useEffect(() => {
getData()
}, [])
const Add: FC<AddProps> = ({cur_id, name, index}) => {
function addCur() {
Taro.showModal({
@ -71,14 +43,9 @@ const AddCur = () => {
const is_required = confirm ? 1 : 0
Taro.showLoading()
await ManageApi.addCur({course_id: [cur_id], dep_id: [Number(id)], is_required})
const map = new Map(dataMap)
const data = map.get(categoryId!) || []
if (data) {
data.splice(index, 1)
}
map.delete(categoryId!)
map.set(categoryId!, data)
setDataMap(map)
const oldData: Curriculum[] = JSON.parse(JSON.stringify(data))
oldData.splice(index, 1)
setData(oldData)
Taro.showToast({title: "添加成功"})
} catch (e) {
}
@ -92,22 +59,12 @@ const AddCur = () => {
)
}
useEffect(() => {
getCategory().then()
}, [])
useEffect(() => {
getData().then()
}, [categoryId])
return (
<CustomWrapper>
<View className='bg-white'>
<Tabs tabList={category} onChange={(data) => setCategoryId(data.tab?.value as number)} current={categoryId}/>
</View>
<>
{
(categoryId && dataMap.get(categoryId)?.length) ?
data.length ?
<View className='bg-white mt-2 py-2 flex flex-wrap'>
{dataMap.get(categoryId)?.map((d, index) => (
{data?.map((d, index) => (
<VideoCover
key={d.id}
thumb={d.thumb}
@ -120,7 +77,7 @@ const AddCur = () => {
</View>
: <Empty name='无更多课程'/>
}
</CustomWrapper>
</>
)
}

@ -37,6 +37,12 @@ const AddStudent = () => {
return
}
}
if (!depIds.length) {
Taro.showToast({title: "请选择部门", icon: 'error'})
return
}
Taro.showLoading()
try {
if (params.id) {
@ -80,7 +86,7 @@ const AddStudent = () => {
Taro.useDidShow(() => {
const newDepIds = storageDep.get()
setDepIds( newDepIds.length ?newDepIds: depIds)
setDepIds(newDepIds.length ? newDepIds : depIds)
})
return (
@ -113,7 +119,7 @@ const AddStudent = () => {
</View>
</View>
<Button className='add button' formType='submit'></Button>
<Button className='add button' formType='submit'>{params.id ? '修改' : "新建"}</Button>
</Form>
</View>
)

@ -109,6 +109,7 @@ const DepAdmin: FC = () => {
sort: manages.length,
})
}
getData()
setShow(false)
} catch (e) {
Taro.showToast({title: '操作失败', icon: 'error'})
@ -131,7 +132,6 @@ const DepAdmin: FC = () => {
return (
<>
<View>
{manages.map(d => <PopPut
key={d.id}
title={d.name}

@ -21,7 +21,7 @@
border-radius: 10rpx;
margin-right: 10rpx;
width: 280rpx;
height: 150rpx;
height: 180rpx;
}
.del {

@ -1,7 +1,7 @@
import {getCurrentInstance} from "@tarojs/runtime";
import Taro, {useReachBottom} from "@tarojs/taro";
import {ManageApi} from "@/api/manage";
import React, {FC, useState} from "react";
import React, {FC, useEffect, useState} from "react";
import {Image, View} from "@tarojs/components";
import {curriculum} from "@/api";
import MyButton from "@/components/button/MyButton";
@ -68,7 +68,10 @@ const DepCur: FC = () => {
Taro.navigateTo({url: "/pages/manage/addCur/addCur?id=" + id})
}
Taro.useDidShow(() => getData())
useEffect(() => {
getData()
}, [])
useReachBottom(() => {
if (data.length < total) {
getData(true)
@ -79,7 +82,7 @@ const DepCur: FC = () => {
<View className={styles.page}>
{
data.length ?
data.map(d => <View className={styles.curBox}>
data.map(d => <View key={d.id} className={styles.curBox}>
<View className={styles.imageBox}>
<Image src={d.thumb} className={styles.image} mode='aspectFill'/>
<View>{d.title}</View>

@ -61,10 +61,9 @@ const MeetingsConfig: FC = () => {
{
meeting.length ?
meeting.map((d, index) => <View className={styles.meeting} key={d.id}>
<View className={styles.title} onClick={() => jumpInfo(d.id)}>
<View className={styles.title} onClick={() => Date.now() < d.estimate_end_time && jumpInfo(d.id)}>
<View className='font-weight mb-1'>{d.name}</View>
<View>{formatDate(new Date(d.estimate_start_time), "MM-dd")} {formatDate(new Date(d.estimate_start_time), "MM-dd")}</View>
{Date.now() > d.estimate_end_time && <View className={styles.overdue}></View>}
</View>
<View className={styles.del} onClick={() => del(d.id, index)}></View>

@ -16,15 +16,21 @@ const SelectDep: FC = () => {
curriculum.department().then(res => {
setDeps(res.data)
})
setIds(JSON.parse(depIds))
}, [])
const onChange = useCallback((id: number) => {
setIds([
...ids,
id
])
const index = ids.indexOf(id)
if (index === -1) {
setIds([
...ids,
id
])
} else {
const oldIds: number[] = JSON.parse(JSON.stringify(ids))
oldIds.splice(index, 1)
setIds(oldIds)
}
}, [ids])
function ok() {
@ -38,7 +44,7 @@ const SelectDep: FC = () => {
return (
<View className='px-2 bg-white'>
{deps.map(d => <View className='flex align-center' key={d.id} onClick={()=>onChange(d.id)}>
{deps.map(d => <View className='flex align-center' key={d.id} onClick={() => onChange(d.id)}>
<Checkbox value={d.id + ''} checked={ids.includes(d.id)}/>
<PopPut
key={d.id}

@ -13,8 +13,8 @@ const SpotMeeting: FC = () => {
const path = encodeURIComponent("/pages/meeting/meeting")
const params = Taro.getCurrentInstance().router?.params as { id: string | undefined }
const [manages, setManages] = useState<Manage[]>([])
const [start, setStart] = useState<string>(formatDate(new Date(), "YY-MM-dd hh:00:00"))
const [end, setEnd] = useState<string>(formatDate(new Date(), "YY-MM-dd 18:00:00"))
const [start, setStart] = useState<string>(formatDate(new Date(new Date().getTime() + 24 * 60 * 60 * 1000), "YY-MM-dd hh:00:00"))
const [end, setEnd] = useState<string>(formatDate(new Date(new Date().getTime() + 24 * 60 * 60 * 1000), "YY-MM-dd 18:00:00"))
const [depid, setDepid] = useState<number | null>(null)
const [imgUrl, setImgUrl] = useState('')
const [name, setName] = useState('')
@ -277,7 +277,8 @@ const SpotMeeting: FC = () => {
</Form>
</View>
{!params.id && <View className='text-center text-muted my-3' onClick={jumpMeetings}> <Icon name='chevron-right'/></View>}
{!params.id && <View className='text-center text-muted my-3' onClick={jumpMeetings}> <Icon
name='chevron-right'/></View>}
</View>
</View>

@ -37,6 +37,7 @@ const UserInfo: FC = () => {
const {userId} = Taro.getCurrentInstance().router?.params as { userId: string }
const [data, setData] = useState<User | null>(null)
const [lineData, setLineData] = useState<any[]>([])
const router = Taro.useRouter()
const {user} = Profile.useContainer()
function setRoleType() {
@ -69,9 +70,11 @@ const UserInfo: FC = () => {
title: '是否确认删除',
async success({confirm}) {
if (confirm) {
await ManageApi.del(userId)
Taro.showToast({title: '删除成功'})
Taro.navigateBack()
try {
await ManageApi.del(userId)
Taro.showToast({title: '删除成功'})
Taro.navigateBack()
} catch (e) {}
}
}
})
@ -98,6 +101,14 @@ const UserInfo: FC = () => {
})
}, [])
Taro.useDidShow(() => {
if (data) {
userApi.info(router.params.userId!).then(res => {
setData(res)
})
}
})
return (
<View className={styles.page}>
<Info data={data}/>
@ -106,7 +117,7 @@ const UserInfo: FC = () => {
<Tabs tabList={tabList} onChange={tabChange}/>
<View className='font-weight font-36 mt-5 mb-3'>
<Text style={{margin: '0 10px', color: '#00D6AC'}}>{lineData.reduce((pre, cur) => pre + cur.value, 0)}</Text>
<Text style={{margin: '0 10px', color: '#00D6AC'}}>{lineData.reduce((pre, cur) => pre + cur.value, 0)}</Text>
</View>
<LineChart data={lineData}/>

29
types/user.d.ts vendored

@ -78,19 +78,20 @@ interface CueStats {
/** 课时学习记录 */
interface HourHistory {
id: number;
duration: number;
course: Curriculum;
user_id: number;
created_date: string;
start_at: string;
end_at: string;
course_id: number;
hour_id: number;
unique_ident: number;
total_hour_count: number;
finished_count: number;
interface HourHistory extends Curriculum {
userCourseRecord: {
id: number;
duration: number;
user_id: number;
created_date: string;
start_at: string;
end_at: string;
course_id: number;
hour_id: number;
unique_ident: number;
hour_count: number;
finished_count: number;
}
}
@ -98,5 +99,5 @@ interface Offline {
depid: number
start_time: number
end_time: number
path:string
path: string
}

Loading…
Cancel
Save