课程管理选择部门 && 删除公司课程

main
king 1 year ago
parent c39dbc6847
commit 289240d179
  1. 6
      src/api/course.ts
  2. 15
      src/api/manage.ts
  3. 38
      src/components/dateTimePicker/dateTimePicker.tsx
  4. 17
      src/pages/manage/courseAdmin/components/search.tsx
  5. 1
      src/pages/manage/courseAdmin/courseAdmin.config.ts
  6. 48
      src/pages/manage/courseAdmin/courseAdmin.module.scss
  7. 173
      src/pages/manage/courseAdmin/courseAdmin.tsx
  8. 7
      src/pages/manage/depAdmin/depAdmin.tsx
  9. 50
      src/pages/manage/selectDep/selectDep.tsx
  10. BIN
      src/static/img/screen.png
  11. 13
      types/curriculum.d.ts

@ -20,6 +20,10 @@ export const courseApi = {
}, },
/** 获取部门课程 */ /** 获取部门课程 */
getCourseAll(data: CourseAllParam) { getCourseAll(data: CourseAllParam) {
return request('/api/v1/course/all/company', "GET", data) return request<{ data: Curriculum[], total: number }>('/api/v1/course/all/company', "GET", data)
},
/** 删除公司课程 */
delCourse(course_id: number) {
return request(`/api/v1/course/del/${course_id}`, "DELETE")
} }
} }

