Merge branch 'main' into zhuhao_dev

This commit is contained in:
朱浩 2024-08-31 14:13:56 +08:00
commit 0e5ca5eff2
20 changed files with 237 additions and 58 deletions

View File

@ -13,11 +13,6 @@ asarUnpack:
win: win:
executableName: AIx executableName: AIx
icon: resources/logo2.ico icon: resources/logo2.ico
extraFiles:
- from: ./node_modules/im_electron_sdk/lib/
to: ./resources
filter:
- '**/*'
nsis: nsis:
oneClick: false oneClick: false
allowToChangeInstallationDirectory: true allowToChangeInstallationDirectory: true
@ -50,3 +45,9 @@ publish:
url: https://file.ysaix.com:7868/src/assets/smarttalk/ url: https://file.ysaix.com:7868/src/assets/smarttalk/
electronDownload: electronDownload:
mirror: https://npmmirror.com/mirrors/electron/ mirror: https://npmmirror.com/mirrors/electron/
# 额外依赖打包到输出目录
extraFiles:
- from: ./node_modules/im_electron_sdk/lib/
to: ./resources
filter:
- '**/*'

View File

@ -30,6 +30,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"electron-dl-manager": "^3.0.0", "electron-dl-manager": "^3.0.0",
"electron-log": "^5.1.7", "electron-log": "^5.1.7",
"electron-store": "8.0.0",
"electron-updater": "^6.1.7", "electron-updater": "^6.1.7",
"element-plus": "^2.7.6", "element-plus": "^2.7.6",
"fabric": "^5.3.0", "fabric": "^5.3.0",
@ -63,18 +64,5 @@
"vite-plugin-windicss": "^1.9.3", "vite-plugin-windicss": "^1.9.3",
"vue": "^3.4.30", "vue": "^3.4.30",
"windicss": "^3.5.6" "windicss": "^3.5.6"
},
"build": {
"win": {
"extraFiles": [
{
"from": "./node_modules/im_electron_sdk/lib/",
"to": "./resources",
"filter": [
"**/*"
]
}
]
}
} }
} }

BIN
resources/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -3,13 +3,17 @@ import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset' import icon from '../../resources/icon.png?asset'
import File from './file' import File from './file'
import chat from './chat' // chat封装 import chat from './chat' // chat封装
import Store from './store' // Store封装
import updateInit from './update'
// 代理 electron/remote // 代理 electron/remote
// 第一步引入remote // 第一步引入remote
import remote from '@electron/remote/main' import remote from '@electron/remote/main'
// 第二步: 初始化remote // 第二步: 初始化remote
remote.initialize() remote.initialize()
import updateInit from './update' // 持久化数据-初始化
Store.initialize()
File({ app, shell, BrowserWindow, ipcMain }) File({ app, shell, BrowserWindow, ipcMain })
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

32
src/main/store.js Normal file
View File

@ -0,0 +1,32 @@
/**
* @description 解决 主进程|渲染进程 数据共享
*/
import Store from 'electron-store' // 持久化存储
// 设置ipc与渲染器通信
Store.initRenderer()
// 默认共享数据
const defaultData = {
model: 'select', // 悬浮球-当前模式
showBoardAll: false, // 全屏画板-是否显示
isPdfWin: false, // pdf窗口是否打开
isToolWin: false, // 工具窗口是否打开
curSubjectNode: {
data: {}, // 当前教材节点 (包含当前教材 单元)
querySearch: {} // 查询资源所需参数
}
}
// 初始化
export function initialize(){
const store = new Store({
name: 'cache-store', // 存储文件名
fileExtension: 'ini', // 文件后缀名
encryptionKey: 'Eihrjwi7h104h2Kub423' // 数据加密-防止用户直接改配置
})
store.clear() // 先清除-所有缓存数据
store.set(defaultData) // 初始化-默认数据
return store
}
export default { initialize }

View File

