This commit is contained in:
zdg 2024-08-02 09:22:18 +08:00
commit bb02f92bb2
12 changed files with 730 additions and 221 deletions

View File

@ -31,7 +31,7 @@ function createLoginWindow() {
preload: join(__dirname, '../preload/index.js'), preload: join(__dirname, '../preload/index.js'),
sandbox: false, sandbox: false,
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false, // 沙箱取消 contextIsolation: false // 沙箱取消
} }
}) })
loginWindow.type = 'login' // 唯一标识 loginWindow.type = 'login' // 唯一标识
@ -41,7 +41,7 @@ function createLoginWindow() {
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
loginWindow.loadURL('http://localhost:5173/#/login') loginWindow.loadURL('http://localhost:5173/#/login')
} else { } else {
loginWindow.loadFile(join(__dirname, '../renderer/index.html'), {hash: 'login'}) loginWindow.loadFile(join(__dirname, '../renderer/index.html'), { hash: 'login' })
updateInit(loginWindow) updateInit(loginWindow)
} }
@ -71,7 +71,7 @@ function createMainWindow() {
sandbox: false, sandbox: false,
// nodeIntegration: true, // nodeIntegration: true,
nodeIntegration: true, // nodeApi调用 nodeIntegration: true, // nodeApi调用
contextIsolation: false, // 沙箱取消 contextIsolation: false // 沙箱取消
// webSecurity: false // 跨域关闭 // webSecurity: false // 跨域关闭
} }
}) })
@ -80,7 +80,8 @@ function createMainWindow() {
mainWindow.show() mainWindow.show()
}) })
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
setTimeout(() => { // 延迟销毁 setTimeout(() => {
// 延迟销毁
mainWindow = null mainWindow = null
}, 1000) }, 1000)
// app.quit() // 主窗口关闭-结束所有进程 // app.quit() // 主窗口关闭-结束所有进程
@ -92,7 +93,7 @@ function createMainWindow() {
mainWindow.webContents.openDevTools() mainWindow.webContents.openDevTools()
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] ) mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else { } else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html')) mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
} }
@ -102,7 +103,6 @@ function createMainWindow() {
remote.enable(mainWindow.webContents) remote.enable(mainWindow.webContents)
} }
// 作业窗口相关-开发中 // 作业窗口相关-开发中
let linkWindow let linkWindow
async function createLinkWin(data) { async function createLinkWin(data) {
@ -122,12 +122,15 @@ async function createLinkWin(data) {
}) })
linkWindow.type = 'link' // 唯一标识 linkWindow.type = 'link' // 唯一标识
let cookieDetails = { ...data.cookieData } let cookieDetails = { ...data.cookieData }
await linkWindow.webContents.session.cookies.set(cookieDetails).then(()=>{ await linkWindow.webContents.session.cookies
console.log('Cookie is successful'); .set(cookieDetails)
}).catch( error =>{ .then(() => {
console.error('Cookie is error', error); console.log('Cookie is successful')
}) })
.catch((error) => {
console.error('Cookie is error', error)
})
data.fullPath = data.fullPath.replaceAll('//', '/')
linkWindow.loadURL(data.fullPath) linkWindow.loadURL(data.fullPath)
linkWindow.once('ready-to-show', () => { linkWindow.once('ready-to-show', () => {
@ -169,9 +172,10 @@ app.on('ready', () => {
} }
if (mainWindow) { if (mainWindow) {
mainWindow.close() // 先发出这个关闭指令 mainWindow.close() // 先发出这个关闭指令
setTimeout(() => { // setTimeout(() => {
//
mainWindow.destroy() mainWindow.destroy()
}, 200); }, 200)
} }
}) })
@ -199,21 +203,19 @@ app.on('ready', () => {
createLinkWin(data) createLinkWin(data)
}) })
// 新窗口创建-监听 // 新窗口创建-监听
ipcMain.on('new-window', (e,data) => { ipcMain.on('new-window', (e, data) => {
const { id, type } = data const { id, type } = data
const win = BrowserWindow.fromId(id) const win = BrowserWindow.fromId(id)
win.type = type // 绑定独立标识 win.type = type // 绑定独立标识
remote.enable(win.webContents) // 开启远程服务 remote.enable(win.webContents) // 开启远程服务
}) })
// 打开-登录窗口 // 打开-登录窗口
createLoginWindow() createLoginWindow()
app.on('activate', function () { app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createLoginWindow() if (BrowserWindow.getAllWindows().length === 0) createLoginWindow()
}) })
}) })
// Quit when all windows are closed, except on macOS. There, it's common // Quit when all windows are closed, except on macOS. There, it's common

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'; 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'; img-src * 'self' data: blob:" />
</head> </head>

