Merge pull request 'zhuhao_dev' (#104) from zhuhao_dev into main

Reviewed-on: #104
This commit is contained in:
朱浩 2024-08-07 15:51:11 +08:00
commit bd2024eed2
11 changed files with 273 additions and 25 deletions

View File

@ -5,15 +5,15 @@ VITE_APP_TITLE = AIx数字平台
VITE_APP_ENV = 'production' VITE_APP_ENV = 'production'
# AIx融合数字管理系统/生产环境 # AIx融合数字管理系统/生产环境
VITE_APP_BASE_API = 'https://file.ysaix.com:7868/prod-api' VITE_APP_BASE_API = 'https://prev.ysaix.com:7868/prod-api'
VITE_APP_DOMAIN = 'file.ysaix.com' VITE_APP_DOMAIN = 'prev.ysaix.com'
VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api' VITE_APP_UPLOAD_API = 'https://prev.ysaix.com:7868/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli # 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/' VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'

19
.env.test Normal file
View File

@ -0,0 +1,19 @@
# 页面标题
VITE_APP_TITLE = AIx数字平台
# 生产环境配置
VITE_APP_ENV = 'production'
# AIx融合数字管理系统/生产环境
VITE_APP_BASE_API = 'https://file.ysaix.com:7868/prod-api'
VITE_APP_DOMAIN = 'file.ysaix.com'
VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'

47
electron-builder-prod.yml Normal file
View File

@ -0,0 +1,47 @@
appId: com.electron.app
productName: AIx
directories:
buildResources: build
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
asarUnpack:
- resources/**
win:
executableName: AIx
icon: resources/logo2.ico
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
artifactName: ${name}-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false
dmg:
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://prev.ysaix.com:7868/src/assets/smarttalk/
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/

View File

@ -1,6 +1,6 @@
{ {
"name": "aix-win", "name": "aix-win",
"version": "1.0.2", "version": "1.0.4",
"description": "An Electron application with Vue", "description": "An Electron application with Vue",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "example.com", "author": "example.com",
@ -14,7 +14,8 @@
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir", "build:unpack": "npm run build && electron-builder --dir",
"build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml", "build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml",
"build:test": "npm run build && electron-builder --win --config ./electron-builder.yml", "build:test": "electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml",
"build:prod": "electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml",
"build:mac": "npm run build && electron-builder --mac", "build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux" "build:linux": "npm run build && electron-builder --linux"
}, },

View File

@ -77,10 +77,10 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
console.error('Error uploading file:', err) console.error('Error uploading file:', err)
} }
}) })
}, 5000) }, 20000)
} }
}) })
}, 1000) }, 10000)
}) })
function getFileMD5(path) { function getFileMD5(path) {

View File

@ -71,6 +71,8 @@ const curBookId = ref(-1)
const curBookName = ref('') const curBookName = ref('')
// //
const curBookImg = ref('') const curBookImg = ref('')
//
const curBookPath = ref('')
// //
const volumeOne = ref([]) const volumeOne = ref([])
// //
@ -126,10 +128,11 @@ const getSubjectContent = async () => {
} }
// //
const changeBook = ({ id, itemtitle, avartar }) => { const changeBook = ({ id, itemtitle, avartar, fileurl }) => {
curBookId.value = id curBookId.value = id
curBookName.value = itemtitle curBookName.value = itemtitle
curBookImg.value = BaseUrl + avartar curBookImg.value = BaseUrl + avartar
curBookPath.value = fileurl
getTreeData() getTreeData()
setTimeout(() => { setTimeout(() => {
dialogVisible.value = false dialogVisible.value = false
@ -176,7 +179,8 @@ const emitChangeBook = () => {
textBook: { textBook: {
curBookId: curBookId.value, curBookId: curBookId.value,
curBookName: curBookName.value, curBookName: curBookName.value,
curBookImg: curBookImg.value curBookImg: curBookImg.value,
curBookPath: curBookPath.value
}, },
node: curNode node: curNode
} }
@ -276,6 +280,7 @@ const getSubject = async () => {
curBookName.value = subjectList.value[0].itemtitle curBookName.value = subjectList.value[0].itemtitle
curBookId.value = subjectList.value[0].id curBookId.value = subjectList.value[0].id
curBookImg.value = BaseUrl + subjectList.value[0].avartar curBookImg.value = BaseUrl + subjectList.value[0].avartar
curBookPath.value = subjectList.value[0].fileurl
} }
@ -291,7 +296,7 @@ const handleNodeClick = (data, node) => {
* data : 当前节点数据 * data : 当前节点数据
* node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象 * node : 当前节点对象 包含当前节点所有数据 parent属性 指向父节点Node对象
*/ */
const nodeData = data; const nodeData = data;
const parentNode = node.parent.data; const parentNode = node.parent.data;
@ -306,7 +311,8 @@ const handleNodeClick = (data, node) => {
textBook: { textBook: {
curBookId: curBookId.value, curBookId: curBookId.value,
curBookName: curBookName.value, curBookName: curBookName.value,
curBookImg: curBookImg.value curBookImg: curBookImg.value,
curBookPath: curBookPath.value
}, },
node: toRaw(nodeData) node: toRaw(nodeData)
} }
@ -428,4 +434,4 @@ onMounted(() => {
background-color: #eaf3ff !important; background-color: #eaf3ff !important;
color: #409EFF color: #409EFF
} }
</style> </style>