@ -46,22 +46,9 @@ export interface depCurProps {
interface AddCurProps { interface AddCurProps {
course_id: number[] course_id: number[]
dep_id: number[] dep_id: number[]
is_required: 1 | 0 is_required: (1 | 0)[]
} }
export interface CurLearningRecord {
course: Curriculum
data: User[]
/** 部门 */
departments: Record<number, string>
/** 第一次记录 */
user_course_hour_user_first_at: Record<string, unknown>
/** 学习记录 */
user_course_records: Record<string, LearnRecord>
/** 学员部门 */
user_dep_ids: Record<number, number[]>
total: number
}
interface DepListData { interface DepListData {
data: User[] data: User[]

@ -1,33 +1,33 @@
import {FC} from "react"; import {FC} from "react";
import {Picker, View} from "@tarojs/components";
interface Props { interface Props {
children: JSX.Element children: JSX.Element
disabled?: boolean disabled?: boolean
} }
const DateTimePicker: FC<Props> = (props) => { const DateTimePicker: FC<Props> = () => {
function bindMultiPickerChange(e) { // function bindMultiPickerChange() {
let activityArray = JSON.parse(JSON.stringify(this.activityArray)), // let activityArray = JSON.parse(JSON.stringify(this.activityArray)),
{value} = e.detail, // {value} = e.detail,
_result = []; // _result = [];
for (let i = 0; i < value.length; i++) { // for (let i = 0; i < value.length; i++) {
_result[i] = activityArray[i][value[i]].id; // _result[i] = activityArray[i][value[i]].id;
} // }
this.$emit("result", _result); // this.$emit("result", _result);
} // }
return ( return (
<Picker <></>
mode="multiSelector" // <Picker
range-key='name' // mode="multiSelector"
disabled={props.disabled} // range-key='name'
onChange={bindMultiPickerChange} // disabled={props.disabled}
> // onChange={bindMultiPickerChange}
{props.children}12 // >
</Picker> // {props.children}12
// </Picker>
) )
} }

@ -1,11 +1,12 @@
import {FC, useCallback, useEffect, useState} from "react"; import {FC, useCallback, useEffect, useState} from "react";
import {Input, Radio, Text, View} from "@tarojs/components"; import {Image, Input, Radio, Text, View} from "@tarojs/components";
import styles from "../courseAdmin.module.scss"; import styles from "../courseAdmin.module.scss";
import Icon from "@/components/icon"; import Icon from "@/components/icon";
import {CourseAllParam, curriculum} from "@/api"; import {CourseAllParam, curriculum} from "@/api";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container"; import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
import Empty from "@/components/empty/empty"; import Empty from "@/components/empty/empty";
import MyButton from "@/components/button/MyButton"; import MyButton from "@/components/button/MyButton";
import screen from '@/static/img/screen.png'
interface Props { interface Props {
param: CourseAllParam param: CourseAllParam
@ -36,7 +37,11 @@ export const Search: FC<Props> = ({param, setParam}) => {
const putParam = useCallback(() => { const putParam = useCallback(() => {
setShow(false) setShow(false)
if (param.dep_id !== depId) { if (param.dep_id !== depId) {
setParam({...param, dep_id: depId}) setParam({
...param,
dep_id: depId,
page: 1
})
} }
}, [depId]) }, [depId])
@ -53,10 +58,16 @@ export const Search: FC<Props> = ({param, setParam}) => {
placeholder='搜索名称' placeholder='搜索名称'
className='ml-1 flex-1' className='ml-1 flex-1'
value={param.title} value={param.title}
onBlur={(e) => setParam({...param, title: e.detail.value})}/> onBlur={(e) => setParam({
...param,
title: e.detail.value,
page: 1
})}/>
</View> </View>
<View onClick={() => setShow(true)}> <View onClick={() => setShow(true)}>
<Text></Text> <Text></Text>
<Image src={screen}
style={{display: 'inline-block', width: '15px', verticalAlign: 'middle', marginLeft: '5px'}}/>
</View> </View>
</View> </View>

@ -1,3 +1,4 @@
export default definePageConfig({ export default definePageConfig({
navigationBarTitleText: '课程管理', navigationBarTitleText: '课程管理',
onReachBottomDistance: 30
}) })

@ -7,7 +7,7 @@
} }
.searchInput { .searchInput {
width: 570rpx; width: 550rpx;
height: 68rpx; height: 68rpx;
display: flex; display: flex;
align-items: center; align-items: center;
@ -28,3 +28,49 @@
overflow: auto; overflow: auto;
padding: 20px 0; padding: 20px 0;
} }
.curBox {
background: #fff;
margin-top: 20px;
}
.curTitle {
padding: 30rpx;
display: flex;
border-bottom: 1px solid #F5F8F7;
}
.curImage {
width: 280rpx;
height: 164rpx;
border-radius: 10rpx;
margin: 0 20px;
background: #eee;
}
.Operation {
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.curList {
padding-bottom: 100px;
}
.add {
border-top: 1px solid #F5F8F7;
background: #FFFFFF;
text-align: center;
position: fixed;
bottom: 0;
padding-bottom: env(safe-area-inset-bottom);
width: 100%;
color: #45D4A8;
}
.addBatch {
display: flex;
justify-content: space-between;
padding: 30rpx;
}

@ -1,9 +1,17 @@
import {FC, useEffect, useState} from "react"; import {FC, useCallback, useEffect, useState} from "react";
import {View} from "@tarojs/components"; import {Image, Radio, Text, View} from "@tarojs/components";
import {Search} from "./components/search"; import {Search} from "./components/search";
import {CourseAllParam, courseApi} from "@/api"; import {CourseAllParam, courseApi, ManageApi} from "@/api";
import styles from './courseAdmin.module.scss'
import Taro, {useReachBottom} from "@tarojs/taro";
import MyButton from "@/components/button/MyButton";
import storageDep from "@/hooks/storageDep";
const CourseAdmin: FC = () => { const CourseAdmin: FC = () => {
const [total, setTotal] = useState(0)
const [data, setData] = useState<Curriculum[]>([])
const [batch, setBatch] = useState(false)
const [curs, setCurs] = useState<number[]>([])
const [param, setParam] = useState<CourseAllParam>({ const [param, setParam] = useState<CourseAllParam>({
page: 1, page: 1,
page_size: 10, page_size: 10,
@ -11,13 +19,170 @@ const CourseAdmin: FC = () => {
dep_id: 0 dep_id: 0
}) })
/**
*@param replace
*/
function getData(replace = false) {
courseApi.getCourseAll({...param, page_size: param.page_size * (replace ? param.page : 1)}).then(res => {
setTotal(res.total)
if (param.page === 1 || replace) {
setData(res.data)
} else {
setData([
...data,
...res.data,
])
}
})
}
useEffect(() => { useEffect(() => {
courseApi.getCourseAll(param) getData()
}, [param]) }, [param])
useReachBottom(() => {
if (data.length < total) {
setParam({
...param,
page: param.page + 1
})
}
})
function all() {
if (curs.length === data.length) {
setCurs([])
} else {
setCurs(data.map(d => d.id))
}
}
function addCurs(id: number) {
const index = curs.indexOf(id)
if (index === -1) {
setCurs([...curs, id])
} else {
const old: number[] = JSON.parse(JSON.stringify(curs))
old.splice(index, 1)
setCurs(old)
}
}
function del(id: number, index: number) {
Taro.showModal({
title: '删除警告',
content: "删除后所有部门不可查看",
async success({confirm}) {
if (confirm) {
try {
await courseApi.delCourse(id)
Taro.showToast({title: '删除成功'})
const oldData: Curriculum[] = JSON.parse(JSON.stringify(data))
oldData.splice(index, 1)
setData(oldData)
} catch (e) {
}
}
}
})
}
function changeDep(id: number, data: CurDepInfo[]) {
const depList: Number[] = []
const required: Number[] = []
data.forEach(d => {
depList.push(d.dep_id)
required.push(d.is_required)
})
// @ts-ignore
batchChangDep([id], depList, required)
}
/**
*
* @param ids id
* @param depList []
* @param required []
*/
function batchChangDep(ids: number[], depList = [], required = []) {
if (!ids.length) {
Taro.showToast({title: '请选择课程', icon: 'none'})
return
}
if (ids.length === 1) {
setCurs(ids)
}
Taro.navigateTo({url: `/pages/manage/selectDep/selectDep?depIds=${JSON.stringify(depList)}&required=${JSON.stringify(required)}`})
}
Taro.useDidShow(useCallback(async () => {
const dep_id = storageDep.get()
const is_required = storageDep.getRequired()
if (!dep_id.length || !is_required.length || !curs.length) return;
try {
await ManageApi.addCur({course_id: curs, dep_id, is_required})
Taro.showToast({title: '修改成功'})
// deps 中没有 筛选条件中的depid 删除已选的课程
if (param.dep_id && dep_id.includes(param.dep_id)) {
const newData = data.reduce((pre, cur) => {
if (!dep_id.includes(cur.id)) {
pre.push(cur)
}
return pre
}, [] as Curriculum[])
setData(newData)
} else {
getData()
}
} catch (e) {
}
}, [curs, data]))
return ( return (
<View> <View>
<Search param={param} setParam={setParam}/> <Search param={param} setParam={setParam}/>
<View className={styles.curList}>
{
data.map((d, index) => <View key={d.id} className={styles.curBox}>
<View className={styles.curTitle} onClick={() => addCurs(d.id)}>
{batch && <Radio
checked={curs.includes(d.id)}
style={{marginTop: '30px'}}
onClick={() => addCurs(d.id)}/>}
<Image src={d.thumb} className={styles.curImage}/>
<View>{d.title}</View>
</View>
<View className={styles.Operation}>
<View onClick={() => del(d.id, index)}></View>
<View onClick={() => changeDep(d.id, d.data)}></View>
</View>
</View>)
}
</View>
<View className={styles.add}>
{
!batch
&& data.length > 0
&& <View style={{margin: 'auto', padding: '15px'}} onClick={() => setBatch(true)}></View>
}
{
batch && <View className={styles.addBatch}>
<Radio onClick={all} checked={data.length === curs.length}> </Radio>
<Text onClick={() => setBatch(false)}></Text>
<MyButton size='mini' onClick={() => batchChangDep(curs)}>{curs.length}</MyButton>
</View>
}
</View>
</View> </View>
) )
} }

@ -11,7 +11,6 @@ import {Profile} from '@/store'
const DepAdmin: FC = () => { const DepAdmin: FC = () => {
const router = useRouter() const router = useRouter()
// const params = getCurrentInstance()?.router?.params as { dep_id: string, name: string, id: string }
const [manages, setManages] = useState<Manage[]>([]) const [manages, setManages] = useState<Manage[]>([])
const [show, setShow] = useState(false) const [show, setShow] = useState(false)
const [users, setUsers] = useState<User[]>([]) const [users, setUsers] = useState<User[]>([])
@ -94,6 +93,10 @@ const DepAdmin: FC = () => {
} }
const addDep = useCallback(async () => { const addDep = useCallback(async () => {
if (!depName) {
Taro.showToast({title: '请填写部门名称!', icon: 'error'})
return
}
try { try {
if (isPut) { if (isPut) {
await ManageApi.putDep(router.params.id!, depName) await ManageApi.putDep(router.params.id!, depName)
@ -163,7 +166,7 @@ const DepAdmin: FC = () => {
<ShowModel <ShowModel
show={show} show={show}
title={router.params.name ? `修改${decodeURI(router.params.name)}` : '添加部门'} title={isPut ? `修改${depName}` : '添加部门'}
onClickOverlay={() => setShow(false)} onClickOverlay={() => setShow(false)}
onOk={addDep} onOk={addDep}
> >

@ -1,5 +1,5 @@
import React, {FC, useCallback, useEffect, useState} from "react"; import React, {FC, useCallback, useEffect, useState} from "react";
import {Checkbox, View} from "@tarojs/components"; import {Checkbox, Switch, View} from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import {curriculum} from "@/api"; import {curriculum} from "@/api";
import PopPut from "@/components/popPut/popPut"; import PopPut from "@/components/popPut/popPut";
@ -51,7 +51,7 @@ const SelectDep: FC = () => {
function ok() { function ok() {
if (!ids.length) { if (!ids.length) {
Taro.showToast({title: '请选部门', icon: "error"}) Taro.showToast({title: '请选部门', icon: "error"})
return return
} }
storageDep.set(ids) storageDep.set(ids)
@ -59,16 +59,50 @@ const SelectDep: FC = () => {
Taro.navigateBack({delta: 1}) Taro.navigateBack({delta: 1})
} }
function isRequired(dep_id: number): boolean {
const index = ids.indexOf(dep_id)
if (index === -1) {
return false
} else {
return !!required?.[index]
}
}
function requiredChange(dep_id: number, value: boolean) {
const index = ids.indexOf(dep_id)
if (index === -1) {
setIds([...ids, dep_id])
setRequired([...required, value ? 1 : 0])
} else {
const oldRequired: number[] = JSON.parse(JSON.stringify(required))
oldRequired.splice(index, 1, value ? 1 : 0)
setRequired(oldRequired)
}
}
return ( return (
<View className='px-2 bg-white'> <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)}/> <Checkbox value={d.id + ''} checked={ids.includes(d.id)}/>
<PopPut <View style={{flex: 1}}>
key={d.id} <PopPut
title={d.name} key={d.id}
chevron title={d.name}
leftImage={folder} chevron
/> leftImage={folder}
content={
params?.required ?
<View className='flex align-center'>
<Switch
onClick={() => event?.stopImmediatePropagation()}
checked={isRequired(d.id)}
onChange={(e) => requiredChange(d.id, e.detail.value)}/>
</View>
: null
}
/>
</View>
</View>)} </View>)}
<View className={'my-3'}> <View className={'my-3'}>

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

@ -1,3 +1,9 @@
/** 课程划分部门 和 必修 */
interface CurDepInfo {
dep_id: number
is_required: 0 | 1
}
interface Curriculum { interface Curriculum {
id: number; id: number;
title: string; title: string;
@ -13,8 +19,9 @@ interface Curriculum {
thumb: string; thumb: string;
/** 时间 */ /** 时间 */
course_duration: number course_duration: number
data: CurDepInfo[]
[key:string]:any [key: string]: any
} }
/** 课程信息 */ /** 课程信息 */
@ -91,7 +98,7 @@ interface LearnRecord {
/** 学习记录 */ /** 学习记录 */
interface HourCacheParam { interface HourCacheParam {
courseId:number courseId: number
/** 视频学习时长 */ /** 视频学习时长 */
duration: number; duration: number;
/** 课时结束学习时间 */ /** 课时结束学习时间 */
@ -100,5 +107,5 @@ interface HourCacheParam {
/** 课时开始学习时间 */ /** 课时开始学习时间 */
start_date: number; start_date: number;
user_id: number; user_id: number;
unique_ident:number unique_ident: number
} }

Loading…
Cancel
Save