View File

@ -55,6 +55,8 @@
<script setup> <script setup>
import { ref, reactive, toRaw, onMounted, nextTick, watch } from 'vue' import { ref, reactive, toRaw, onMounted, nextTick, watch } from 'vue'
import useUserStore from '@/store/modules/user'
import { listEvaluation } from '@/api/subject'
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -66,6 +68,8 @@ const props = defineProps({
default: '移动至' default: '移动至'
} }
}) })
const userStore = useUserStore()
const { edustage, edusubject, userId } = userStore.user
const dialogVisible = ref(false) const dialogVisible = ref(false)
const bookVisible = ref(false) const bookVisible = ref(false)
@ -106,10 +110,26 @@ watch(() => props.modelValue, (newVal) => {
}) })
const getSubjectContent = () => { const getSubjectContent = async () => {
const params = {
edusubject,
edustage,
entpcourseedituserid: userId,
pageSize: 500
}
let data;
if (localStorage.getItem('evaluationList')) {
evaluationList.value = JSON.parse(localStorage.getItem('evaluationList')) evaluationList.value = JSON.parse(localStorage.getItem('evaluationList'))
let data = evaluationList.value data = evaluationList.value
}
else {
const { rows } = await listEvaluation(params)
localStorage.setItem('evaluationList', JSON.stringify(rows))
evaluationList.value = rows
data = rows
}
// //
getSubject() getSubject()
@ -121,8 +141,18 @@ const getSubjectContent = () => {
getTreeData() getTreeData()
} }
const getSubject = () => { const getSubject = async () => {
subjectList.value = JSON.parse(localStorage.getItem('subjectList')) subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
if (localStorage.getItem('subjectList')) {
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
}
else {
const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
}
// //
curBookName.value = subjectList.value[0].itemtitle curBookName.value = subjectList.value[0].itemtitle
curBookId.value = subjectList.value[0].id curBookId.value = subjectList.value[0].id
@ -297,7 +327,8 @@ onMounted(() => {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: #000; color: #000;
.icon-close{
.icon-close {
cursor: pointer; cursor: pointer;
} }
} }

View File

@ -1,7 +1,15 @@
<template> <template>
<section class="app-main"> <section class="app-main">
<div class="app-main-left no-select">
<div v-for="(item, index) in title" :key="index" :class="item.active?'active':''" class="app-main-left-item" @click="active(index)">
<div class="app-main-left-item-icon">
<i :class="item.img"></i>
</div>
<div class="app-main-left-item-text">{{item.name}}</div>
</div>
</div>
<transition mode="out-in" name="fade-transform"> <transition mode="out-in" name="fade-transform">
<div v-show="$route != null" style="height: 100%"> <div v-show="$route != null" style="height: 100%; flex: 1">
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<keep-alive> <keep-alive>
<component :is="Component" v-if="route.meta.keepAlive" :key="route.name" /> <component :is="Component" v-if="route.meta.keepAlive" :key="route.name" />
@ -13,10 +21,170 @@
</section> </section>
</template> </template>
<script setup></script> <script setup>
import { reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import routerStore from '@/store/modules/route'
const router = useRouter()
const title = reactive([
{
name: '教学工作台',
img: 'iconfont icon-gongzuotai',
id: 1,
child: [
{
name: '课程教学',
img: 'iconfont icon-PPT',
type: 'hash',
url: '/prepare',
child1: []
},
{
name: '作业管理',
img: 'iconfont icon-36zuoyepingtai',
url: '/teaching/classtaskassign?titleName=作业布置',
child1: []
}
]
},
{
name: '教学研究室',
img: 'iconfont icon-yanjiushi',
id: 2,
child: [
{
name: '资源研究',
url: '/resource',
type: 'hash',
img: 'iconfont icon-business-report',
child1: []
},
{
name: '课标分析',
url: '/teaching/chatwithstandard',
img: 'iconfont icon-kecheng',
child1: []
},
{
name: '教材分析',
url: '/teaching/chatwithtextbook',
img: 'iconfont icon-yanjiushi',
child1: []
},
{
name: '高考研究',
url: '/education/colentrance',
img: 'iconfont icon-icon_kaoshifenxi',
child1: []
}
]
},
{
name: '教学资源库',
img: 'iconfont icon-zhuanyeziyuanku',
id: 3,
child: [
{
name: '教学素材',
img: 'iconfont icon-sucai',
url: '/teaching/materialbank',
child1: []
},
{
name: '课程资源',
img: 'iconfont icon-kechengziyuan',
url: '/teaching/coursewareresource',
child1: []
},
{
name: '习题资源',
img: 'iconfont icon-iconku-zhuanqu-',
url: '/teaching/quesbank',
child1: []
}
]
}
])
const active = (index) => {
const route = routerStore()
title.forEach((item, i) => {
if (i === index) {
item.active = true
route.setNowRouter(item.child)
if (item.id !== 3) {
router.push(item.child[0].url)
}
} else {
item.active = false
}
})
}
onMounted(()=>{
active(0)
})
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.no-select {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
user-select: none; /* Standard syntax */
}
.app-main { .app-main {
height: 100%; height: 100%;
display: flex;
flex-direction: row;
.app-main-left {
width: 80px;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
.app-main-left-item {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 65px;
height: 65px;
padding-top: 10px;
margin: 10px 0;
cursor: pointer;
&:hover {
background: #839ce0;
border-radius: 10px;
.app-main-left-item-icon {
background: #fff;
color: #758fd3;
}
}
&.active {
background: #839ce0;
border-radius: 10px;
.app-main-left-item-icon {
background: #fff;
color: #758fd3;
}
}
.app-main-left-item-text {
font-size: 12px;
}
.app-main-left-item-icon {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
background: #7c97e1;
color: #fff;
i {
font-size: 24px;
}
}
}
}
} }
</style> </style>

View File

@ -4,9 +4,15 @@
<h3 class="title" @click="changeTab">AIX智慧课堂</h3> <h3 class="title" @click="changeTab">AIX智慧课堂</h3>
<div class="change-tab"> <div class="change-tab">
<ul class="flex"> <ul class="flex">
<li v-for="item in menus" :key="item.path" class="flex" <li
:class="currentRoute == item.path ? 'active-li' : ''" @click="changePage(item.path)"> v-for="(item, index) in routeHeader.nowRouter"
<i class="iconfont" :class="item.icon"></i> :key="index"
class="flex"
:style="{'color' : item.color}"
:class="currentRoute === item.url ? 'active-li' : ''"
@click="handleOutLink(item.url,item.type)"
>
<i :class="item.img"></i>
<span class="text">{{ item.name }}</span> <span class="text">{{ item.name }}</span>
</li> </li>
</ul> </ul>
@ -14,13 +20,17 @@
</div> </div>
<div class="right-section flex"> <div class="right-section flex">
<WindowTools/> <WindowTools />
<div class="user flex"> <div class="user flex">
<div class="avatar-container"> <div class="avatar-container">
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click"> <el-dropdown
class="right-menu-item hover-effect"
trigger="click"
@command="handleCommand"
>
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img :src="userStore.user.avatar" class="user-avatar" style="float: left;" /> <img :src="userStore.user.avatar" class="user-avatar" style="float: left" />
<div style="margin-top: 18px; font-size: 0.8em;"> {{ userStore.user.nickName }}</div> <div style="margin-top: 18px; font-size: 0.8em">{{ userStore.user.nickName }}</div>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
@ -44,13 +54,31 @@ import { useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import WindowTools from '@/components/window-tools/index.vue' import WindowTools from '@/components/window-tools/index.vue'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import routerStore from '@/store/modules/route'
import outLink from '@/utils/linkConfig'
const routeHeader = routerStore()
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
const currentRoute = ref('') const currentRoute = ref('')
const menus = ref([ const handleOutLink = (path, type) => {
if (!path) return
if (type === 'hash') {
router.push(path)
} else {
// key linkConfig.js
let configObj = outLink().getBaseData()
let fullPath = configObj.fullPath + path
fullPath = fullPath.replaceAll('//', '/')
//
ipcRenderer.send('openWindow', {
fullPath: fullPath,
cookieData: { ...configObj.data }
})
}
}
/*const menus = ref([
{ {
icon: 'icon-zhuye2 icon-homepage', icon: 'icon-zhuye2 icon-homepage',
name: '主页', name: '主页',
@ -66,12 +94,12 @@ const menus = ref([
name: '备课', name: '备课',
path: '/prepare' path: '/prepare'
}, },
/*{ {
icon: 'icon-jiangke1 icon-teach', icon: 'icon-jiangke1 icon-teach',
name: '授课', name: '授课',
path: '/teach' path: '/teach'
}*/ }
]) ])*/
// //
watch( watch(
@ -82,20 +110,19 @@ watch(
{ immediate: true } { immediate: true }
) )
const changePage = (url) => { const changePage = (url) => {
router.push(url) router.push(url)
} }
function handleCommand(command) { function handleCommand(command) {
switch (command) { switch (command) {
case "setLayout": case 'setLayout':
setLayout(); setLayout()
break; break
case "logout": case 'logout':
logout(); logout()
break; break
default: default:
break; break
} }
} }
@ -104,20 +131,25 @@ function logout() {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { })
userStore.logOut().then(() => { .then(() => {
// router.replace('/login') userStore
ipcRenderer && ipcRenderer.send('openLoginWindow') .logOut()
}).catch(()=>{ .then(() => {
// router.replace('/login') // router.replace('/login')
ipcRenderer && ipcRenderer.send('openLoginWindow') ipcRenderer && ipcRenderer.send('openLoginWindow')
}) })
}).catch(() => { }); .catch(() => {
// router.replace('/login')
ipcRenderer && ipcRenderer.send('openLoginWindow')
})
})
.catch(() => {})
} }
const emits = defineEmits(['setLayout']) const emits = defineEmits(['setLayout'])
function setLayout() { function setLayout() {
emits('setLayout'); emits('setLayout')
} }
</script> </script>
@ -160,7 +192,7 @@ function setLayout() {
color: #f99b53; color: #f99b53;
} }
.icon-homepage{ .icon-homepage {
color: #0a84ff; color: #0a84ff;
} }
@ -179,7 +211,7 @@ function setLayout() {
.active-li { .active-li {
background: #d3e3fb; background: #d3e3fb;
color: #409EFF; color: #409eff;
} }
} }
} }
@ -203,7 +235,6 @@ function setLayout() {
padding-bottom: 5px; padding-bottom: 5px;
flex-direction: column; flex-direction: column;
.user { .user {
.user-info { .user-info {
padding-right: 5px; padding-right: 5px;

View File

@ -32,6 +32,6 @@ let uploaderStore = ref(uploaderState())
height: 80px; height: 80px;
} }
.el-main { .el-main {
--el-main-padding: 0 20px; --el-main-padding: 0 20px 0 0;
} }
</style> </style>

View File

@ -0,0 +1,15 @@
import { defineStore } from 'pinia'
const routerStore = defineStore('router', {
state: () => ({
nowRouter: []
}),
actions: {
setNowRouter(payload) {
this.nowRouter = payload
}
},
mutations: {},
persist: true
})
export default routerStore

View File

@ -1,11 +1,9 @@
import { defineStore } from "pinia" import { defineStore } from 'pinia'
import { login, logout, getInfo } from '@/api/login' import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import defAva from '@/assets/images/user.png' import defAva from '@/assets/images/user.png'
const useUserStore = defineStore( const useUserStore = defineStore('user', {
'user',
{
state: () => ({ state: () => ({
token: getToken(), token: getToken(),
id: '', id: '',
@ -23,11 +21,13 @@ const useUserStore = defineStore(
const code = userInfo.code const code = userInfo.code
const uuid = userInfo.uuid const uuid = userInfo.uuid
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => { login(username, password, code, uuid)
.then((res) => {
setToken(res.token) setToken(res.token)
this.token = res.token this.token = res.token
resolve(res) resolve(res)
}).catch(error => { })
.catch((error) => {
reject(error) reject(error)
}) })
}) })
@ -35,45 +35,47 @@ const useUserStore = defineStore(
// 获取用户信息 // 获取用户信息
getInfo() { getInfo() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getInfo().then(res => { getInfo()
.then((res) => {
res.user.avatar = import.meta.env.VITE_APP_BASE_API + res.user.avatar res.user.avatar = import.meta.env.VITE_APP_BASE_API + res.user.avatar
const user = res.user const user = res.user
this.user = user this.user = user
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar; const avatar = user.avatar == '' || user.avatar == null ? defAva : user.avatar
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 if (res.roles && res.roles.length > 0) {
// 验证返回的roles是否是一个非空数组
this.roles = res.roles this.roles = res.roles
this.permissions = res.permissions this.permissions = res.permissions
} else { } else {
this.roles = ['ROLE_DEFAULT'] this.roles = ['ROLE_DEFAULT']
} }
this.id = user.userId this.id = user.userId
this.userName = user.userName this.userName = user.userName
this.nickName = user.nickName; this.nickName = user.nickName
this.avatar = avatar; this.avatar = avatar
this.userType = user.userType; this.userType = user.userType
this.deptId = user.deptId; this.deptId = user.deptId
this.deptName = user.deptName; this.deptName = user.deptName
this.deptLogo = user.deptLogo; this.deptLogo = user.deptLogo
this.deptSlogan = user.deptSlogan; this.deptSlogan = user.deptSlogan
this.activeDeptId = user.activeDeptId; this.activeDeptId = user.activeDeptId
this.activeDeptName = user.activeDeptName; this.activeDeptName = user.activeDeptName
this.parentDeptId = user.parentDeptId; this.parentDeptId = user.parentDeptId
this.parentDeptName = user.parentDeptName; this.parentDeptName = user.parentDeptName
this.edusubject = user.edusubject; this.edusubject = user.edusubject
this.edudegree = user.edudegree; this.edudegree = user.edudegree
this.edustage = user.edustage; this.edustage = user.edustage
this.userType = user.userType; this.userType = user.userType
this.studentId = user.studentId; this.studentId = user.studentId
this.timUserId = user.timuserid; this.timUserId = user.timuserid
this.plainpwd = user.plainpwd; this.plainpwd = user.plainpwd
this.roles = res.roles; this.roles = res.roles
resolve(res) resolve(res)
}).catch(error => { })
.catch((error) => {
reject(error) reject(error)
}) })
}) })
@ -81,14 +83,16 @@ const useUserStore = defineStore(
// 退出系统 // 退出系统
logOut() { logOut() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
logout(this.token).then(() => { logout(this.token)
.then(() => {
this.token = '' this.token = ''
this.roles = [] this.roles = []
this.permissions = [] this.permissions = []
localStorage.clear() localStorage.clear()
removeToken() removeToken()
resolve() resolve()
}).catch(error => { })
.catch((error) => {
removeToken() // zdg: 网络异常时,清除前端退出进入登录页 removeToken() // zdg: 网络异常时,清除前端退出进入登录页
reject(error) reject(error)
}) })
@ -96,6 +100,6 @@ const useUserStore = defineStore(
} }
}, },
persist: true persist: true
}) })
export default useUserStore export default useUserStore

