import {reactive} from 'vue'; import request from "../utils/request"; import {showFailToast, showSuccessToast} from "vant"; import dayjs from "dayjs"; import { Comment, CommentRefTypes, Daka, DakaScope, HtyBaseRoles, HtyRoles, Clazz, JihuaQueryParam, Lianxi, MultiVals, NotifyParam, NotifyTypes, Piyue, CourseSection, TeacherSummary } from '~/types' import useUser from "~/store/user"; import useRefResource from "~/store/ref-resource"; import {formatDate} from "~/utils"; import useLoading from "~/store/loading"; import useLianxi from "~/store/lianxi"; import useComment from "~/store/comment"; import usePiyue from "~/store/piyue"; import useNotify from "~/store/notify"; import useCourseCategory from "~/store/qumu-category"; const initialDaka = () => ({ start_date: dayjs().startOf('day').toDate(), end_date: dayjs().add(6, 'day').startOf('day').toDate(), duration_days: 7, course_sections: [] as CourseSection[] } as Daka); export interface DakaState { current: Daka, list: Daka[], total: number, pages: number, hanging: boolean, checking: boolean, current_lianxi?: Lianxi, query_cache?: JihuaQueryParam, } const store = reactive({ current: initialDaka(), list: [], total: 0, pages: 0, hanging: false, checking: false }); export default function useDaka() { const usingUser = useUser(); const {create_lianxi, remove_lianxi, check_lianxi_tasks} = useLianxi(); const {create_piyue} = usePiyue(); const {create_comment, load_comments, check_comments_task, get_resource_note_group, get_resource_note_group_by_ref_id, check_resource_notes_task} = useComment() const {notify_ws} = useNotify(); const {get_hty_resource_by_id} = useRefResource(); const usingQC = useCourseCategory() usingQC.query(); const notify = (params: NotifyParam, role_key: HtyRoles) => notify_ws(params, role_key); const {load_start, load_done} = useLoading() function getEndDate({start_date, duration_days}: Daka) { let end = dayjs(start_date).add(duration_days, 'day'); return formatDate(end); } function reset() { if (!store.hanging) { store.current = initialDaka(); } store.hanging = false; } async function query(params: JihuaQueryParam): Promise { let {current: {hty_id}, currentRole} = usingUser.store; if (currentRole === HtyBaseRoles.TEACHER) { params.teacher_id = hty_id; params.scope = DakaScope.ALL; } else if (currentRole === HtyBaseRoles.STUDENT) { params.student_id = hty_id; } store.query_cache = params; load_start() const {r, d, e} = await request({url: '/api/v1/ws/find_dakas_with_sections_by_user_id', params}); load_done() if (r) { if (params.page === 1) { store.list = []; } let [list, pages, total] = d; let kcs = await queryClazzByDakaIds(list.map((x: Daka) => x.id)) let dict: {[key: string]: Clazz} = {}; kcs.forEach(kc => { kc.dakas?.vals.forEach(x => { dict[x.id as string] = kc; }) }) store.list = [...store.list, ...list.map((x: Daka) => ({...convert(x), clazz: dict[x.id], end_date: getEndDate(x)}))]; store.total = total; store.pages = pages; } else { showFailToast(e); } return Promise.resolve(r); } async function queryClazzByDakaIds(daka_ids: string[]): Promise { load_start() const {r, d, e} = await request({ url: '/api/v1/clazz/find_by_daka_ids', data: daka_ids, method: 'POST' }); load_done() if (r) { return d || []; } else { return [] } } async function get_qs_resource_note_groups() { for (let i = 0; i < store.current.course_sections.length; i++) { let qs = store.current.course_sections[i] if (!qs.resource_note_group) { await get_resource_note_group(qs) } else { // we need to set comments to global state by check_comments_tasks function await check_resource_notes_task(qs.resource_note_group) } } } async function check_tasks() { if (store.checking) return; store.checking = true; await check_comments_task(store.current.id) await check_lianxi_tasks(store.current.course_sections); store.checking = false; } const convert = (daka: Daka) => { let { course_sections, course_sections2 } = daka; let lianxiDict: { [key: string]: CourseSection } = {}; course_sections.forEach(qs => { lianxiDict[qs.id as string] = qs }) if (course_sections2?.vals?.length) { daka.course_sections = course_sections2.vals daka.course_sections.forEach(qs => { let tmp = lianxiDict[qs.id as string]; qs.lianxis = tmp?.lianxis; qs.lianxi_count = tmp?.lianxi_count; qs.not_piyue_count = tmp?.not_piyue_count }) } return daka; } async function read(id: string, forceReload = false) { if (!id) return; if (store.current.id === id && !forceReload) return; load_start() const {r, d, e} = await request({url: `/api/v1/ws/find_daka_by_id2/${id}`}); if (r) { convert(d); store.current = {...d, end_date: getEndDate(d)}; await load_comments(id); await get_qs_resource_note_groups(); } else { reset() showFailToast(e); } load_done() } function valid() { let { name, start_date, students, course_sections } = store.current; if (!name) { showFailToast("请输入打卡名称!") return false; } if (!start_date) { showFailToast("请选择打卡起始日期!") return false; } if (!students?.val.users.vals.length) { showFailToast("请选择学生!") return false; } if (!course_sections?.length) { showFailToast('请添加课程!') return false; } return true } async function prepare(qs?: CourseSection) { let { course_sections, course_sections2, ...rest } = store.current console.log(rest) console.log(qs) let qsData: {vals: CourseSection[]} = {vals: []} for (let i = 0; i < course_sections.length; i++) { if (qs?.id === course_sections[i].id) { course_sections[i] = qs as CourseSection; store.current.course_sections[i] = qs as CourseSection; } let { resources, ref_resources, id, resource_note_group, ...rest } = course_sections[i]; ref_resources = ref_resources || resources; if (!resource_note_group) { resource_note_group = await get_resource_note_group_by_ref_id(id as string) } qsData.vals.push({id, ref_resources, ...rest, resource_note_group}) } return {...rest, course_sections: qsData, course_section_ids: course_sections.map(x => x.id)} as unknown as Daka } async function create(): Promise { if (!valid()) return; load_start() let data = await prepare(); if (!data) { load_done(); return; } let { end_date, teachers, students, ...rest } = data; let teacher_id = usingUser.store.current.hty_id; let teacher_name = usingUser.store.current.real_name; let created_by = usingUser.store.current.hty_id; clean_teachers(teachers) let payload = {teacher_id, teacher_name, created_by, students, teachers, ...rest}; const {r, d, e} = await request({ url: `/api/v1/ws/create_daka`, data: {...payload}, method: 'POST' }) if (r) { showSuccessToast("打卡创建成功!"); // notify students students?.val.users.vals.forEach(u => { notify({ hty_id: u.user_id, daka_id: d, notify_type: NotifyTypes.DakaCreate, remark: "请认真完成练习,按时提交" } as NotifyParam, HtyBaseRoles.STUDENT) }) // notify piyue teacher if (teachers?.vals?.length) { teachers?.vals.forEach(t => { notify({ hty_id: t.teacher_id, daka_id: d, notify_type: NotifyTypes.DakaCreate, remark: "请注意查阅学生练习" } as NotifyParam, HtyBaseRoles.TEACHER) }) } } else { showFailToast(e); } load_done() return d; } function clean_teachers(teachers?: MultiVals) { if (!teachers) return; // clean repeat teachers let teacher_ids = new Set(usingUser.store.current.hty_id), i = 0; while (i < teachers?.vals?.length) { let {teacher_id} = teachers.vals[i] if (teacher_ids.has(teacher_id)) { teachers.vals.splice(i, 1); } else { teacher_ids.add(teacher_id) i++ } } } async function update(qs? :CourseSection) { if (!valid()) return Promise.reject() load_start() let data = await prepare(qs); if (!data) { load_done(); return 0; } let { end_date, teachers, students, clazz, ...rest } = data; clean_teachers(teachers) const {r, e} = await request({ url: `/api/v1/ws/update_daka`, data: { teachers, students, ...rest, updated_by: usingUser.store.current.hty_id, updated_at: new Date() }, method: 'POST' }); load_done() if (r) { showSuccessToast('保存成功'); reset() // notify students students?.val.users.vals.forEach(u => { notify({ hty_id: u.user_id, daka_id: rest.id, notify_type: NotifyTypes.DakaUpdate } as NotifyParam, HtyBaseRoles.STUDENT) }) // notify all piyue teachers if (teachers?.vals?.length) { teachers?.vals.forEach(t => { notify({ hty_id: t.teacher_id, daka_id: rest.id, notify_type: NotifyTypes.DakaUpdate } as NotifyParam, HtyBaseRoles.TEACHER) }) } } else { load_done() showFailToast(e); } return r; } async function remove() { load_start() let { id, students } = store.current const {r, e} = await request({ url: `/api/v1/ws/delete_daka_by_id/${id}`, method: 'POST' }); load_done() students?.val.users.vals.forEach(u => { notify({ hty_id: u.user_id, daka_id: id, notify_type: NotifyTypes.DakaDelete } as NotifyParam, HtyBaseRoles.STUDENT) }) if (!r) { showFailToast(e); } return r; } async function get_shifan(daka_course_section_id?: string) { let relation = store.current.relations?.find(r => r.id === daka_course_section_id); let course_section = store.current.course_sections.find(qs => qs.id === relation?.course_section_id); let audio_type = course_section?.course_category_key || usingQC.getDefault()?.category_key; let shifan = (course_section?.ref_resources || course_section?.resources)?.find(r => r.is_shifan); if (shifan) { let url = shifan.resource_url; if (shifan.hty_resource_id) { let resource = await get_hty_resource_by_id(shifan.hty_resource_id); if (resource) { url = resource.url; } } return { shifan_url: url, audio_type } } return; } async function create_daka_lianxi(data: Lianxi) { load_start() let shifan = await get_shifan(data.daka_course_section_id); let lianxi_id = await create_lianxi(data, shifan) if (lianxi_id) { await read(store.current.id, true); } load_done() return lianxi_id; } async function delete_daka_lianxi(data: Lianxi) { let relation = store.current.relations?.find(r => r.id === data.daka_course_section_id); let course_section = store.current.course_sections.find(qs => qs.id === relation?.course_section_id); const r = await remove_lianxi(data.id); notify({ hty_id: store.current.teacher_id, lianxi_id: data.id, course_name: course_section?.course_name, course_section_name: course_section?.section_name, notify_type: NotifyTypes.LianxiDelete } as NotifyParam, HtyBaseRoles.TEACHER) await read(store.current.id, true); return r; } async function create_daka_comment(comment: Comment) { // @ts-ignore let qs = store.current.course_sections.find(x => x.lianxis?.includes(store.current_lianxi)); console.log('qs...', qs) let shifan_url = qs?.ref_resources?.find(r => r.is_shifan)?.resource_url; let audio_type = qs?.course_category_key; console.log('shifan url...', shifan_url, audio_type) const created = await create_comment(comment, {shifan_url, audio_type}); if (created) { let piyues = store.current_lianxi?.piyues; if (comment.ref_type === CommentRefTypes.Piyue) { piyues?.forEach(x => { if (x.id === comment.ref_id) { if (!x.comments.includes(created)) { x.comments.push(created) } } }) } else if (comment.ref_type === CommentRefTypes.ResourceNote) { piyues?.forEach(x => { x.resource_note_group?.resource_notes?.forEach(y => { if (y.id === comment.ref_id) { if (!y.comments?.includes(created)) { y.comments?.push(created) } } }) }) } return created } } async function create_daka_piyue(piyue: Piyue) { const piyue_id = await create_piyue(piyue); if (piyue_id) { await read(store.current.id, true); notify({ first: "同学,您提交的练习老师批阅啦", hty_id: store.current_lianxi?.created_by, lianxi_id: piyue.lianxi_id, piyue_id, notify_type: NotifyTypes.PiyueCreate } as NotifyParam, HtyBaseRoles.STUDENT) let params = { first: `学生【${store.current_lianxi?.creator_name}】的打卡练习已被【${usingUser.store.current.real_name}】老师批阅`, hty_id: store.current.teacher_id, lianxi_id: piyue.lianxi_id, piyue_id, notify_type: NotifyTypes.PiyueCreate } if (usingUser.store.current.hty_id === store.current.teacher_id) { // send notification to all piyue teachers if supervisor piyued store.current.teachers?.vals?.forEach(t => { notify({...params, hty_id: t.teacher_id} as NotifyParam, HtyBaseRoles.TEACHER) }) } else { // send notification to supervisor teacher and all other piyue teachers if subsidiary teacher piyued notify({...params} as NotifyParam, HtyBaseRoles.TEACHER) store.current.teachers?.vals?.forEach(t => { if (t.teacher_id === usingUser.store.current.hty_id) return; notify({...params, hty_id: t.teacher_id} as NotifyParam, HtyBaseRoles.TEACHER) }) } } return piyue_id; } function get_lianxi_by_id(lianxi_id: string) { let lianxi; store.current.course_sections.forEach(qs => { qs.lianxis?.forEach(lx => { if (lx.id === lianxi_id) { lianxi =lx; return; } }) }) return lianxi; } return { store, query, read, create, update, remove, reset, create_daka_lianxi, notify, get_shifan, prepare, delete_daka_lianxi, create_daka_piyue, check_tasks, create_daka_comment, load_comments, get_lianxi_by_id }; }