View File

@ -96,9 +96,9 @@ const hanleFileChange = (file) => {
return false return false
} }
// //
// B < KB < MB < GB // B < KB < MB < GB
// file.raw.size B // file.raw.size B
const fileSize = file.raw.size / 1024 / 1024 > 100 const fileSize = file.raw.size / 1024 / 1024 > 500
if (fileSize) { if (fileSize) {
ElMessage.error('文件大小错误! 请上传小于100M的文件!') ElMessage.error('文件大小错误! 请上传小于100M的文件!')
return false return false
@ -126,7 +126,7 @@ const checkFile = (item, file) => {
else{ else{
return false return false
} }
} }
// //

View File

@ -118,7 +118,7 @@ export default {
file.callback(res) file.callback(res)
}, },
beforeUpload(file) { beforeUpload(file) {
const MAX_SIZE = 100 * 1024 * 1024 // 2MB const MAX_SIZE = 500 * 1024 * 1024 // 2MB
if (file.size > MAX_SIZE) { if (file.size > MAX_SIZE) {
this.$message.error('文件大小不能超过 100MB!') this.$message.error('文件大小不能超过 100MB!')
return false return false

View File

@ -17,7 +17,7 @@ const uploaderStore = defineStore('uploader', {
pushFile(payload) { pushFile(payload) {
let _this = this let _this = this
let arr = payload.filter((item) => { let arr = payload.filter((item) => {
const MAX_SIZE = 100 * 1024 * 1024; // 2MB const MAX_SIZE = 500 * 1024 * 1024; // 2MB
if (item.size > MAX_SIZE) { if (item.size > MAX_SIZE) {
ElMessage.error('文件大小不能超过 100MB!'); ElMessage.error('文件大小不能超过 100MB!');
return false; return false;

View File

@ -0,0 +1,129 @@
<template>
<el-dialog
v-model="centerDialogVisible"
class="reserv-dialog"
title="预约课程"
destroy-on-close
width="600"
style="text-align: left"
>
<el-form :model="form" label-width="auto" style="max-width: 600px">
<el-form-item label="课程名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="课程类型" prop="location">
<el-segmented v-model="form.type" :block="false" :options="locationOptions" />
</el-form-item>
<el-form-item label="&nbsp;" prop="location">
<div>{{ locationMessage }}</div>
</el-form-item>
<el-form-item label="上课时间">
<el-col :span="6">
<el-date-picker
v-model="form.day"
type="date"
:editable="false"
class="reserv-date-pick"
placeholder="请选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-time-picker
v-model="form.time"
is-range
:editable="false"
format="HH:mm"
class="reserv-time-pick"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</el-col>
</el-form-item>
<el-form-item label="授课对象">
<el-radio-group v-model="form.resource">
<el-radio v-for="(item, index) in classList" :key="index" :value="item.id">{{item.caption}}({{item.classstudentcount}})</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="教室">
<el-input v-model="form.classRoom" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="centerDialogVisible = false">取消</el-button>
<el-button type="primary" @click="centerDialogVisible = false"> 提交 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, defineExpose, onMounted, reactive, computed } from 'vue'
import { listClassmain } from '@/api/classManage'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user
const centerDialogVisible = ref(false)
const form = reactive({
name: '',
type: '常规课',
delivery: false,
day:'',
time:'',
resource: '',
classRoom: ''
})
const locationOptions = [
{
label: '常规课',
value: '常规课',
disabled: false,
message: '现场公屏授课,学生无需长时间打开平板上。'
},
{
label: '公开课',
value: '公开课',
disabled: true,
message: '现场公屏授课,学生需打开平开与老师进行互动,如点赞、互动作业。'
},
{
label: '直播课',
value: '直播课',
disabled: true,
message: '远程直播授课,学生需打开平开观看实时直播教学,并与老师进行互动。'
}
]
const locationMessage = computed(() => {
return locationOptions.find((item) => item.value === form.type).message
})
const openDialog = () => {
centerDialogVisible.value = true
}
const closeDialog = () => {
centerDialogVisible.value = false
}
const classList = ref([])
onMounted(() => {
listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' }).then(
(response) => {
classList.value = [...response.rows]
}
)
})
defineExpose({
openDialog,
closeDialog
})
</script>
<style scoped lang="scss">
:deep(.reserv-date-pick) {
width: 140px;
}
:deep(.reserv-time-pick) {
width: 240px;
}
</style>

View File

@ -3,12 +3,12 @@
<ChooseTextbook @change-book="nodeClick" @node-click="nodeClick" /> <ChooseTextbook @change-book="nodeClick" @node-click="nodeClick" />
<div class="page-right"> <div class="page-right">
<div class="header-top flex"> <div class="header-top flex">
<div class="textbook-img"> <div class="textbook-img" @click="navtoPdf">
<el-image style="width: 80px; height: 110px" :src="curBookImg" /> <el-image style="width: 80px; height: 110px" :src="curBookImg" />
</div> </div>
<div class="top-item"> <div class="top-item">
<el-button class="btn" @click="handleOutLink('standard')">课标研读</el-button> <el-button class="btn" @click="handleOutLink('standard')">课标研读</el-button>
<el-button class="btn" @click="navtoPdf">电子课本</el-button> <el-button class="btn" @click="openReserv">预约课程</el-button>
<el-button class="btn" @click="handleOutLink('gk')">高考研读</el-button> <el-button class="btn" @click="handleOutLink('gk')">高考研读</el-button>
<el-button class="btn" @click="handleOutLink('aiModel')">教学大模型</el-button> <el-button class="btn" @click="handleOutLink('aiModel')">教学大模型</el-button>
</div> </div>
@ -59,11 +59,14 @@
<uploadDialog v-model="isDialogOpen" @submit-file="submitFile" /> <uploadDialog v-model="isDialogOpen" @submit-file="submitFile" />
<SetHomework v-model="setDialog" :entpcourseid="entpcourseid" :row="row" @on-close="closeHomework" /> <SetHomework v-model="setDialog" :entpcourseid="entpcourseid" :row="row" @on-close="closeHomework" />
</div> </div>
<reserv ref="reservDialog"></reserv>
</template> </template>
<script setup> <script setup>
import { Check } from '@element-plus/icons-vue' import { Check } from '@element-plus/icons-vue'
import Reserv from '@/views/prepare/container/reserv.vue'
</script> </script>
<script> <script>
const Remote = require('@electron/remote')
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import uploadDialog from '@/components/upload-dialog/index.vue' import uploadDialog from '@/components/upload-dialog/index.vue'
import { Refresh } from '@element-plus/icons-vue' import { Refresh } from '@element-plus/icons-vue'
@ -82,7 +85,7 @@ 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'
import { delClasswork, addEntpcourse } from '@/api/teaching/classwork' import { delClasswork, addEntpcourse } from '@/api/teaching/classwork'
const fs = require('fs');
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {
@ -108,6 +111,7 @@ export default {
fileList: [], fileList: [],
currentNode: {}, currentNode: {},
currentFileList: [], currentFileList: [],
curBookPath: '',
lastAsyncAllTime: '', lastAsyncAllTime: '',
uploadData: { uploadData: {
textbookId: null, textbookId: null,
@ -158,6 +162,38 @@ export default {
} }
}, },
methods: { methods: {
getBookPathFromServer() {
let fileName = this.curBookPath
if (!fileName) return
fileName = fileName.replace('.txt', '.pdf')
return new Promise((resolve, reject)=>{
const userDataPath = Remote.app.getPath('userData')
const appRootFilePath = userDataPath + '\\selfFile\\'
let filePath = appRootFilePath + fileName
fs.access(filePath, fs.constants.F_OK, (err) => {
let filePath = import.meta.env.VITE_APP_RES_FILE_PATH + fileName
if (err) {
//线
ipcRenderer.send('download-file-default', {
url: filePath,
fileName: fileName
})
ipcRenderer.once('download-file-default' + fileName, (e, isSuccess) => {
if (isSuccess === true) {
resolve(appRootFilePath + fileName)
}else {
ElMessage({
type: 'info',
message: `下载教材失败!`
})
}
})
}else {
resolve(appRootFilePath + fileName)
}
})
})
},
createFile() { createFile() {
creatPPT(this.currentNode.label + '.pptx', this.uploadData).then((res) => { creatPPT(this.currentNode.label + '.pptx', this.uploadData).then((res) => {
this.currentFileList.unshift(res.resData) this.currentFileList.unshift(res.resData)
@ -277,8 +313,10 @@ export default {
}) })
}, },
async nodeClick(data) { async nodeClick(data) {
console.log(data)
if (this.currentNode.id === data.node.id) return if (this.currentNode.id === data.node.id) return
this.curBookImg = data.textBook.curBookImg this.curBookImg = data.textBook.curBookImg
this.curBookPath = data.textBook.curBookPath
this.checkFileList = [] this.checkFileList = []
let cata = parseCataByNode(data.node) let cata = parseCataByNode(data.node)
this.currentNode = data.node this.currentNode = data.node
@ -288,7 +326,7 @@ export default {
this.uploadData.textbookId = data.textBook.curBookId this.uploadData.textbookId = data.textBook.curBookId
this.initHomeWork() this.initHomeWork()
await this.asyncAllFile() await this.asyncAllFile()
}, },
async initHomeWork() { async initHomeWork() {
if (this.timerId) { if (this.timerId) {
@ -305,7 +343,7 @@ export default {
let { rows } = await this.getChapterId() let { rows } = await this.getChapterId()
this.entpcourseid = rows[0].id this.entpcourseid = rows[0].id
} }
// //
this.getHomeWorkList() this.getHomeWorkList()
} }
@ -331,6 +369,9 @@ export default {
cform.createblankfile = 'yes'; cform.createblankfile = 'yes';
return addEntpcourse(cform) return addEntpcourse(cform)
}, },
openReserv(){
// this.$refs['reservDialog'].openDialog()
},
// //
handleOutLink(key) { handleOutLink(key) {
if (key == 'homeWork') { if (key == 'homeWork') {
@ -448,7 +489,9 @@ export default {
}, },
// PDF- // PDF-
navtoPdf() { async navtoPdf() {
let path = await this.getBookPathFromServer()
console.log(path)
createWindow('open-PDF', { url: '/classBegins/index' }) createWindow('open-PDF', { url: '/classBegins/index' })
}, },
// - // -
@ -528,6 +571,9 @@ export default {
overflow: hidden; overflow: hidden;
margin-right: 20px; margin-right: 20px;
z-index: 1; z-index: 1;
&:hover {
cursor: pointer;
}
} }
.top-item { .top-item {