View File

@ -1,8 +1,7 @@
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
const baseConfig = () => {
const userStore = useUserStore() const userStore = useUserStore()
const baseConfig = { return {
// Electron 设置cookie // Electron 设置cookie
url: import.meta.env.VITE_APP_BUILD_BASE_PATH, url: import.meta.env.VITE_APP_BUILD_BASE_PATH,
// url: 'https://file.ysaix.com:7868', // url: 'https://file.ysaix.com:7868',
@ -12,52 +11,48 @@ const baseConfig = {
value: userStore.token, value: userStore.token,
// 域名 // 域名
domain: import.meta.env.VITE_APP_DOMAIN domain: import.meta.env.VITE_APP_DOMAIN
}
// 作业布置
const homeWork = {
data: { ...baseConfig},
// 完整路径
fullPath: `${baseConfig.url}/teaching/classtaskassign?titleName=%E4%BD%9C%E4%B8%9A%E5%B8%83%E7%BD%AE`
}
// 作业反馈
const feedback = {
data: { ...baseConfig},
// 完整路径
fullPath: `${baseConfig.url}/teaching/classtaskassign?titleName=作业反馈`
}
// 高考研读
const gk = {
data: { ...baseConfig},
fullPath: `${baseConfig.url}/education/colentrance`
}
// 课标研读
const standard = {
data: { ...baseConfig},
fullPath: `${baseConfig.url}/teaching/chatwithstandard`
}
// 教学大模型
const aiModel = {
data: { ...baseConfig},
fullPath: `${baseConfig.url}/platofai`
}
const getBaseData = () => {
return {
data: { ...baseConfig},
fullPath: `${baseConfig.url}`
} }
} }
export default { export default () => {
homeWork, return {
feedback, // 作业布置
gk, homeWork: {
standard, data: { ...baseConfig() },
aiModel, // 完整路径
getBaseData fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=%E4%BD%9C%E4%B8%9A%E5%B8%83%E7%BD%AE`
},
// 作业反馈
feedback: {
data: { ...baseConfig() },
// 完整路径
fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=作业反馈`
},
// 高考研读
gk: {
data: { ...baseConfig() },
fullPath: `${baseConfig().url}/education/colentrance`
},
// 课标研读
standard: {
data: { ...baseConfig() },
fullPath: `${baseConfig().url}/teaching/chatwithstandard`
},
// 教学大模型
aiModel: {
data: { ...baseConfig() },
fullPath: `${baseConfig().url}/platofai`
},
getBaseData: () => {
return {
data: { ...baseConfig() },
fullPath: `${baseConfig().url}`
}
}
}
} }

View File

@ -34,9 +34,6 @@
<div <div
v-if="item.levelFirstName" v-if="item.levelFirstName"
style=" style="
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1; flex: 1;
text-align: left; text-align: left;
" "
@ -67,7 +64,7 @@
<div class="item-popover" @click="closePopver(index)"> <div class="item-popover" @click="closePopver(index)">
<template v-if="item.uniquekey"> <template v-if="item.uniquekey">
<div class="item-popover-item"> <div class="item-popover-item">
<el-button text @click="editTalk(item, index)"> <el-button text @click="setHomeWork(item, index)">
<i class="iconfont icon-bianji"></i> <i class="iconfont icon-bianji"></i>
<span>布置</span> <span>布置</span>
</el-button> </el-button>
@ -145,10 +142,10 @@ export default {
} }
} }
}, },
emits: { 'on-move': null, 'on-delete': null }, emits: { 'on-move': null, 'on-delete': null, 'on-set': null },
data() { data() {
return { return {
listenList: [] listenList: [],
} }
}, },
methods: { methods: {
@ -230,6 +227,10 @@ export default {
} }
}) })
}) })
},
//
setHomeWork(item){
this.$emit('on-set', item)
} }
} }
} }

