Merge pull request 'lyc-dev' (#153) from lyc-dev into main

This commit is contained in:
lyc 2024-08-23 11:22:52 +08:00
commit b922219602
9 changed files with 461 additions and 355 deletions

View File

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

View File

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

View File

@ -8,7 +8,7 @@
http-equiv="Content-Security-Policy"
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'; 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'; media-src *;img-src * 'self' data: blob:" />
</head>

View File

@ -0,0 +1,124 @@
<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

@ -0,0 +1,323 @@
<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

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

View File

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

View File

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