Compare commits

..

No commits in common. "be222a2ba60742533646a6eea12e1099c6fa69d6" and "99ee438fd7fe3ebdf1ca861e794425b815921589" have entirely different histories.

18 changed files with 408 additions and 622 deletions

View File

@ -43,7 +43,6 @@
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"vue-router": "^4.4.0", "vue-router": "^4.4.0",
"xgplayer": "^3.0.19",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {

View File

@ -150,6 +150,7 @@ async function createLinkWin(data) {
// 初始化完成 // 初始化完成
app.on('ready', () => { app.on('ready', () => {
process.env.LANG = 'en_US.UTF-8' process.env.LANG = 'en_US.UTF-8'
process.env['ELECTRON_DISABLE_SANDBOX'] = true;
// 设置应用程序用户模型标识符 // 设置应用程序用户模型标识符
electronApp.setAppUserModelId('com.electron') electronApp.setAppUserModelId('com.electron')

View File

@ -8,7 +8,7 @@
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/> --> /> -->
<meta http-equiv="Content-Security-Policy" content="connect-src *; default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *;img-src * 'self' data: blob:" /> <meta http-equiv="Content-Security-Policy" content="connect-src *; default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * 'self' data: blob:" />
</head> </head>

View File

@ -5,9 +5,8 @@
<el-dropdown style="width: 100%;height: 100%"> <el-dropdown style="width: 100%;height: 100%">
<div class="el-dropdown-link flex" style="width: 100%;height: 100%;justify-content: space-between;align-items: center"> <div class="el-dropdown-link flex" style="width: 100%;height: 100%;justify-content: space-between;align-items: center">
<span>{{titleName}}</span> <span>{{titleName}}</span>
<!-- <i class="iconfont icon-xiangyou"></i>--> <i class="iconfont icon-xiangyou"></i>
</div> </div>
<!-- 学科学段选择-->
<template #dropdown> <template #dropdown>
<div style="width: 300px;padding: 20px"> <div style="width: 300px;padding: 20px">
<ThirdIndex @getVertion="getVertion"></ThirdIndex> <ThirdIndex @getVertion="getVertion"></ThirdIndex>
@ -30,12 +29,10 @@ import ThirdIndex from './third/index.vue'
import {nextTick, reactive, ref,onMounted,watch} from 'vue' import {nextTick, reactive, ref,onMounted,watch} from 'vue'
import {getBook, getTextbook} from '@/api/file/third' import {getBook, getTextbook} from '@/api/file/third'
import useThirdStore from '@/store/modules/thirdTextbook' import useThirdStore from '@/store/modules/thirdTextbook'
import useUserStore from '@/store/modules/user'
const useThird = useThirdStore() const useThird = useThirdStore()
const useStore = useUserStore()
const emit = defineEmits(['nodeClick']) const emit = defineEmits(['nodeClick'])
const titleName = ref('') const titleName = ref('小学-语文')
// //
const treeData = ref([]) const treeData = ref([])
// //
@ -161,12 +158,16 @@ const handleNodeClick = (data,node) => {
} }
// //
onMounted(() => { onMounted(() => {
titleName.value = `${useStore.user.edustage}-${useStore.user.edusubject}` const info = localStorage.getItem('selectBookInfo')
if(info){
const {gradeName,subjectName} = JSON.parse(info)
titleName.value = `${gradeName}-${subjectName}`
}
treeLoading.value = true treeLoading.value = true
}) })
// //
watch(() => useThird,() => { watch(() => useThird,() => {
titleName.value = `${useStore.user.edustage}-${useStore.user.edusubject}` titleName.value = `${useThird.gradeName}-${useThird.subjectName}`
},{deep:true}) },{deep:true})
const defaultProps = { const defaultProps = {

View File

@ -14,12 +14,10 @@ import { gradeList } from '@/utils/resourceDict'
import SelectSubject from './selectSubject.vue' import SelectSubject from './selectSubject.vue'
import {getSubjects,getTextbookVersion} from '@/api/file/third' import {getSubjects,getTextbookVersion} from '@/api/file/third'
import useThirdStore from '@/store/modules/thirdTextbook' import useThirdStore from '@/store/modules/thirdTextbook'
import useUserStore from '@/store/modules/user'
const useThird = useThirdStore() const useThird = useThirdStore()
const useStore = useUserStore()
const emit = defineEmits(['getVertion']) const emit = defineEmits(['getVertion'])
const active = ref(1) const active = ref(useThird.activeGrade || gradeList[0].value)
// //
const subjectList = ref([]) const subjectList = ref([])
//id //id
@ -29,17 +27,9 @@ const handleClick = (tab) => {
} }
// //
const getSubject = (value) => { const getSubject = (value) => {
const currentIndex = gradeList.findIndex(item => item.value === value)
getSubjects({stage:value}).then(res => { getSubjects({stage:value}).then(res => {
if(res.code === 200){ if(res.code === 200){
subjectList.value = res.data.map(item => { subjectList.value = [...res.data]
return {
...item,
gradeName:gradeList[currentIndex].label
}
})
const nameIndex = subjectList.value.findIndex(item => item.subjectName === useStore.user.edusubject)
getTagId({subjectId:subjectList.value[nameIndex].subjectId,subjectName:subjectList.value[nameIndex].subjectName})
if(textbookVersionId.value === 0){ if(textbookVersionId.value === 0){
getTagId(subjectList.value[0]) getTagId(subjectList.value[0])
} }
@ -54,14 +44,21 @@ const getTagId = (item) => {
if(res.code === 200){ if(res.code === 200){
emit('getVertion',res.data) emit('getVertion',res.data)
useThird.getSelectBookInfo({...item,activeGrade:active.value,gradeName:gradeList[currentIndex].label}) useThird.getSelectBookInfo({...item,activeGrade:active.value,gradeName:gradeList[currentIndex].label})
localStorage.setItem('selectBookInfo',JSON.stringify({...item,activeGrade:active.value,gradeName:gradeList[currentIndex].label}))
} }
}) })
} }
onMounted(() => { onMounted(() => {
const currentIndex = gradeList.findIndex(item => item.label === useStore.user.edustage)
// //
getSubject(gradeList[currentIndex].value) handleClick(active.value)
active.value = gradeList[currentIndex].value //
const info = localStorage.getItem('selectBookInfo')
if(info){
const {activeGrade,subjectId,subjectName} = JSON.parse(info)
active.value = activeGrade
//
getTagId({subjectId:subjectId,subjectName:subjectName})
}
}) })
</script> </script>

View File

@ -1,11 +1,11 @@
<template> <template>
<template v-for="item in subjectList" :key="item.subjectId"> <template v-for="item in subjectList" :key="item.subjectId">
<el-tag <el-tag
:type="useStore.user.edusubject === item.subjectName && useStore.user.edustage === item.gradeName ? 'primary' : 'info'" type="primary"
effect="dark" effect="dark"
round round
class="tagItem" style="cursor: pointer;margin-right: 5px;margin-top: 5px;"
:style="useStore.user.edusubject === item.subjectName && useStore.user.edustage === item.gradeName ? {'cursor': 'pointer'} : {'cursor': 'no-Drop'}" @click="clickTag(item)"
> >
{{ item.subjectName }} {{ item.subjectName }}
</el-tag> </el-tag>
@ -13,8 +13,6 @@
</template> </template>
<script setup name="selectSubject"> <script setup name="selectSubject">
import useUserStore from "@/store/modules/user";
const useStore = useUserStore()
const props = defineProps({ const props = defineProps({
subjectList: { subjectList: {
type: Array, type: Array,
@ -22,14 +20,11 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['clickTag']) const emit = defineEmits(['clickTag'])
// const clickTag = (item) => { const clickTag = (item) => {
// emit('clickTag',item) emit('clickTag',item)
// } }
</script> </script>
<style scoped> <style scoped>
.tagItem{
margin-right: 5px;
margin-top: 5px;
}
</style> </style>

View File

@ -1,124 +0,0 @@
<template>
<el-drawer v-model="model" class="preview-drawer" title="title" :modal="false" :with-header="false" append-to-body
size="50%">
<div class="flex drawer-header">
<div>
<div class="flex file-name">
<FileImage :size="30" :file-name="'row.fileName'" />
<span class="name">{{ row.fileShowName }}</span>
</div>
<div class="flex file-tag">
<el-tag type="info" class="tag">{{ row.fileSuffix }}</el-tag>
<el-tag type="info" class="tag">{{ row.fileFlag }}</el-tag>
</div>
</div>
<div class="header-close" @click="onClose"><i class="iconfont icon-guanbi"></i></div>
</div>
<div class="drawer-content">
<!-- <iframe src="./aaa.pdf" width="600px" height="600px"></iframe> -->
<!-- <div ref="playerRef" class="video-box"></div> -->
<!-- <video src="" controls autoplay></video> -->
<el-image style="width: 100%;" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" />
</div>
</el-drawer>
</template>
<script setup>
import { watch, ref, nextTick } from 'vue'
import Player from 'xgplayer'
import 'xgplayer/dist/index.min.css'
import FileImage from '@/components/file-image/index.vue'
const model = defineModel()
const props = defineProps({
row: {
type: Object,
default(){
return {}
}
},
})
const playerRef = ref();
const handleClose = () => {
}
//
const onClose = () => {
model.value = false
}
const init = () => {
nextTick(() => {
//
let player = new Player({
el: playerRef.value,
url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4',
width: '100%',
autoplay: false,// false
videoAttributes: {// video
crossOrigin: 'anonymous',// CORS
},
cssFullscreen: false,
loop:true,// false
commonStyle:{
progressColor: '#cccccce6',//
},
});
})
}
watch(model, (newVal) => {
if (newVal) {
// init()
}
})
</script>
<style lang="scss" scoped>
:deep(.el-drawer__body) {
padding: 0;
}
.preview-drawer {
padding: 0;
}
.drawer-header {
justify-content: space-between;
align-items: center;
background-color: #fff;
.file-name {
align-items: center;
margin-bottom: 5px;
.name {
margin-left: 5px;
}
}
.file-tag {
.tag {
margin-right: 10px;
}
}
.header-close {
cursor: pointer;
.icon-guanbi {
font-size: 20px;
}
}
}
.drawer-content {
margin-top: 10px;
}
.video-box {
width: 100%;
height: 300px
}
</style>

View File

@ -1,323 +0,0 @@
<template>
<el-dialog v-model="model" center top="10vh" width="600px" :show-close="false" destroy-on-close append-to-body
style="border-radius: 10px; padding: 10px 15px">
<template #header>
<div class="homerwork-header flex">
<span>{{ title }}</span>
<i class="iconfont icon-guanbi" @click="cloneDialog(ruleFormRef)"></i>
</div>
</template>
<div v-loading="setLoading">
<el-form :model="form" label-width="80px" ref="ruleFormRef" :rules="rules">
<el-form-item label="班级" prop="grade">
<el-scrollbar max-height="200px" style="width: 100%">
<el-tree :props="defaultProps" :load="getLoad" node-key="id" highlight-current @check="handleCheckChange"
lazy show-checkbox />
</el-scrollbar>
</el-form-item>
<el-form-item label="选中学生" prop="student">
<el-scrollbar max-height="200px">
<el-tag v-for="(tag, index) in studentList" :key="tag.studentid" closable type="primary"
@close="delStudent(index)">
{{ tag.name }}
</el-tag>
</el-scrollbar>
</el-form-item>
<el-form-item label="完成要求" prop="feedback">
<el-radio-group v-model="form.feedback">
<el-radio value="必做" size="large">必做</el-radio>
<el-radio value="选做" size="large">选做</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="截止时间" prop="deaddate">
<el-date-picker v-model="form.deaddate" value-format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
time-format="HH:mm" type="datetime" :clearable="false" placeholder="请选择截止时间" />
</el-form-item>
<el-form-item label="推荐用时" prop="timelength">
<el-input-number v-model="form.timelength" :min="1" :max="500" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click.stop="cloneDialog(ruleFormRef)">取消</el-button>
<el-button type="primary" @click.stop="onSubmit(ruleFormRef)"> 确定 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { listClassmain, listClassgroup } from '@/api/classManage/index'
import { saveByClassWorkArray } from '@/api/teaching/classwork'
import useUserStore from '@/store/modules/user'
import { uniqBy, groupBy } from 'lodash'
const model = defineModel({ type: Boolean, default: false })
const props = defineProps({
entpcourseid: {
default: ''
},
row: {
default: ''
},
title: {
type: String,
default: '布置作业'
}
})
const emit = defineEmits(['on-close', 'on-success'])
const ruleFormRef = ref('')
//
const defaultProps = {
children: 'children',
label: 'label',
isLeaf: 'leaf'
}
// loading
const setLoading = ref(false)
//
const userInfo = useUserStore().user
//
const gradeList = ref([])
//
const studentList = ref([])
//
const form = reactive({
feedback: '必做',
deaddate: '',
timelength: 1
})
//
const validateGrade = (rule, value, callback) => {
if (studentList.value.length == 0) {
callback(new Error('请勾选班级或者学生'))
} else {
callback()
}
}
const validateStudent = (rule, value, callback) => {
if (studentList.value.length == 0) {
callback(new Error('学生不能为空'))
} else {
callback()
}
}
const rules = reactive({
grade: [{ validator: validateGrade, trigger: 'blur' }],
student: [{ validator: validateStudent, trigger: 'blur' }]
})
//
const getGradeList = async () => {
let { rows } = await listClassmain({
classuserid: userInfo.userId,
pageSize: 100,
status: 'open'
})
rows.forEach((item) => {
item.label = item.caption
item.level = 0
item.leaf = false
item.children = []
item.classstudentlist = JSON.parse('[' + item.classstudentlist + ']')
item.classstudentlist.forEach((el) => {
el.classId = item.id
})
})
return rows
}
//
const getLoad = async (node, resolve) => {
//
if (node.level == 0) {
gradeList.value = await getGradeList()
resolve(gradeList.value)
}
//
if (node.level == 1) {
listClassgroup({ classid: node.key, orderby: 'orderidx', pageSize: 100 }).then((res) => {
if (res.rows.length > 0) {
let ary = []
res.rows.forEach((item) => {
if (item.parentid === 0) {
//studentGroup
let studentGroup = JSON.parse('[' + item.studentlist + ']')
studentGroup.forEach((el) => {
el.label = el.name
el.leaf = true
el.level = 2
el.id = el.studentid
el.classId = item.classid
})
ary.push({
label: item.groupname,
leaf: false,
...item,
level: 1,
// children
children: studentGroup
})
}
})
resolve(ary)
} else {
//
let students = node.data.classstudentlist
students.forEach((item) => {
item.label = item.name
item.level = 2
item.leaf = true
})
resolve(students)
}
})
}
//
if (node.level == 2) {
resolve(node.data.children)
}
}
//
const handleCheckChange = (data, checked) => {
studentList.value = []
//
let checkNodes = checked.checkedNodes
let ary = []
checkNodes.forEach((item) => {
//
if (item.level == 0) {
ary = [...ary, ...item.classstudentlist]
}
//
if (item.level == 1) {
ary = [...ary, ...item.children]
}
//
if (item.level == 2) {
ary = [...ary, item]
}
})
studentList.value = uniqBy(ary, 'studentid')
}
//
const delStudent = (index) => {
studentList.value.splice(index, 1)
}
const onSubmit = (formEl) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
/**
* 根据学生列表中的classId分班
* studentList 为选中的所有学生 这些学生可能来自不同班级
*/
let gradeObj = groupBy(studentList.value, 'classId')
//
let ary = []
for (const value in gradeObj) {
// AIx web
let obj = {
id: 0,
parentid: props.row.id,
classid: value,
classcourseid: 0,
entpcourseid: props.entpcourseid,
studentlist: JSON.stringify(gradeObj[value]),
feedback: form.feedback,
workkey: '',
timelength: form.timelength,
weights: 1,
deaddate: form.deaddate,
workdate: getCurrentDate(),
uniquekey: props.row.uniquekey,
entpcourseworklist: '[' + props.row.entpcourseworklist + ']',
needMsgNotifine: 'false',
msgkey: 'newclasswork',
title: '作业任务',
msgcontent: '',
teachername: userInfo.nickName,
unixstamp: new Date().getTime(),
worktype: props.row.worktype
}
ary.push(obj)
}
setLoading.value = true
saveByClassWorkArray({
classworkarray: JSON.stringify(ary)
})
.then(() => {
setLoading.value = false
ElMessage.success('操作成功')
emit('on-success')
cloneDialog(formEl)
})
.catch(() => {
setLoading.value = false
})
} else {
return false
}
})
}
const expandedKeys = ref([])
const checkedKeys = ref([])
//
const cloneDialog = (formEl) => {
studentList.value = []
checkedKeys.value = []
expandedKeys.value = []
formEl.resetFields()
model.value = false
}
//
const getCurrentDate = () => {
const now = new Date()
const year = now.getFullYear()
let month = now.getMonth() + 1 // 0+1
let day = now.getDate()
if (month < 10) {
month = '0' + month
}
if (day < 10) {
day = '0' + day
}
return `${year}-${month}-${day}`
}
onMounted(() => {
//
form.deaddate = getCurrentDate() + ' ' + '10:00:00'
})
</script>
<style lang="scss" scoped>
.homerwork-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.el-tag {
margin-right: 10px;
margin-bottom: 10px;
}
.dialog-footer {
padding-bottom: 10px;
}
:deep(.el-checkbox) {
transform: scale(1.3);
}
</style>

View File

@ -169,12 +169,12 @@ export class ImChat {
logout() { logout() {
if (!this.timChat) return if (!this.timChat) return
return this.timChat.TIMLogout().then(res => { return this.timChat.TIMLogout().then(res => {
this.setConsole('%cim-chat: logout', '登出成功') console.log('登出成功', res)
this.status.isLogin = false this.status.isLogin = false
this.timChat.TIMUninit() // 反初始化 this.timChat.TIMUninit() // 反初始化
return res return res
}).catch(error => { }).catch(error => {
this.setConsole('%cim-chat: logout', '登出失败', error) console.log('登出失败', error)
return error return error
}) })
} }

View File

@ -88,10 +88,6 @@ export const gradeList = [
] ]
//课件类别 //课件类别
export const coursewareTypeList = [ export const coursewareTypeList = [
{
label:'全部',
value:''
},
{ {
label:'课件', label:'课件',
value:3 value:3

View File

@ -0,0 +1,332 @@
<template>
<el-dialog v-model="dialogVisible" center top="10vh" width="600px" :show-close="false" append-to-body
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="homerwork-header flex">
<span>{{ title }}</span>
<i class="iconfont icon-guanbi" @click="cloneDialog"></i>
</div>
</template>
<div v-loading="setLoading">
<el-form :model="form" label-width="80px" ref="ruleForm" :rules="rules">
<el-form-item label="班级" prop="grade">
<el-scrollbar max-height="200px" style="width: 100%;">
<el-tree ref="treeRef" :data="gradeList" :props="defaultProps" :load="getLoad" node-key="id"
@check="handleCheckChange" lazy show-checkbox />
</el-scrollbar>
</el-form-item>
<el-form-item label="选中学生" prop="student">
<el-scrollbar max-height="200px">
<el-tag v-for="(tag, index) in studentList" :key="tag.studentid" closable type="primary"
@close="delStudent(index)">
{{ tag.name }}
</el-tag>
</el-scrollbar>
</el-form-item>
<el-form-item label="完成要求" prop="feedback">
<el-radio-group v-model="form.feedback">
<el-radio value="必做" size="large">必做</el-radio>
<el-radio value="选做" size="large">选做</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="截止时间" prop="deaddate">
<el-date-picker v-model="form.deaddate" value-format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
time-format="HH:mm" type="datetime" :clearable="false" placeholder="请选择截止时间" />
</el-form-item>
<el-form-item label="推荐用时" prop="timelength">
<el-input-number v-model="form.timelength" :min="1" :max="500" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click.stop="cloneDialog">取消</el-button>
<el-button type="primary" @click.stop="onSubmit('ruleForm')">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import { ElMessage } from 'element-plus'
import { listClassmain, listClassgroup } from '@/api/classManage/index'
import { saveByClassWorkArray } from '@/api/teaching/classwork'
import useUserStore from '@/store/modules/user'
import { uniqBy, groupBy } from 'lodash'
export default {
props: {
modelValue: {
type: Boolean,
default: false
},
entpcourseid: {
default: ''
},
row: {
default: ''
},
title: {
type: String,
default: '布置作业'
}
},
data() {
return {
dialogVisible: false,
defaultProps: {
children: 'children',
label: 'label',
isLeaf: 'leaf',
},
setLoading: false,
//
userInfo: null,
//
gradeList: [],
curGradeId: '',
//
groupList: [],
//
studentList: [],
//
form: {
feedback: '必做',
deaddate: '',
timelength: 1
},
//
rules: {
grade: [
{ validator: this.validateGrade, trigger: 'blur' }
],
student: [
{ validator: this.validateStudent, trigger: 'blur' }
]
}
}
},
created() {
//
this.form.deaddate = this.getCurrentDate() + ' ' + '10:00:00'
this.userInfo = useUserStore().user
},
methods: {
//
getGradeList() {
listClassmain({ classuserid: this.userInfo.userId, pageSize: 100, status: 'open' }).then(res => {
let list = res.rows
list.forEach(item => {
item.label = item.caption
item.level = 0
item.children = []
item.classstudentlist = JSON.parse("[" + item.classstudentlist + "]")
item.classstudentlist.forEach(el => {
el.classId = item.id
})
})
this.gradeList = list
})
},
//
getLoad(node, resolve) {
if (node.level == 0) return resolve([])
//
if (node.level == 1) {
listClassgroup({ classid: node.key, orderby: 'orderidx', pageSize: 100 }).then(res => {
if (res.rows.length > 0) {
let ary = []
res.rows.forEach(item => {
if (item.parentid === 0) {
//studentGroup
let studentGroup = JSON.parse("[" + item.studentlist + "]")
studentGroup.forEach(el => {
el.label = el.name
el.leaf = true
el.level = 2,
el.id = el.studentid
el.classId = item.classid
})
ary.push({
label: item.groupname,
...item,
level: 1,
children: studentGroup
})
}
})
resolve(ary)
}
else {
resolve([])
}
})
}
//
if (node.level == 2) {
resolve(node.data.children)
}
},
//
handleCheckChange(data, checked) {
this.studentList = []
//
let checkNodes = checked.checkedNodes
let ary = []
checkNodes.forEach(item => {
//
if (item.level == 0) {
ary = [...ary, ...(item.classstudentlist)]
}
//
if (item.level == 1) {
ary = [...ary, ...(item.children)]
}
//
if (item.level == 2) {
ary = [...ary, item]
}
})
this.studentList = uniqBy(ary, 'studentid')
},
//
delStudent(index) {
this.studentList.splice(index, 1)
},
onSubmit(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
/**
* 根据学生列表中的classId分班
* studentList 为选中的所有学生 这些学生可能来自不同班级
*/
let gradeObj = groupBy(this.studentList, 'classId')
//
let ary = []
for (const value in gradeObj) {
// AIx web
let obj = {
id: 0,
parentid: this.row.id,
classid: value,
classcourseid: 0,
entpcourseid: this.entpcourseid,
studentlist: JSON.stringify(gradeObj[value]),
feedback: this.form.feedback,
workkey: "",
timelength: this.form.timelength,
weights: 1,
deaddate: this.form.deaddate,
workdate: this.getCurrentDate(),
uniquekey: this.row.uniquekey,
entpcourseworklist: "[" + this.row.entpcourseworklist + "]",
needMsgNotifine: 'false',
msgkey: 'newclasswork',
title: "作业任务",
msgcontent: '',
teachername: this.userInfo.nickName,
unixstamp: new Date().getTime(),
worktype: this.row.worktype
}
ary.push(obj)
}
this.setLoading = true
saveByClassWorkArray({
classworkarray: JSON.stringify(ary)
}).then(() => {
this.setLoading = false
ElMessage.success('操作成功')
this.cloneDialog()
}).catch(()=>{
this.setLoading = false
})
} else {
return false
}
})
},
//
cloneDialog() {
this.$emit('on-close')
this.studentList = []
this.$refs['ruleForm'].resetFields();
},
//
getCurrentDate() {
const now = new Date();
const year = now.getFullYear();
let month = now.getMonth() + 1; // 0+1
let day = now.getDate()
if(month < 10){
month = '0' + month
}
if(day < 10){
day = '0' + day
}
return `${year}-${month}-${day}`;
},
validateGrade(rule, value, callback) {
if (this.studentList.length == 0) {
callback(new Error('请勾选班级或者学生'));
}
else {
callback()
}
},
validateStudent(rule, value, callback) {
if (this.studentList.length == 0) {
callback(new Error('学生不能为空'));
}
else {
callback()
}
},
},
watch: {
modelValue(val) {
this.dialogVisible = val
if (val) {
this.getGradeList()
}
}
},
}
</script>
<style lang="scss" scoped>
.homerwork-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.el-tag {
margin-right: 10px;
margin-bottom: 10px;
}
.dialog-footer{
padding-bottom: 10px
}
:deep(.el-checkbox){
transform : scale(1.3)
}
:deep(.el-icon){
transform : scale(1.3)
}
</style>

View File

@ -114,6 +114,7 @@
v-model="setDialog" v-model="setDialog"
:entpcourseid="entpcourseid" :entpcourseid="entpcourseid"
:row="row" :row="row"
@on-close="closeHomework"
/> />
</div> </div>
<reserv <reserv
@ -142,7 +143,7 @@ import { toTimeText } from '@/utils/date'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { parseCataByNode, creatPPT, asyncLocalFile } from '@/utils/talkFile' import { parseCataByNode, creatPPT, asyncLocalFile } from '@/utils/talkFile'
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue' import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
import SetHomework from '@/components/set-homework/index.vue' import SetHomework from './container/set-homework.vue'
import outLink from '@/utils/linkConfig' import outLink from '@/utils/linkConfig'
import { createWindow } from '@/utils/tool' import { createWindow } from '@/utils/tool'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
@ -486,6 +487,10 @@ export default {
this.isLoading = false this.isLoading = false
}) })
}, },
closeHomework() {
this.setDialog = false
},
// PDF- // PDF-
async navtoPdf() { async navtoPdf() {
// if (toolStore.isPdfWin) return this.$message.error('') // if (toolStore.isPdfWin) return this.$message.error('')

View File

@ -53,7 +53,7 @@
</template> </template>
<script setup> <script setup>
import {watch,ref,onMounted} from 'vue' import {watch,ref} from 'vue'
import useResoureStore from '../store' import useResoureStore from '../store'
import {coursewareTypeList} from '@/utils/resourceDict' import {coursewareTypeList} from '@/utils/resourceDict'
const isThird = ref(false) const isThird = ref(false)
@ -71,9 +71,6 @@ const debouncedChangeName = debounce(sourceStore.changeName, 300);
const onchangeInput = () => { const onchangeInput = () => {
debouncedChangeName() debouncedChangeName()
} }
onMounted(() => {
sourceStore.query.fileSource = '平台'
})
watch(() => sourceStore.query.fileSource,() => { watch(() => sourceStore.query.fileSource,() => {
sourceStore.query.fileSource === '第三方'?isThird.value = true:isThird.value = false sourceStore.query.fileSource === '第三方'?isThird.value = true:isThird.value = false

View File

@ -69,7 +69,7 @@ export default defineStore('resource', {
bookId:0, bookId:0,
stage:0, stage:0,
subjectId:0, subjectId:0,
type:'', type:3,
title:'' title:''
}, },
result: { result: {

View File

@ -51,7 +51,7 @@
</el-scrollbar> </el-scrollbar>
</div> </div>
<SetHomework v-model="setDialog" :title="'推送作业'" :entpcourseid="entpcourseid" :row="curRow" <SetHomework v-model="setDialog" :title="'推送作业'" :entpcourseid="entpcourseid" :row="curRow"
@on-close="closeHomework" @on-success="successHomework"/> @on-close="closeHomework" />
</div> </div>
<div class="homework flex" v-else> <div class="homework flex" v-else>
<div class="unit-top flex"> <div class="unit-top flex">
@ -73,12 +73,13 @@
import { ref, onMounted, toRaw } from 'vue'; import { ref, onMounted, toRaw } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { getSmarttalkPage } from '@/api/file' import { getSmarttalkPage, getPrepareById } from '@/api/file'
import SetHomework from '@/components/set-homework/index.vue' import SetHomework from '@/views/prepare/container/set-homework.vue'
import FileImage from '@/components/file-image/index.vue' import FileImage from '@/components/file-image/index.vue'
import { useGetHomework } from '@/hooks/useGetHomework' import { useGetHomework } from '@/hooks/useGetHomework'
import { ipcMsgSend, ipcMsgSend2, ipcMsgInvoke } from '@/utils/tool' import { ipcMsgSend, ipcMsgInvoke } from '@/utils/tool'
import { useToolState } from '@/store/modules/tool' import { useToolState } from '@/store/modules/tool'
import { asyncLocalFile } from '@/utils/talkFile'
import Lesson from './lesson.vue'; import Lesson from './lesson.vue';
import { parseCataByNode } from '@/utils/talkFile' import { parseCataByNode } from '@/utils/talkFile'
import outLink from '@/utils/linkConfig' import outLink from '@/utils/linkConfig'
@ -96,9 +97,11 @@ const dataList = ref([])
const setDialog = ref(false) const setDialog = ref(false)
// //
const resourceList = ref([]) const resourceList = ref([])
//
//
const curRow = ref('') const curRow = ref('')
const sendHomework = (row) => { const sendHomework = (row) => {
curRow.value = row curRow.value = row
setTimeout(() => { setTimeout(() => {
@ -109,13 +112,12 @@ const sendHomework = (row) => {
// //
const closeHomework = async() => { const closeHomework = async() => {
ipcMsgSend('tool-sphere:set:ignore', true) ipcMsgSend('tool-sphere:set:ignore', true)
}
//
const successHomework = ()=>{
// im-(app|) // im-(app|)
ipcMsgInvoke('im-chat:msg', curRow.value.id, MsgEnum.HEADS.MSG_0016) await ipcMsgInvoke('im-chat:msg', curRow.value.id, MsgEnum.HEADS.MSG_0016)
setDialog.value = false
} }
// change
const changeChapter = async (data)=>{ const changeChapter = async (data)=>{
const { res } = await useGetHomework(data) const { res } = await useGetHomework(data)
dataList.value = res dataList.value = res

View File

@ -13,8 +13,9 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, nextTick, toRaw } from 'vue' import { ref, onMounted, nextTick, markRaw, toRaw } from 'vue'
import { useToolState } from '@/store/modules/tool' import { useToolState } from '@/store/modules/tool'
import { parseCataByNode } from '@/utils/talkFile'
const emit = defineEmits(['changeChapter']) const emit = defineEmits(['changeChapter'])
const toolStore = useToolState() const toolStore = useToolState()

View File

@ -1,15 +1,9 @@
<template> <template>
<div v-if="props.test"> <div v-if="props.test">
<el-button type="primary" @click="trigger" v-tap:trigger="">点赞</el-button> <el-button type="primary" @click="trigger">点赞</el-button>
<el-button type="primary" @click="trigger(2, '学生A')" v-tap:trigger="[2,'学生A']">疑惑</el-button> <el-button type="primary" @click="trigger(1, '学生A')">疑惑</el-button>
</div> </div>
<!-- 温度计-模式 --> <div ref="warpRef" class="c-warp">
<div v-if="props.type == 2" class="c-anim">
<div class="item like" :style="`height:${wtData.like*5}px;`"></div>
<div class="item doubt" :style="`height:${wtData.doubt*5}px;`"></div>
</div>
<!-- (默认)弹幕-模式 -->
<div v-else ref="warpRef" class="c-warp">
<template v-for="(item, index) in iconCache.list"> <template v-for="(item, index) in iconCache.list">
<slot> <slot>
<el-icon v-if="props.def"><Star /></el-icon> <el-icon v-if="props.def"><Star /></el-icon>
@ -25,46 +19,29 @@
</slot> </slot>
</template> </template>
</div> </div>
</template> </template>
<script setup> <script setup>
// - // -
import { ref, nextTick, useSlots, reactive } from 'vue' import { ref, nextTick, useSlots, reactive } from 'vue'
import {Star} from '@element-plus/icons-vue' import {Star} from '@element-plus/icons-vue'
const warpRef = ref(null) const warpRef = ref(null)
const props = defineProps({ test: Boolean, def: Boolean })
const slots = useSlots() // const slots = useSlots() //
const isSlot = !!Object.keys(slots).length // 使
const props = defineProps({
test: Boolean, // -
def: Boolean, // 使
type: String, // 1- 2-
tback: Number, // -退
tsleep: Number, // -退
})
let iconCache = reactive({list:[]}) // let iconCache = reactive({list:[]}) //
let wtData = reactive({ // - const isSlot = !!Object.keys(slots).length // 使
like: 0, doubt: 0, // === type 1 def ===
timeout: {like: null, doubt: null}, // -
interval: {like: null, doubt: null}, // -
})
// === type 1: 2: ===
const trigger = async (type, name) => { const trigger = async (type, name) => {
if (typeof type == 'string') { name = type; type = 1 } // if (typeof type == 'string') { name = type; type = 1 } //
if (props.type == 2) { // - iconCache.list.push({
weatherglassModel(type) name,
} else { // - icon: isSlot||props.def ? '' : getRandomIcon(type) //
iconCache.list.push({ })
name, nextTick(() => {
icon: isSlot||props.def ? '' : getRandomIcon(type) // const el = warpRef.value.lastElementChild //
}) const tipEl = el.querySelector('.tip') //
nextTick(() => { // console.log(el)
const el = warpRef.value.lastElementChild // animInit(el, tipEl, type)
const tipEl = el.querySelector('.tip') // })
// console.log(el)
animInit(el, tipEl, type)
})
}
} }
// //
const animInit = (el, tipEl, type) => { const animInit = (el, tipEl, type) => {
@ -118,31 +95,7 @@ const animInit = (el, tipEl, type) => {
} }
amFn() // amFn() //
} }
// - 1: 2:
const weatherglassModel = async(type) => {
const field = type == 2 ? 'doubt' : 'like'
wtData[field]++
autoReduce(type)
}
// -
const autoReduce = type => {
const field = type == 2 ? 'doubt' : 'like'
const reduceFn = () => { // : 1s
wtData.interval[field] = setInterval(() => {
wtData[field]--
// -
if (wtData[field] <= 0) {
clearInterval(wtData.interval[field])
wtData[field] = 0
}
}, (props.tback||100))
}
if (!!wtData.timeout[field]) clearInterval(wtData.timeout[field]) //
if (!!wtData.interval[field]) clearInterval(wtData.interval[field]) //
wtData.timeout[field] = setTimeout(() => {
reduceFn()
}, (props.tsleep||20) * 1000)
}
// - // -
const setStatic = (el, type, val, end) => el.style[type] = val + (end || '') const setStatic = (el, type, val, end) => el.style[type] = val + (end || '')
// //
@ -154,7 +107,7 @@ const resetIconCache = () => {
const getRandomIcon = (type) => { const getRandomIcon = (type) => {
let iconArr = [] let iconArr = []
switch (type) { switch (type) {
case 2: // icon- case 1: // icon-
iconArr = ['yiwen', 'a-yiwen', 'yihuo', 'yiwen-01'] iconArr = ['yiwen', 'a-yiwen', 'yihuo', 'yiwen-01']
break break
default: default:
@ -179,28 +132,11 @@ const getRandomBool = () => Math.random() > 0.5
const toNumber = (v, pos = 2) => Number(v.toFixed(pos)) const toNumber = (v, pos = 2) => Number(v.toFixed(pos))
// //
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
// -
const vTap = {
mounted(el, binding) {
const instance = binding.instance
const fn = binding.arg
const args = binding.value||[]
if (!fn) return
let cleId = null
el.addEventListener('mousedown', () => {
cleId = setInterval(() => {instance[fn](...args)}, 10);
})
el.addEventListener('mouseup', () => {
!!cleId && clearInterval(cleId)
})
}
}
// //
defineExpose({ trigger }) defineExpose({ trigger })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// -
.c-warp { .c-warp {
position: fixed; position: fixed;
right: 0; right: 0;
@ -243,31 +179,4 @@ defineExpose({ trigger })
} }
} }
} }
// -
.c-anim {
position: fixed;
// height: 90vh;
// border: 1px solid;
inset: auto auto 3em 1em;
display: flex;
gap: 10px;
align-items: flex-end;
.item{
flex: 1;
border-radius: 3px;
min-width: 15px;
// height: 500px;
&.like{
background-image: linear-gradient(to top, #fef0f0, #f56c6c);
// animation: striped-flow 5s linear infinite;
}
&.doubt{
background-image: linear-gradient(to top, #fdf6ec, #e6a23c);
}
}
}
@keyframes striped-flow {
0%{background-position: 0 500px;}
100%{background-position: 0 0;}
}
</style> </style>

View File

@ -7,7 +7,7 @@
<side-vue v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue> <side-vue v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue>
<!-- 点赞组件 --> <!-- 点赞组件 -->
<upvote-vue ref="upvoteRef" type="2"></upvote-vue> <upvote-vue ref="upvoteRef"></upvote-vue>
<!-- im-chat 聊天组件 --> <!-- im-chat 聊天组件 -->
<im-chat ref="imChatRef" @change="chatChange" /> <im-chat ref="imChatRef" @change="chatChange" />
@ -67,8 +67,7 @@ const classObj = reactive({ // 课程相关
data: {} // data: {} //
}) })
const msgIds = [] // id const msgIds = [] // id
const electron = window.electron // electron const btnList = [ //
const btnList = [ //
{ label: '选择', value: 'select', icon: 'icon-mouse' }, { label: '选择', value: 'select', icon: 'icon-mouse' },
{ label: '画笔', value: 'brush', icon: 'icon-huabi' }, { label: '画笔', value: 'brush', icon: 'icon-huabi' },
{ label: '板擦', value: 'erase', icon: 'icon-xiangpica' }, { label: '板擦', value: 'erase', icon: 'icon-xiangpica' },
@ -79,10 +78,9 @@ const btnList = [ // 工具栏按钮列表
] ]
// === === // === ===
onMounted(async() => { onMounted(async() => {
if (!electron) return // getClassInfo() // ex3
getClassInfo() // ex3
setTimeout(() => { setTimeout(() => {
resetStatus() // - resetStatus() // -
}, 200); }, 200);
}) })
@ -132,7 +130,7 @@ const chatChange = (type, data, ...args) => {
case MsgEnum.HEADS.MSG_0002: // case MsgEnum.HEADS.MSG_0002: //
// console.log(':', data) // console.log(':', data)
if(msgIds.includes(msgId)) return // - if(msgIds.includes(msgId)) return // -
upvoteRef.value.trigger(head, data.name) // | upvoteRef.value.trigger(data.name) // |
if (msgIds.length >= 100) msgIds.shift() // if (msgIds.length >= 100) msgIds.shift() //
msgIds.push(msgId) // msgIds.push(msgId) //
break break