@ -50,7 +50,7 @@ export function getClassmain(id) {
// 获取小组列表 // 获取小组列表
export function listClassgroup(query) { export function listClassgroup(query) {
return request({ return request({
url: '/education/classgroup/list', url: '/education/classgroup/new/list',
method: 'get', method: 'get',
params: query params: query
}) })

View File

@ -163,6 +163,10 @@ const handleNodeClick = (data,node) => {
onMounted(() => { onMounted(() => {
titleName.value = `${useStore.user.edustage}-${useStore.user.edusubject}` titleName.value = `${useStore.user.edustage}-${useStore.user.edusubject}`
treeLoading.value = true treeLoading.value = true
//loading
setTimeout(() => {
treeLoading.value = false
},2000)
}) })
// //
watch(() => useThird,() => { watch(() => useThird,() => {

View File

@ -1,7 +1,7 @@
<template> <template>
<el-tabs v-model="active" class="demo-tabs" @tab-change="handleClick"> <el-tabs v-model="active" class="demo-tabs" @tab-change="handleClick">
<template v-for="(item,index) in gradeList" :key="index"> <template v-for="(item,index) in gradeList" :key="index">
<el-tab-pane :label="item.label" :name="item.value"> <el-tab-pane :label="item.label" :name="item.value" disabled>
<SelectSubject ref="selectSubject" :subjectList="subjectList" @clickTag="getTagId"></SelectSubject> <SelectSubject ref="selectSubject" :subjectList="subjectList" @clickTag="getTagId"></SelectSubject>
</el-tab-pane> </el-tab-pane>
</template> </template>
@ -24,7 +24,9 @@ const active = ref(1)
const subjectList = ref([]) const subjectList = ref([])
//id //id
const textbookVersionId = ref(0) const textbookVersionId = ref(0)
//
const handleClick = (tab) => { const handleClick = (tab) => {
console.log(tab,'tab')
getSubject(tab) getSubject(tab)
} }
// //
@ -32,6 +34,7 @@ const getSubject = (value) => {
const currentIndex = gradeList.findIndex(item => item.value === 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){
if(res.data.length === 0) return
subjectList.value = res.data.map(item => { subjectList.value = res.data.map(item => {
return { return {
...item, ...item,

View File

@ -250,10 +250,10 @@ const onSubmit = (formEl) => {
saveByClassWorkArray({ saveByClassWorkArray({
classworkarray: JSON.stringify(ary) classworkarray: JSON.stringify(ary)
}) })
.then(() => { .then((res) => {
setLoading.value = false setLoading.value = false
ElMessage.success('操作成功') ElMessage.success('操作成功')
emit('on-success') emit('on-success', res.data)
cloneDialog(formEl) cloneDialog(formEl)
}) })
.catch(() => { .catch(() => {

View File

@ -10,7 +10,7 @@
class="flex" class="flex"
:style="{'color' : item.color}" :style="{'color' : item.color}"
:class="currentRoute === item.url ? 'active-li' : ''" :class="currentRoute === item.url ? 'active-li' : ''"
@click="handleOutLink(item.url,item.type)" @click="handleOutLink(item.url,item.type, item.name)"
> >
<i :class="item.img"></i> <i :class="item.img"></i>
<span class="text">{{ item.name }}</span> <span class="text">{{ item.name }}</span>
@ -57,6 +57,7 @@ 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 routerStore from '@/store/modules/route'
import outLink from '@/utils/linkConfig' import outLink from '@/utils/linkConfig'
const routeHeader = routerStore() const routeHeader = routerStore()
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
const userStore = useUserStore() const userStore = useUserStore()
@ -64,7 +65,7 @@ const router = useRouter()
const currentRoute = ref('') const currentRoute = ref('')
const dev_api = ref(import.meta.env.VITE_APP_BASE_API) const dev_api = ref(import.meta.env.VITE_APP_BASE_API)
const handleOutLink = (path, type) => { const handleOutLink = (path, type, name) => {
if (!path) return if (!path) return
if (type === 'hash') { if (type === 'hash') {
router.push(path) router.push(path)
@ -73,6 +74,11 @@ const handleOutLink = (path, type) => {
let configObj = outLink().getBaseData() let configObj = outLink().getBaseData()
let fullPath = configObj.fullPath + path let fullPath = configObj.fullPath + path
fullPath = fullPath.replaceAll('//', '/') fullPath = fullPath.replaceAll('//', '/')
const { levelFirstId, levelSecondId } = JSON.parse(localStorage.getItem('unitId'))
let unitId = levelSecondId ? levelSecondId :levelFirstId
if(name == '教材分析' || name == '高考研究'){
fullPath += `?unitId=${unitId}`
}
// //
ipcRenderer.send('openWindow', { ipcRenderer.send('openWindow', {
key: path, key: path,

View File

@ -76,3 +76,36 @@ export const toTimeText = (timeStamp, simple) => {
} }
return timeText return timeText
} }
/**
*
* @returns 当前年--
*/
export const getCurrentTime = (format)=> {
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
if(format == 'YYYY-MM-DD'){
return `${year}-${month}-${day}`;
}
if(format == 'HH:mm'){
return `${hours}:${minutes}`;
}
}
/**
*
* @param {number} m 指定时间
* @returns 指定时间之后的 小时:分钟
*/
export const getAfterMinutes = (m) => {
const now = new Date();
const afterMinutes = new Date(now.getTime() + m * 60 * 1000);
let hours = afterMinutes.getHours();
hours = hours < 10 ? ('0' + hours) : hours
let minutes = afterMinutes.getMinutes();
minutes = minutes < 10 ? ('0' + minutes) : minutes
return `${hours}:${minutes}`;
}

View File

@ -6,12 +6,14 @@
// Remote.app.getAppPath() E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar // Remote.app.getAppPath() E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar
// path.join(__dirname) 根目录 E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar\out\renderer // path.join(__dirname) 根目录 E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar\out\renderer
const isNode = typeof require !== 'undefined' // 是否支持node函数 const isNode = typeof require !== 'undefined' // 是否支持node函数
const path = isNode?require('path'):{} const path = isNode?require('path'):{}
const Remote = isNode?require('@electron/remote'):{} const Remote = isNode?require('@electron/remote'):{}
const { ipcRenderer } = isNode?require('electron'):window.electron || {} const { ipcRenderer } = isNode?require('electron'):window.electron || {}
const API = isNode?window.api:{} // preload-api const API = isNode?window.api:{} // preload-api
import { useToolState } from '@/store/modules/tool' // 获取store状态 import { useToolState } from '@/store/modules/tool' // 获取store状态
const Store = isNode?require('electron-store'):null // 持久化存储
// 常用变量 // 常用变量
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':'' const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
const isDev = isNode?process.env.NODE_ENV !== 'production':'' const isDev = isNode?process.env.NODE_ENV !== 'production':''
@ -19,6 +21,12 @@ const toolState = useToolState() // 获取store状态
// 暴露Remote中的属性 // 暴露Remote中的属性
export const ipcMain = Remote?.ipcMain || {} export const ipcMain = Remote?.ipcMain || {}
// 暴露Store存储对象
export const store = Store ? new Store({
name: 'cache-store', // 存储文件名
fileExtension: 'ini', // 文件后缀名
encryptionKey: 'Eihrjwi7h104h2Kub423' // 数据加密-防止用户直接改配置
}) : {}
/** /**
* 获取静态资源开发和生产环境 * 获取静态资源开发和生产环境
* @param {*} url * @param {*} url

View File

@ -35,6 +35,7 @@
placeholder="请选择日期" placeholder="请选择日期"
format="YYYY-MM-DD" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
:disabled-date="disabledDate"
/> />
</el-col> </el-col>
</el-form-item> </el-form-item>
@ -53,6 +54,8 @@
range-separator="-" range-separator="-"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
:disabled-hours="disabledHours"
:disabled-minutes="disabledMinute"
/> />
</el-col> </el-col>
</el-form-item> </el-form-item>
@ -81,6 +84,8 @@ import { ref, defineExpose, onMounted, reactive, computed, watch } from 'vue'
import { addSmartClassReserv, updateSmartClassReserv, listClassmain } from '@/api/classManage' import { addSmartClassReserv, updateSmartClassReserv, listClassmain } from '@/api/classManage'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getCurrentTime, getAfterMinutes } from '@/utils/date'
const emit = defineEmits(['addSuccess']) const emit = defineEmits(['addSuccess'])
const props = defineProps({ const props = defineProps({
bookId: { bookId: {
@ -143,6 +148,52 @@ const locationOptions = [
const locationMessage = computed(() => { const locationMessage = computed(() => {
return locationOptions.find((item) => item.value === form.type).message return locationOptions.find((item) => item.value === form.type).message
}) })
//
const disabledDate = (time)=> {
//
const today = new Date();
if (time.getTime() < today.getTime() - 8.64e7) {
// true
return true;
}
//
const oneMonthLater = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
if (time.getTime() > oneMonthLater.getTime()) {
return true;
}
return false;
}
// -
const disabledHours = ()=>{
if(getCurrentTime('YYYY-MM-DD') == form.day){
const arrs = []
for (let i = 0; i < 24; i++) {
if (new Date().getHours() <= i) continue;
arrs.push(i)
}
return arrs;
}
}
// -
const disabledMinute = (hour,role) => {
if(getCurrentTime('YYYY-MM-DD') == form.day){
const arrs = []
if(role == 'start'){
for (let i = 0; i < 60; i++) {
if (new Date().getMinutes() <= i) continue;
arrs.push(i)
}
return arrs;
}
else{
if(form.time[0]) return []
}
}
}
const openDialog = (data) => { const openDialog = (data) => {
if (data) { if (data) {
updateForm.value = data updateForm.value = data
@ -154,6 +205,11 @@ const openDialog = (data) => {
form.resource = data.classList.split(',').map((item) => parseInt(item)) form.resource = data.classList.split(',').map((item) => parseInt(item))
form.classRoom = data.classRoom form.classRoom = data.classRoom
} }
else{
//
form.day = getCurrentTime('YYYY-MM-DD')
form.time = [getCurrentTime('HH:mm'), getAfterMinutes(45)]
}
centerDialogVisible.value = true centerDialogVisible.value = true
} }
const closeDialog = () => { const closeDialog = () => {

View File

@ -422,7 +422,6 @@ export default {
this.uploadData.levelSecondId = cata[1] this.uploadData.levelSecondId = cata[1]
this.uploadData.levelThirdId = cata[2] this.uploadData.levelThirdId = cata[2]
this.uploadData.textbookId = data.textBook.curBookId this.uploadData.textbookId = data.textBook.curBookId
const toolStore = useToolState()
toolStore.curSubjectNode.data = data toolStore.curSubjectNode.data = data
toolStore.curSubjectNode.querySearch = this.uploadData toolStore.curSubjectNode.querySearch = this.uploadData
this.initHomeWork() this.initHomeWork()

View File

@ -70,6 +70,8 @@ const getData = (data) => {
levelSecondId levelSecondId
} }
sourceStore.handleQuery() sourceStore.handleQuery()
// ID
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId}))
} }
const getDataOther = (data) => { const getDataOther = (data) => {
sourceStore.thirdQuery.chapterId = data.chapterId sourceStore.thirdQuery.chapterId = data.chapterId

View File

@ -19,6 +19,7 @@ const emit = defineEmits(['update:modelValue'])
onMounted(async() => { onMounted(async() => {
if (canvasRef.value) { if (canvasRef.value) {
FabricVue.drawConfig.drawColors = ['red'] FabricVue.drawConfig.drawColors = ['red']
FabricVue.drawConfig.drawWidth = 3
FabricVue.boardConfig.mode = TYPES.ActionMode.OTHER FabricVue.boardConfig.mode = TYPES.ActionMode.OTHER
FabricVue.boardConfig.backgroundColor = 'transparent' FabricVue.boardConfig.backgroundColor = 'transparent'
const option = { freeDrawingCursor: 'default' } const option = { freeDrawingCursor: 'default' }

View File

@ -111,9 +111,10 @@ const closeHomework = async() => {
ipcMsgSend('tool-sphere:set:ignore', true) ipcMsgSend('tool-sphere:set:ignore', true)
} }
// //
const successHomework = ()=>{ const successHomework = (data)=>{
// console.log('', data)
// im-(app|) // im-(app|)
ipcMsgInvoke('im-chat:msg', curRow.value.id, MsgEnum.HEADS.MSG_0016) ipcMsgInvoke('im-chat:msg', data, MsgEnum.HEADS.MSG_0016)
} }
// change // change
const changeChapter = async (data)=>{ const changeChapter = async (data)=>{

View File

@ -2,17 +2,19 @@
<div class="warp" ref="btnRef" :style="isFold?'min-height:auto;':''"> <div class="warp" ref="btnRef" :style="isFold?'min-height:auto;':''">
<slot name="start"></slot> <slot name="start"></slot>
<!-- 工具按钮 --> <!-- 工具按钮 -->
<el-space direction="vertical" v-show="!isFold"> <transition name="el-zoom-in-bottom">
<template v-for="(item,index) in list"> <el-space direction="vertical" v-show="!isFold">
<slot :name="item.prop" :item="item" :index="index"> <template v-for="(item,index) in list">
<div class="c-btn flex flex-col items-center gap-2 p-2" @click.stop="clickHandel(item,$event)"> <slot :name="item.prop" :item="item" :index="index">
<i class="iconfont" :class="item.icon" :style="item.style" /> <div class="c-btn flex flex-col items-center gap-2 p-2" @click.stop="clickHandel(item,$event)">
<span>{{item.label||item.text||item.name}}</span> <i class="iconfont" :class="item.icon" :style="item.style" />
</div> <span>{{item.label||item.text||item.name}}</span>
</slot> </div>
</template> </slot>
<slot name="append"></slot> </template>
</el-space> <slot name="append"></slot>
</el-space>
</transition>
<slot name="end"> <slot name="end">
<span class="fold" @click="isFold=!isFold" :style="isFold?'margin: 5px;':''"> <span class="fold" @click="isFold=!isFold" :style="isFold?'margin: 5px;':''">
{{isFold?'<<<':'>>>'}} {{isFold?'<<<':'>>>'}}

View File

@ -36,6 +36,7 @@ class Drag {
const {cx, cy} = this.getMousePos(e) const {cx, cy} = this.getMousePos(e)
this.x = cx this.x = cx
this.y = cy this.y = cy
this.getCurPos() // 被拖拽元素初始坐标
// 手动-触发事件 v-drag-start // 手动-触发事件 v-drag-start
this.el.dispatchEvent(new CustomEvent('v-drag-start', {detail:{drag: this}})) this.el.dispatchEvent(new CustomEvent('v-drag-start', {detail:{drag: this}}))
} }
@ -85,12 +86,20 @@ class Drag {
} }
// 获取移动后坐标 // 获取移动后坐标
getPos(x, y) { getPos(x, y) {
const w = this.max.w - this.toRound(this.el.clientWidth) // 边界控制:图标元素
const h = this.max.h - this.toRound(this.el.clientHeight) // const w = this.max.w - this.toRound(this.el.clientWidth)
// const h = this.max.h - this.toRound(this.el.clientHeight)
// 边界控制:整个工具
const w = this.max.w - this.toRound(this.handle.clientWidth)
const h = this.max.h - this.toRound(this.handle.clientHeight)
x = x < 0 ? 0 : x > w ? w : x x = x < 0 ? 0 : x > w ? w : x
y = y < 0 ? 0 : y > h ? h : y y = y < 0 ? 0 : y > h ? h : y
return { x, y } return { x, y }
} }
getCurPos(dom) {
const pos = this[dom||'handle']?.getBoundingClientRect()
this.data = {left:this.toRound(pos.left), top:this.toRound(pos.top)}
}
// 小数转整数 // 小数转整数
toRound = v => Math.round(v) toRound = v => Math.round(v)
} }

View File

@ -20,17 +20,19 @@
<el-image :src="logo" draggable="false" /> <el-image :src="logo" draggable="false" />
</div> </div>
</div> </div>
<div class="tool-btns" v-show="!isFold"> <transition name="a-fade">
<el-segmented class="c-btns" v-model="tabActive" :options="btnList" size="large" block <div class="tool-btns" v-if="!isFold">
@change="tabChange"> <el-segmented class="c-btns" v-model="tabActive" :options="btnList" size="large" block
<template #default="{item}"> @change="tabChange">
<div class="c-btn flex flex-col items-center gap-2 p-2"> <template #default="{item}">
<i class="iconfont" :class="item.icon" :style="item.style" /> <div class="c-btn flex flex-col items-center gap-2 p-2">
<span>{{item.label}}</span> <i class="iconfont" :class="item.icon" :style="item.style" />
</div> <span>{{item.label}}</span>
</template> </div>
</el-segmented> </template>
</div> </el-segmented>
</div>
</transition>
</div> </div>
</div> </div>
</template> </template>
@ -41,7 +43,7 @@ import { onMounted, ref, reactive, watchEffect } from 'vue'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { ElMessageBox, ElMessage, ElLoading } from 'element-plus' import { ElMessageBox, ElMessage, ElLoading } from 'element-plus'
import * as classManageApi from '@/api/classManage' import * as classManageApi from '@/api/classManage'
import logo from '@root/resources/icon.png' // logo import logo from '@root/resources/logo.png' // logo
import boardVue from './components/board.vue' // - import boardVue from './components/board.vue' // -
import sideVue from './components/side.vue' // - import sideVue from './components/side.vue' // -
import upvoteVue from './components/upvote.vue' // - import upvoteVue from './components/upvote.vue' // -
@ -109,6 +111,23 @@ const tabChange = (val) => {
const logoHandle = (e,t) => { const logoHandle = (e,t) => {
if (Date.now() - dragtime.value < 200) { if (Date.now() - dragtime.value < 200) {
isFold.value = !isFold.value isFold.value = !isFold.value
setTimeout(() => {
// :
const dom = document.querySelector('.tool-bottom-all')
const { x } = dom.getBoundingClientRect()
const w = window.innerWidth - (470 || 80)
// if (x > w) dom.style.left = `${w}px`
if (x > w) { //
let left = x
const animatFn = () => {
left-=30
if (left < w) left == w
dom.style.left = `${left}px`
if (left > w) requestAnimationFrame(animatFn)
}
requestAnimationFrame(animatFn)
}
}, 20);
} }
} }
// -穿 // -穿
@ -262,4 +281,15 @@ watchEffect(() => {
} }
} }
} }
.a-fade-leave-active,.a-fade-enter-active{
transition: all .3s;
}
.a-fade-enter-from,.a-fade-leave-to{
width: 0;
opacity: 0;
}
.a-fade-enter-to,.a-fade-leave-from{
width: 350px;
opacity: 1;
}
</style> </style>