View File

@ -0,0 +1,215 @@
<template>
<el-dialog v-model="dialogVisible" center top="10vh" width="600px" :show-close="false"
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="homerwork-header flex">
<span>布置作业</span>
<i class="iconfont icon-guanbi" @click="cloneDialog"></i>
</div>
</template>
<div>
<el-form :model="form" label-width="80px">
<el-form-item label="班级">
<el-tree ref="treeRef" :data="treeData" :props="defaultProps" :load="getLoad" node-key="id"
@check="handleCheckChange" lazy show-checkbox />
</el-form-item>
<el-form-item label="选中学生">
<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="完成要求">
<el-radio-group v-model="form.feedtype">
<el-radio value="必做" size="large">必做</el-radio>
<el-radio value="选做" size="large">选做</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="截至时间">
<el-date-picker v-model="endTime" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" placeholder="请选择截至时间"
@change="changeTime" />
</el-form-item>
<el-form-item label="推荐用时">
<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="cloneDialog">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import { listClassmain, listClassgroup } from '@/api/classManage/index'
import useUserStore from '@/store/modules/user'
import { uniqBy, cloneDeep } from 'lodash'
export default {
props: {
modelValue: {
type: Boolean,
default: false
}
},
data() {
return {
dialogVisible: false,
defaultProps: {
children: 'children',
label: 'label',
isLeaf: 'leaf',
},
treeData: [],
//
userInfo: null,
//
gradeList: [],
curGradeId: '',
//
groupList: [],
//
studentList: [],
//
form: {
feedtype: '必做',
endTime: '',
timelength: 1
},
endTime: ''
}
},
created() {
//
this.endTime = this.getCurrentDate() + ' ' + '10:00:00'
this.userInfo = useUserStore().user
this.getGradeList()
},
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 + "]")
})
console.log(list)
this.gradeList = list
this.treeData = list
})
},
getLoad(node, resolve) {
console.log(node.level)
if (node.level == 0) return resolve([])
if (node.level == 1) {
listClassgroup({ classid: node.key, orderby: 'orderidx', pageSize: 100 }).then(res => {
console.log(res)
if (res.rows.length > 0) {
let ary = []
res.rows.forEach(item => {
if (item.parentid === 0) {
let studentGroup = JSON.parse("[" + item.studentlist + "]")
studentGroup.forEach(el => {
el.label = el.name
el.leaf = true
el.level = 2,
el.id = el.studentid
})
ary.push({
label: item.groupname,
...item,
level: 1,
children: studentGroup
})
}
})
// console.log(ary)
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)
},
//
cloneDialog() {
this.$emit('on-close')
},
//
changeTime(value) {
console.log(value, 100)
},
//
getCurrentDate() {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1; // 0+1
const day = now.getDate()
return `${year}-${month.length == 2 ? month : '0' + month}-${day.length == 2 ? day : '0' + day}`;
}
},
watch: {
modelValue(val) {
this.dialogVisible = val
}
}
}
</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;
}
</style>

View File

@ -13,17 +13,23 @@
<el-button class="btn" @click="handleOutLink('aiModel')">教学大模型</el-button> <el-button class="btn" @click="handleOutLink('aiModel')">教学大模型</el-button>
</div> </div>
<el-button type="primary" class="to-class-btn" @click="openLesson"> <el-button type="primary" class="to-class-btn" @click="openLesson">
<i class="iconfont icon-lingdang"></i>上课</el-button> <i class="iconfont icon-lingdang"></i>上课</el-button
>
<div class="top-zoom-style"></div> <div class="top-zoom-style"></div>
</div> </div>
<div class="prepare-body-header"> <div class="prepare-body-header">
<div> <div>
<label style="font-size: 15px">{{ currentFileList.length }}个文件</label>&nbsp; <label style="font-size: 15px">{{ currentFileList.length }}个文件</label>&nbsp;
<el-popover placement="top-start" :width="250" trigger="hover"> <el-popover placement="top-start" :width="250" trigger="hover">
<template #default> <template #default>
<div> <div>
<el-button v-if="lastAsyncAllTime" type="success" size="small" :icon="Check" circle /> <el-button
v-if="lastAsyncAllTime"
type="success"
size="small"
:icon="Check"
circle
/>
{{ lastAsyncAllTime ? toTimeText(lastAsyncAllTime) + '同步成功' : '' }} {{ lastAsyncAllTime ? toTimeText(lastAsyncAllTime) + '同步成功' : '' }}
</div> </div>
</template> </template>
@ -41,23 +47,42 @@
<el-button @click="handleOutLink('feedback')">作业反馈</el-button> <el-button @click="handleOutLink('feedback')">作业反馈</el-button>
<el-button @click="handleOutLink('homeWork')">布置作业</el-button> <el-button @click="handleOutLink('homeWork')">布置作业</el-button>
<el-button @click="isDialogOpen = true">上传资料</el-button> <el-button @click="isDialogOpen = true">上传资料</el-button>
<el-button type="primary" style="margin-left: 10px" @click="createFile">新建课件</el-button> <el-button type="primary" style="margin-left: 10px" @click="createFile"
>新建课件</el-button
>
</div> </div>
</div> </div>
<el-checkbox-group v-model="checkFileList" class="prepare-body-main" <el-checkbox-group
:style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }"> v-model="checkFileList"
<file-list-item v-for="(item, index) in currentFileList" :key="index" :item="item" :index="index" class="prepare-body-main"
@on-move="onMoveSingleFile" @on-delete="deleteTalk"> :style="{ 'margin-bottom': checkFileList.length > 0 ? '40px' : '0' }"
>
<file-list-item
v-for="(item, index) in currentFileList"
:key="index"
:item="item"
:index="index"
@on-move="onMoveSingleFile"
@on-delete="deleteTalk"
@on-set="openSet"
>
<el-checkbox label="" :value="item" /> <el-checkbox label="" :value="item" />
</file-list-item> </file-list-item>
</el-checkbox-group> </el-checkbox-group>
<file-oper-batch v-show="checkFileList.length > 0" <file-oper-batch
v-show="checkFileList.length > 0"
:indeterminate="checkFileList.length > 0 && checkFileList.length < currentFileList.length" :indeterminate="checkFileList.length > 0 && checkFileList.length < currentFileList.length"
:choose="checkFileList" :check-all="isCheckAll" @click-delete="clickDelete" @click-move="clickMove" :choose="checkFileList"
@cancel="checkFileList = []" @click-choose="clickChoose"></file-oper-batch> :check-all="isCheckAll"
@click-delete="clickDelete"
@click-move="clickMove"
@cancel="checkFileList = []"
@click-choose="clickChoose"
></file-oper-batch>
</div> </div>
<MoveFile v-model="isMoveDialogOpen" @on-submit="chooseMoveCata" /> <MoveFile v-model="isMoveDialogOpen" @on-submit="chooseMoveCata" />
<uploadDialog v-model="isDialogOpen" @submit-file="submitFile" /> <uploadDialog v-model="isDialogOpen" @submit-file="submitFile" />
<SetHomework v-model="setDialog" @on-close="closeHomework" />
</div> </div>
</template> </template>
<script setup> <script setup>
@ -77,6 +102,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 './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 { uniqBy, cloneDeep } from 'lodash' import { uniqBy, cloneDeep } from 'lodash'
@ -85,7 +111,15 @@ const { ipcRenderer } = window.electron || {}
export default { export default {
name: 'Prepare', name: 'Prepare',
components: { ChooseTextbook, Refresh, uploadDialog, FileListItem, FileOperBatch, MoveFile }, components: {
ChooseTextbook,
Refresh,
uploadDialog,
FileListItem,
FileOperBatch,
MoveFile,
SetHomework
},
data() { data() {
return { return {
moveFile: [], moveFile: [],
@ -111,7 +145,9 @@ export default {
// //
userStore: '', userStore: '',
entpcourseid: '', entpcourseid: '',
timerId: null timerId: null,
//
setDialog: false
} }
}, },
computed: { computed: {
@ -121,6 +157,13 @@ export default {
) )
} }
}, },
watch: {
$route(to) {
if (to.path != '/prepare' && this.timerId) {
clearInterval(this.timerId)
}
}
},
created() { created() {
this.userStore = useUserStore().user this.userStore = useUserStore().user
ipcRenderer.removeAllListeners('copy-file-default-reply') ipcRenderer.removeAllListeners('copy-file-default-reply')
@ -128,10 +171,8 @@ export default {
this.callback(param) this.callback(param)
}) })
this.lastAsyncAllTime = localStorage.getItem('lastAsyncAllTime') this.lastAsyncAllTime = localStorage.getItem('lastAsyncAllTime')
},
mounted() {
}, },
mounted() {},
activated() { activated() {
if (this.uploadData.textbookId !== null) { if (this.uploadData.textbookId !== null) {
this.asyncAllFile() this.asyncAllFile()
@ -283,16 +324,20 @@ export default {
this.createTimer() this.createTimer()
} }
// key linkConfig.js // key linkConfig.js
let configObj = outLink[key] let configObj = outLink()[key]
// //
ipcRenderer.send('openWindow', { ipcRenderer.send('openWindow', {
fullPath: configObj.fullPath, fullPath: configObj.fullPath,
cookieData: { ...(configObj.data) } cookieData: { ...configObj.data }
}) })
}, },
// ID ID // ID ID
getChapterId() { getChapterId() {
return listEntpcourse({ evalid: this.uploadData.levelSecondId, edituserid: this.userStore.userId, pageSize: 500 }) return listEntpcourse({
evalid: this.uploadData.levelSecondId,
edituserid: this.userStore.userId,
pageSize: 500
})
}, },
// //
createTimer() { createTimer() {
@ -302,20 +347,22 @@ export default {
}, },
// //
getHomeWorkList() { getHomeWorkList() {
homeworklist({ entpcourseid: this.entpcourseid, edituserid: this.userStore.userId, pageSize: 100 }).then(res => { homeworklist({
entpcourseid: this.entpcourseid,
edituserid: this.userStore.userId,
pageSize: 100
}).then((res) => {
// AIx web // AIx web
let list = [] let list = []
for (var i = 0; i < res.rows.length; i++) { for (var i = 0; i < res.rows.length; i++) {
res.rows[i].taskconfig = []
res.rows[i].taskconfig = [];
// child // child
for (var j = 0; j < res.rows.length; j++) { for (var j = 0; j < res.rows.length; j++) {
if (res.rows[j].parentid == res.rows[i].id) { if (res.rows[j].parentid == res.rows[i].id) {
var ss = []; var ss = []
if (res.rows[j].classworkdatastudentids != null) { if (res.rows[j].classworkdatastudentids != null) {
ss = JSON.parse('[' + res.rows[j].classworkdatastudentids + ']'); ss = JSON.parse('[' + res.rows[j].classworkdatastudentids + ']')
} }
var js = { var js = {
id: res.rows[j].id, id: res.rows[j].id,
@ -338,7 +385,7 @@ export default {
weights: res.rows[j].weights, weights: res.rows[j].weights,
feedtype: res.rows[j].feedtype feedtype: res.rows[j].feedtype
} }
res.rows[i].taskconfig.push(js); res.rows[i].taskconfig.push(js)
} }
} }
res.rows[i].fileShowName = res.rows[i].uniquekey res.rows[i].fileShowName = res.rows[i].uniquekey
@ -346,15 +393,16 @@ export default {
// slideid>0PPT // slideid>0PPT
// 2024-05-15jackyshen // 2024-05-15jackyshen
if (res.rows[i].classid == 0 && res.rows[i].slideid == 0) { if (res.rows[i].classid == 0 && res.rows[i].slideid == 0) {
list.push(res.rows[i]); list.push(res.rows[i])
} }
// //
if (res.rows[i].entpcourseworklist != '') { if (res.rows[i].entpcourseworklist != '') {
res.rows[i].entpcourseworklistarray = JSON.parse('[' + res.rows[i].entpcourseworklist + ']'); res.rows[i].entpcourseworklistarray = JSON.parse(
'[' + res.rows[i].entpcourseworklist + ']'
)
} else { } else {
res.rows[i].entpcourseworklistarray = []; res.rows[i].entpcourseworklistarray = []
} }
} }
// //
@ -363,6 +411,13 @@ export default {
this.currentFileList = cloneDeep(ary) this.currentFileList = cloneDeep(ary)
}) })
}, },
//
openSet() {
this.setDialog = true
},
closeHomework() {
this.setDialog = false
},
// PDF- // PDF-
navtoPdf() { navtoPdf() {
createWindow('open-PDF', { url: '/classBegins/index' }) createWindow('open-PDF', { url: '/classBegins/index' })
@ -371,13 +426,6 @@ export default {
openLesson() { openLesson() {
createWindow('tool-sphere', { url: '/tool/sphere' }) createWindow('tool-sphere', { url: '/tool/sphere' })
} }
},
watch: {
$route(to) {
if (to.path != '/prepare' && this.timerId) {
clearInterval(this.timerId)
}
}
} }
} }
</script> </script>
@ -467,7 +515,7 @@ export default {
border-color: #ffffff; border-color: #ffffff;
&:hover { &:hover {
background: rgba(255, 255, 255, 0.3) background: rgba(255, 255, 255, 0.3);
} }
&:first-child { &:first-child {
@ -504,7 +552,6 @@ export default {
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
padding: 0 20px; padding: 0 20px;
} }
.prepare-body-main { .prepare-body-main {