Compare commits
69 Commits
748a3c4b86
...
f9b80dd455
Author | SHA1 | Date |
---|---|---|
zhengdegang | f9b80dd455 | |
zdg | 482cab5cd3 | |
zdg | 0d68e6efa7 | |
zdg | d432d97507 | |
zdg | a9443035c2 | |
lyc | 58e80d590e | |
lyc | 8a1c5d7186 | |
zdg | 7c3f3ea8fb | |
zdg | f2b1a097bb | |
朱浩 | a6e6f450c5 | |
zdg | 64e7c27a44 | |
zdg | c555de5fe5 | |
zhengdegang | a4f276908e | |
zdg | a10d3b078b | |
zdg | c1bba6e443 | |
lyc | 231a7b6dce | |
lyc | 310d9d3a2b | |
lyc | fae380f9c6 | |
lyc | a68fb21203 | |
朱浩 | 2c6ccd893b | |
朱浩 | 540e5f0179 | |
朱浩 | 8a8f90ce16 | |
朱浩 | 050f87a84a | |
yangws | c338f34127 | |
小杨 | 5b49eef8e8 | |
小杨 | 1060929532 | |
baigl | 448c4d979f | |
白了个白 | 222f558807 | |
白了个白 | ef5dfc8ed1 | |
yangws | 733855b95e | |
小杨 | 7a6f57a9b7 | |
白了个白 | 48571086c3 | |
白了个白 | b967c684d6 | |
白了个白 | 84e8acdef7 | |
白了个白 | d8c4556393 | |
“zouyf” | 2d9518804e | |
“zouyf” | e405eb041b | |
lyc | b0d3876e05 | |
lyc | eb197db4fd | |
lyc | 5808798b95 | |
lyc | 393587848f | |
yangws | 57fb6b9c75 | |
小杨 | 1882fd0dae | |
yangws | 7f2cacffac | |
小杨 | e1caa441cb | |
“zouyf” | 09e6bec69f | |
“zouyf” | d26af6446a | |
zouyf | ca3f2658a7 | |
“zouyf” | cd1fc036a2 | |
白了个白 | 42f24041db | |
zouyf | 727299aac9 | |
lyc | 27b0457eef | |
lyc | 34e74c5ec6 | |
“zouyf” | 208f5d496f | |
“zouyf” | 122fb41690 | |
朱浩 | eb4a58b306 | |
朱浩 | a313a0e8b0 | |
白了个白 | b081000f11 | |
“zouyf” | 630a7b4c50 | |
zhengdegang | 01dc5dc643 | |
zdg | 349bdbd181 | |
zdg | 8707b54420 | |
白了个白 | 124b584e38 | |
朱浩 | f08b5a8bee | |
朱浩 | e2255544da | |
朱浩 | cbc6f21100 | |
白了个白 | 639178c827 | |
zdg | 82091fbef5 | |
zdg | 76a14264d7 |
|
@ -130,7 +130,6 @@
|
||||||
"typescript": "~5.3.0",
|
"typescript": "~5.3.0",
|
||||||
"vite": "^5.3.1",
|
"vite": "^5.3.1",
|
||||||
"vite-plugin-windicss": "^1.9.3",
|
"vite-plugin-windicss": "^1.9.3",
|
||||||
"vue": "^3.4.30",
|
|
||||||
"vue-tsc": "^1.8.25",
|
"vue-tsc": "^1.8.25",
|
||||||
"windicss": "^3.5.6"
|
"windicss": "^3.5.6"
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,9 @@ const defaultData = {
|
||||||
curNode: null, // 当前选中的节点
|
curNode: null, // 当前选中的节点
|
||||||
defaultExpandedKeys: [], //展开的节点
|
defaultExpandedKeys: [], //展开的节点
|
||||||
subjectTree: [] // "树结构" 章节
|
subjectTree: [] // "树结构" 章节
|
||||||
}
|
},
|
||||||
|
env: {}, // 不走同步 Pinia - 变量
|
||||||
|
curr: {} // 不走同步 Pinia - 当前信息
|
||||||
},
|
},
|
||||||
local: { // 本地(永久localStorage)
|
local: { // 本地(永久localStorage)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<Screen v-if="screening" />
|
<template v-if="loading">
|
||||||
<Editor v-else-if="_isPC" />
|
加载中...
|
||||||
<Mobile v-else />
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Screen v-if="screening" />
|
||||||
|
<Editor v-else-if="_isPC" />
|
||||||
|
<Mobile v-else />
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue'
|
import { ref, onMounted, watch, onBeforeMount } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useScreenStore, useMainStore, useSnapshotStore } from './store'
|
import { useScreenStore, useMainStore, useSnapshotStore, useSlidesStore } from './store'
|
||||||
import { LOCALSTORAGE_KEY_DISCARDED_DB } from './configs/storage'
|
import { LOCALSTORAGE_KEY_DISCARDED_DB } from './configs/storage'
|
||||||
import { deleteDiscardedDB } from './utils/database'
|
import { deleteDiscardedDB } from './utils/database'
|
||||||
import { isPC } from './utils/common'
|
import { isPC } from './utils/common'
|
||||||
|
@ -17,10 +22,19 @@ import Editor from './views/Editor/index.vue'
|
||||||
import Screen from './views/Screen/index.vue'
|
import Screen from './views/Screen/index.vue'
|
||||||
import Mobile from './views/Mobile/index.vue'
|
import Mobile from './views/Mobile/index.vue'
|
||||||
|
|
||||||
|
// zdg
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
|
import { PPTApi } from './api'
|
||||||
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
|
import './api/watcher' // 监听
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
const _isPC = isPC()
|
const _isPC = isPC()
|
||||||
|
|
||||||
const mainStore = useMainStore()
|
const mainStore = useMainStore()
|
||||||
const snapshotStore = useSnapshotStore()
|
const snapshotStore = useSnapshotStore()
|
||||||
|
const slidesStore = useSlidesStore()
|
||||||
const { databaseId } = storeToRefs(mainStore)
|
const { databaseId } = storeToRefs(mainStore)
|
||||||
const { screening } = storeToRefs(useScreenStore())
|
const { screening } = storeToRefs(useScreenStore())
|
||||||
|
|
||||||
|
@ -29,9 +43,11 @@ if (import.meta.env.MODE !== 'development') {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
await initLoad()
|
||||||
await deleteDiscardedDB()
|
await deleteDiscardedDB()
|
||||||
snapshotStore.initSnapshotDatabase()
|
snapshotStore.initSnapshotDatabase()
|
||||||
mainStore.setAvailableFonts()
|
mainStore.setAvailableFonts()
|
||||||
|
loading.value = false // 加载完毕
|
||||||
})
|
})
|
||||||
|
|
||||||
// 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
|
// 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
|
||||||
|
@ -44,6 +60,30 @@ window.addEventListener('unload', () => {
|
||||||
const newDiscardedDB = JSON.stringify(discardedDBList)
|
const newDiscardedDB = JSON.stringify(discardedDBList)
|
||||||
localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, newDiscardedDB)
|
localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, newDiscardedDB)
|
||||||
})
|
})
|
||||||
|
/** 接口类型 */
|
||||||
|
interface Result {
|
||||||
|
code?: number,
|
||||||
|
msg?: string,
|
||||||
|
data?: any
|
||||||
|
rows?: Array<any>,
|
||||||
|
total?: number
|
||||||
|
}
|
||||||
|
// 获取参数
|
||||||
|
const initLoad: Function = () => {
|
||||||
|
// 获取缓存的ppt 资源数据
|
||||||
|
const resource = sessionStore.get('curr.resource')
|
||||||
|
if (!!resource) { // 有ppt 资源数据缓存
|
||||||
|
slidesStore.setTitle(resource.title)
|
||||||
|
if (!!resource.parentContent) { // 有全局配置项
|
||||||
|
const opt = JSON.parse(resource.parentContent)
|
||||||
|
!!(opt.width??null) && slidesStore.setViewportSize(opt.width) // 有宽度配置项
|
||||||
|
!!(opt.ratio??null) && slidesStore.setViewportRatio(opt.ratio)// 有比例配置项
|
||||||
|
}
|
||||||
|
return PPTApi.getSlideList(resource.id)
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/**
|
||||||
|
* @description ppt幻灯片相关的请求接口-集中处理
|
||||||
|
* @author zdg
|
||||||
|
* @date 2024-11-26
|
||||||
|
*/
|
||||||
|
import { toRaw } from 'vue'
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
|
import * as useStore from '../store' // pptist-状态管理
|
||||||
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
|
import useUserStore from '@/store/modules/user' // 外部-用户信息
|
||||||
|
|
||||||
|
const slidesStore = useStore.useSlidesStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
/** 工具类 */
|
||||||
|
export class Utils {
|
||||||
|
static mxData: any = {
|
||||||
|
throTime: 0, // 节流时间
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 节流-防抖
|
||||||
|
* @param {*} func
|
||||||
|
* @param {*} delay
|
||||||
|
* @param {*} type 1-节流 2-防抖
|
||||||
|
* mxThrottle(() => {xxxx}, 200)
|
||||||
|
*/
|
||||||
|
static mxThrottle(func, delay = 200, type = 1) {
|
||||||
|
if (type == 1) { // 节流(第一次触发后面的事件不触发)
|
||||||
|
const cur = Date.now()
|
||||||
|
const lastExecuted = this.mxData.throTime
|
||||||
|
if (cur - lastExecuted > delay) {
|
||||||
|
this.mxData.throTime = cur
|
||||||
|
return func.apply(this)
|
||||||
|
}
|
||||||
|
} else { // 防抖(只触发最后一次事件)
|
||||||
|
const timer = this.mxData.throTime
|
||||||
|
!!timer && clearTimeout(timer)
|
||||||
|
this.mxData.throTime = setTimeout(() => {
|
||||||
|
return func.apply(this)
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ppt相关后端接口处理 */
|
||||||
|
export class PPTApi {
|
||||||
|
// 变量
|
||||||
|
static isUpdate = true // 是否更新数据
|
||||||
|
|
||||||
|
// 获取所有幻灯片列表
|
||||||
|
static getSlideList(parentid: (Number | String)): Promise<Boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 }
|
||||||
|
const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params)
|
||||||
|
if (res.code === 200) {
|
||||||
|
const slides = (res.rows || []).map(o => {
|
||||||
|
if (!!o.datacontent) {
|
||||||
|
const json = JSON.parse(o.datacontent)
|
||||||
|
!!json && (json.id = o.id)
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
// 如果没有数据,默认空白页
|
||||||
|
return {id: o.id,elements:[],background:{type:"solid",color:"#fff"}}
|
||||||
|
})
|
||||||
|
slidesStore.updateSlideIndex(0) // 下标0 为第一页
|
||||||
|
slidesStore.setSlides(slides) // 写入数据
|
||||||
|
resolve(true)
|
||||||
|
} else msgUtils.msgError(res.msg || '获取数据失败');resolve(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增幻灯片
|
||||||
|
static addSlide(data: object): Promise<Boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const enpt = sessionStore.get('curr.entp')||{}
|
||||||
|
const resource = sessionStore.get('curr.resource')||{}
|
||||||
|
const {id, ...content} = data
|
||||||
|
const params = {
|
||||||
|
parentid: resource.id,
|
||||||
|
entpid: userStore.user.deptId,
|
||||||
|
entpcourseid: enpt.id,
|
||||||
|
ppttype: 'file',
|
||||||
|
title: '',
|
||||||
|
fileurl: '',
|
||||||
|
filetype: 'slide',
|
||||||
|
datacontent: JSON.stringify(content),
|
||||||
|
filekey: '',
|
||||||
|
filetag: '',
|
||||||
|
fileidx: 0,
|
||||||
|
dflag: 0,
|
||||||
|
status: '',
|
||||||
|
edituserid: userStore.id
|
||||||
|
}
|
||||||
|
const rid = await API_entpcoursefile.addEntpcoursefileReturnId(params)
|
||||||
|
if (!!rid) {
|
||||||
|
data.id = rid
|
||||||
|
slidesStore.updateSlide(data)
|
||||||
|
// msgUtils.msgSuccess('新增成功')
|
||||||
|
this.isUpdate = false // 新增后会触发监听,不再更新数据
|
||||||
|
resolve(true)
|
||||||
|
} else msgUtils.msgError('新增失败');resolve(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 监听幻灯片数据变化,更新数据
|
||||||
|
* @param newVal
|
||||||
|
* @param oldVal
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static async updateSlides(newVal: object, oldVal: object) {
|
||||||
|
const newData = toRaw(newVal)
|
||||||
|
const oldData = toRaw(oldVal)
|
||||||
|
// console.log('监听幻灯片数据变化', newData, oldData)
|
||||||
|
if (!(newData&&newData.length)) return // 新数据为空,不需要更新数据
|
||||||
|
else if (!oldData.length) return // 初始加载,旧数据空不需要更新数据
|
||||||
|
|
||||||
|
const currentSlide = toRaw(slidesStore.currentSlide)
|
||||||
|
const isAdd = !/^\d+$/.test(currentSlide.id) // 是否新增
|
||||||
|
if (isAdd) { // 新增的幻灯片(id 为非数字,说明是新增的幻灯片)
|
||||||
|
const bool = await this.addSlide(currentSlide)
|
||||||
|
bool && this.batchUpdateSlides(newData, true) // 批量更新-排序
|
||||||
|
} else { // 防抖-更新
|
||||||
|
if (!this.isUpdate) return this.isUpdate = true // 下次更新数据
|
||||||
|
const params = {
|
||||||
|
id: currentSlide.id,
|
||||||
|
datacontent: JSON.stringify(currentSlide),
|
||||||
|
}
|
||||||
|
Utils.mxThrottle(() => {this.updateSlide(params)}, 1000, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 更新幻灯片
|
||||||
|
static updateSlide(data: object): Promise<Boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const res: Result = await API_entpcoursefile.updateEntpcoursefileNew(data)
|
||||||
|
if (res.code === 200) {
|
||||||
|
resolve(true)
|
||||||
|
} else msgUtils.msgError(res.msg || '更新失败');resolve(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 批量更新 数据|排序
|
||||||
|
* @param list 数据
|
||||||
|
* @param sort 是否只更新排序
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static batchUpdateSlides(list: Slide[], sort: boolean): Promise<Boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const data: object[] = list.map((o, index) => ({
|
||||||
|
id: o.id,
|
||||||
|
fileidx: index,
|
||||||
|
datacontent: sort?null:JSON.stringify(o),
|
||||||
|
}))
|
||||||
|
const res: Result = await API_entpcoursefile.batchUpdateNew(data)
|
||||||
|
if (res.code === 200) {
|
||||||
|
resolve(true)
|
||||||
|
} else msgUtils.msgError(res.msg || '更新失败');resolve(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 删除幻灯片
|
||||||
|
static delSlide(id: string): Promise<Boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const res: Result = await API_entpcoursefile.delEntpcoursefile(id)
|
||||||
|
if (res.code === 200) {
|
||||||
|
resolve(true)
|
||||||
|
} else msgUtils.msgError(res.msg || '删除失败');resolve(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PPTApi
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* @description api 无store循环引用
|
||||||
|
* @author zdg
|
||||||
|
*/
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
// 删除幻灯片
|
||||||
|
static delSlide(id: string): Promise<Boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const res: Result = await API_entpcoursefile.delEntpcoursefile(id)
|
||||||
|
if (res.code === 200) {
|
||||||
|
resolve(true)
|
||||||
|
} else msgUtils.msgError(res.msg || '删除失败');resolve(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* @description 公共监听器
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { watch } from 'vue'
|
||||||
|
import { PPTApi } from './index'
|
||||||
|
import * as store from '../store'
|
||||||
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
|
const slidesStore = store.useSlidesStore()
|
||||||
|
const resource = sessionStore.get('curr.resource')
|
||||||
|
/**
|
||||||
|
* @description 监听器
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 监听幻灯片内容变化
|
||||||
|
watch(() => slidesStore.slides, (newVal, oldVal) => {
|
||||||
|
PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容
|
||||||
|
},{ deep: true })
|
||||||
|
|
||||||
|
// 监听标题变化
|
||||||
|
watch(() => slidesStore.title, (newVal, oldVal) => {
|
||||||
|
if (oldVal == '未命名演示文稿') return // 初始加载,不需要更新数据
|
||||||
|
updatePPT({title: newVal})
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatePPT = async (data) => {
|
||||||
|
if (!resource) return
|
||||||
|
data.id = resource.id
|
||||||
|
await PPTApi.updateSlide(data) // 更新ppt内容
|
||||||
|
sessionStore.set('curr.resource.title', data.title)
|
||||||
|
}
|
|
@ -13,4 +13,4 @@ interface Document {
|
||||||
mozCancelFullScreen(): Promise<void>
|
mozCancelFullScreen(): Promise<void>
|
||||||
webkitExitFullscreen(): Promise<void>
|
webkitExitFullscreen(): Promise<void>
|
||||||
msExitFullscreen(): Promise<void>
|
msExitFullscreen(): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,12 @@ const convertFontSizePtToPx = (html: string, ratio: number) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
const slidesStore = useSlidesStore()
|
||||||
const slidesStore = useSlidesStore()
|
const { theme } = storeToRefs(useSlidesStore())
|
||||||
const { theme } = storeToRefs(useSlidesStore())
|
|
||||||
|
|
||||||
const { addSlidesFromData } = useAddSlidesOrElements()
|
const { addSlidesFromData } = useAddSlidesOrElements()
|
||||||
const { isEmptySlide } = useSlideHandler()
|
const { isEmptySlide } = useSlideHandler()
|
||||||
|
export default () => {
|
||||||
|
|
||||||
const exporting = ref(false)
|
const exporting = ref(false)
|
||||||
|
|
||||||
|
@ -486,9 +486,413 @@ export default () => {
|
||||||
reader.readAsArrayBuffer(file)
|
reader.readAsArrayBuffer(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
importSpecificFile,
|
importSpecificFile,
|
||||||
importPPTXFile,
|
importPPTXFile,
|
||||||
|
PPTXFileToJson,
|
||||||
exporting,
|
exporting,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入PPTX 返回 json
|
||||||
|
export const PPTXFileToJson = (data: File|ArrayBuffer) => {
|
||||||
|
return new Promise(async(resolve, reject) => {
|
||||||
|
if (!data) return
|
||||||
|
let fileArrayBuffer: ArrayBuffer = null
|
||||||
|
let resData = {} // 返回的数据
|
||||||
|
|
||||||
|
const shapeList: ShapePoolItem[] = []
|
||||||
|
for (const item of SHAPE_LIST) {
|
||||||
|
shapeList.push(...item.children)
|
||||||
|
}
|
||||||
|
// 获取文件的 ArrayBuffer
|
||||||
|
const getArrayBuffer = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = () => {
|
||||||
|
resolve(reader.result)
|
||||||
|
}
|
||||||
|
reader.onerror = reject
|
||||||
|
reader.readAsArrayBuffer(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (data instanceof File) { // 文件
|
||||||
|
fileArrayBuffer = await getArrayBuffer()
|
||||||
|
} else if (data instanceof ArrayBuffer) { // ArrayBuffer
|
||||||
|
fileArrayBuffer = data
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid data type')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始解析
|
||||||
|
const json = await parse(fileArrayBuffer)
|
||||||
|
|
||||||
|
const ratio = 96 / 72
|
||||||
|
const width = json.size.width
|
||||||
|
|
||||||
|
resData.def = json // 保留原始数据
|
||||||
|
resData.width = width * ratio
|
||||||
|
resData.ratio = slidesStore.viewportRatio
|
||||||
|
|
||||||
|
const slides: Slide[] = []
|
||||||
|
for (const item of json.slides) {
|
||||||
|
const { type, value } = item.fill
|
||||||
|
let background: SlideBackground
|
||||||
|
if (type === 'image') {
|
||||||
|
background = {
|
||||||
|
type: 'image',
|
||||||
|
image: {
|
||||||
|
src: value.picBase64,
|
||||||
|
size: 'cover',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type === 'gradient') {
|
||||||
|
background = {
|
||||||
|
type: 'gradient',
|
||||||
|
gradient: {
|
||||||
|
type: 'linear',
|
||||||
|
colors: value.colors.map(item => ({
|
||||||
|
...item,
|
||||||
|
pos: parseInt(item.pos),
|
||||||
|
})),
|
||||||
|
rotate: value.rot,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
background = {
|
||||||
|
type: 'solid',
|
||||||
|
color: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const slide: Slide = {
|
||||||
|
id: nanoid(10),
|
||||||
|
elements: [],
|
||||||
|
background,
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseElements = (elements: Element[]) => {
|
||||||
|
for (const el of elements) {
|
||||||
|
const originWidth = el.width || 1
|
||||||
|
const originHeight = el.height || 1
|
||||||
|
const originLeft = el.left
|
||||||
|
const originTop = el.top
|
||||||
|
|
||||||
|
el.width = el.width * ratio
|
||||||
|
el.height = el.height * ratio
|
||||||
|
el.left = el.left * ratio
|
||||||
|
el.top = el.top * ratio
|
||||||
|
|
||||||
|
if (el.type === 'text') {
|
||||||
|
const textEl: PPTTextElement = {
|
||||||
|
type: 'text',
|
||||||
|
id: nanoid(10),
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
rotate: el.rotate,
|
||||||
|
defaultFontName: theme.value.fontName,
|
||||||
|
defaultColor: theme.value.fontColor,
|
||||||
|
content: convertFontSizePtToPx(el.content, ratio),
|
||||||
|
lineHeight: 1,
|
||||||
|
outline: {
|
||||||
|
color: el.borderColor,
|
||||||
|
width: el.borderWidth,
|
||||||
|
style: el.borderType,
|
||||||
|
},
|
||||||
|
fill: el.fillColor,
|
||||||
|
vertical: el.isVertical,
|
||||||
|
}
|
||||||
|
if (el.shadow) {
|
||||||
|
textEl.shadow = {
|
||||||
|
h: el.shadow.h * ratio,
|
||||||
|
v: el.shadow.v * ratio,
|
||||||
|
blur: el.shadow.blur * ratio,
|
||||||
|
color: el.shadow.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slide.elements.push(textEl)
|
||||||
|
}
|
||||||
|
else if (el.type === 'image') {
|
||||||
|
slide.elements.push({
|
||||||
|
type: 'image',
|
||||||
|
id: nanoid(10),
|
||||||
|
src: el.src,
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
fixedRatio: true,
|
||||||
|
rotate: el.rotate,
|
||||||
|
flipH: el.isFlipH,
|
||||||
|
flipV: el.isFlipV,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (el.type === 'audio') {
|
||||||
|
slide.elements.push({
|
||||||
|
type: 'audio',
|
||||||
|
id: nanoid(10),
|
||||||
|
src: el.blob,
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
rotate: 0,
|
||||||
|
fixedRatio: false,
|
||||||
|
color: theme.value.themeColor,
|
||||||
|
loop: false,
|
||||||
|
autoplay: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (el.type === 'video') {
|
||||||
|
slide.elements.push({
|
||||||
|
type: 'video',
|
||||||
|
id: nanoid(10),
|
||||||
|
src: (el.blob || el.src)!,
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
rotate: 0,
|
||||||
|
autoplay: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (el.type === 'shape') {
|
||||||
|
if (el.shapType === 'line' || /Connector/.test(el.shapType)) {
|
||||||
|
const lineElement = parseLineElement(el)
|
||||||
|
slide.elements.push(lineElement)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const shape = shapeList.find(item => item.pptxShapeType === el.shapType)
|
||||||
|
|
||||||
|
const vAlignMap: { [key: string]: ShapeTextAlign } = {
|
||||||
|
'mid': 'middle',
|
||||||
|
'down': 'bottom',
|
||||||
|
'up': 'top',
|
||||||
|
}
|
||||||
|
|
||||||
|
const element: PPTShapeElement = {
|
||||||
|
type: 'shape',
|
||||||
|
id: nanoid(10),
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
viewBox: [200, 200],
|
||||||
|
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
||||||
|
fill: el.fillColor || 'none',
|
||||||
|
fixedRatio: false,
|
||||||
|
rotate: el.rotate,
|
||||||
|
outline: {
|
||||||
|
color: el.borderColor,
|
||||||
|
width: el.borderWidth,
|
||||||
|
style: el.borderType,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
content: convertFontSizePtToPx(el.content, ratio),
|
||||||
|
defaultFontName: theme.value.fontName,
|
||||||
|
defaultColor: theme.value.fontColor,
|
||||||
|
align: vAlignMap[el.vAlign] || 'middle',
|
||||||
|
},
|
||||||
|
flipH: el.isFlipH,
|
||||||
|
flipV: el.isFlipV,
|
||||||
|
}
|
||||||
|
if (el.shadow) {
|
||||||
|
element.shadow = {
|
||||||
|
h: el.shadow.h * ratio,
|
||||||
|
v: el.shadow.v * ratio,
|
||||||
|
blur: el.shadow.blur * ratio,
|
||||||
|
color: el.shadow.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shape) {
|
||||||
|
element.path = shape.path
|
||||||
|
element.viewBox = shape.viewBox
|
||||||
|
|
||||||
|
if (shape.pathFormula) {
|
||||||
|
element.pathFormula = shape.pathFormula
|
||||||
|
element.viewBox = [el.width, el.height]
|
||||||
|
|
||||||
|
const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula]
|
||||||
|
if ('editable' in pathFormula && pathFormula.editable) {
|
||||||
|
element.path = pathFormula.formula(el.width, el.height, pathFormula.defaultValue)
|
||||||
|
element.keypoints = pathFormula.defaultValue
|
||||||
|
}
|
||||||
|
else element.path = pathFormula.formula(el.width, el.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (el.shapType === 'custom') {
|
||||||
|
if (el.path!.indexOf('NaN') !== -1) element.path = ''
|
||||||
|
else {
|
||||||
|
element.special = true
|
||||||
|
element.path = el.path!
|
||||||
|
|
||||||
|
const { maxX, maxY } = getSvgPathRange(element.path)
|
||||||
|
element.viewBox = [maxX || originWidth, maxY || originHeight]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.path) slide.elements.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (el.type === 'table') {
|
||||||
|
const row = el.data.length
|
||||||
|
const col = el.data[0].length
|
||||||
|
|
||||||
|
const style: TableCellStyle = {
|
||||||
|
fontname: theme.value.fontName,
|
||||||
|
color: theme.value.fontColor,
|
||||||
|
}
|
||||||
|
const data: TableCell[][] = []
|
||||||
|
for (let i = 0; i < row; i++) {
|
||||||
|
const rowCells: TableCell[] = []
|
||||||
|
for (let j = 0; j < col; j++) {
|
||||||
|
const cellData = el.data[i][j]
|
||||||
|
|
||||||
|
let textDiv: HTMLDivElement | null = document.createElement('div')
|
||||||
|
textDiv.innerHTML = cellData.text
|
||||||
|
const p = textDiv.querySelector('p')
|
||||||
|
const align = p?.style.textAlign || 'left'
|
||||||
|
|
||||||
|
const span = textDiv.querySelector('span')
|
||||||
|
const fontsize = span?.style.fontSize ? (parseInt(span?.style.fontSize) * ratio).toFixed(1) + 'px' : ''
|
||||||
|
const fontname = span?.style.fontFamily || ''
|
||||||
|
const color = span?.style.color || cellData.fontColor
|
||||||
|
|
||||||
|
rowCells.push({
|
||||||
|
id: nanoid(10),
|
||||||
|
colspan: cellData.colSpan || 1,
|
||||||
|
rowspan: cellData.rowSpan || 1,
|
||||||
|
text: textDiv.innerText,
|
||||||
|
style: {
|
||||||
|
...style,
|
||||||
|
align: ['left', 'right', 'center'].includes(align) ? (align as 'left' | 'right' | 'center') : 'left',
|
||||||
|
fontsize,
|
||||||
|
fontname,
|
||||||
|
color,
|
||||||
|
bold: cellData.fontBold,
|
||||||
|
backcolor: cellData.fillColor,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
textDiv = null
|
||||||
|
}
|
||||||
|
data.push(rowCells)
|
||||||
|
}
|
||||||
|
|
||||||
|
const colWidths: number[] = new Array(col).fill(1 / col)
|
||||||
|
|
||||||
|
slide.elements.push({
|
||||||
|
type: 'table',
|
||||||
|
id: nanoid(10),
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
colWidths,
|
||||||
|
rotate: 0,
|
||||||
|
data,
|
||||||
|
outline: {
|
||||||
|
width: el.borderWidth || 2,
|
||||||
|
style: el.borderType,
|
||||||
|
color: el.borderColor || '#eeece1',
|
||||||
|
},
|
||||||
|
cellMinHeight: 36,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (el.type === 'chart') {
|
||||||
|
let labels: string[]
|
||||||
|
let legends: string[]
|
||||||
|
let series: number[][]
|
||||||
|
|
||||||
|
if (el.chartType === 'scatterChart' || el.chartType === 'bubbleChart') {
|
||||||
|
labels = el.data[0].map((item, index) => `坐标${index + 1}`)
|
||||||
|
legends = ['X', 'Y']
|
||||||
|
series = el.data
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const data = el.data as ChartItem[]
|
||||||
|
labels = Object.values(data[0].xlabels)
|
||||||
|
legends = data.map(item => item.key)
|
||||||
|
series = data.map(item => item.values.map(v => v.y))
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: ChartOptions = {}
|
||||||
|
|
||||||
|
let chartType: ChartType = 'bar'
|
||||||
|
|
||||||
|
switch (el.chartType) {
|
||||||
|
case 'barChart':
|
||||||
|
case 'bar3DChart':
|
||||||
|
chartType = 'bar'
|
||||||
|
if (el.barDir === 'bar') chartType = 'column'
|
||||||
|
if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
|
||||||
|
break
|
||||||
|
case 'lineChart':
|
||||||
|
case 'line3DChart':
|
||||||
|
if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
|
||||||
|
chartType = 'line'
|
||||||
|
break
|
||||||
|
case 'areaChart':
|
||||||
|
case 'area3DChart':
|
||||||
|
if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
|
||||||
|
chartType = 'area'
|
||||||
|
break
|
||||||
|
case 'scatterChart':
|
||||||
|
case 'bubbleChart':
|
||||||
|
chartType = 'scatter'
|
||||||
|
break
|
||||||
|
case 'pieChart':
|
||||||
|
case 'pie3DChart':
|
||||||
|
chartType = 'pie'
|
||||||
|
break
|
||||||
|
case 'radarChart':
|
||||||
|
chartType = 'radar'
|
||||||
|
break
|
||||||
|
case 'doughnutChart':
|
||||||
|
chartType = 'ring'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
slide.elements.push({
|
||||||
|
type: 'chart',
|
||||||
|
id: nanoid(10),
|
||||||
|
chartType: chartType,
|
||||||
|
width: el.width,
|
||||||
|
height: el.height,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
rotate: 0,
|
||||||
|
themeColors: [theme.value.themeColor],
|
||||||
|
textColor: theme.value.fontColor,
|
||||||
|
data: {
|
||||||
|
labels,
|
||||||
|
legends,
|
||||||
|
series,
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (el.type === 'group' || el.type === 'diagram') {
|
||||||
|
const elements = el.elements.map(_el => ({
|
||||||
|
..._el,
|
||||||
|
left: _el.left + originLeft,
|
||||||
|
top: _el.top + originTop,
|
||||||
|
}))
|
||||||
|
parseElements(elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parseElements(item.elements)
|
||||||
|
slides.push(slide)
|
||||||
|
}
|
||||||
|
resData.slides = slides
|
||||||
|
resolve(resData)
|
||||||
|
})
|
||||||
}
|
}
|
|
@ -16,4 +16,4 @@ const app = createApp(App)
|
||||||
app.use(Icon)
|
app.use(Icon)
|
||||||
app.use(Directive)
|
app.use(Directive)
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.mount('#app')
|
app.mount('#app')
|
|
@ -1,186 +1,187 @@
|
||||||
import type { Slide } from '../types/slides'
|
import type { Slide } from '../types/slides'
|
||||||
|
|
||||||
export const slides: Slide[] = [
|
export const slides: Slide[] = [
|
||||||
{
|
// {
|
||||||
id: 'test-slide-1',
|
// id: 'test-slide-1',
|
||||||
elements: [
|
// elements: [
|
||||||
{
|
// {
|
||||||
type: 'shape',
|
// type: 'shape',
|
||||||
id: '4cbRxp',
|
// id: '4cbRxp',
|
||||||
left: 0,
|
// left: 0,
|
||||||
top: 200,
|
// top: 200,
|
||||||
width: 546,
|
// width: 546,
|
||||||
height: 362.5,
|
// height: 362.5,
|
||||||
viewBox: [200, 200],
|
// viewBox: [200, 200],
|
||||||
path: 'M 0 0 L 0 200 L 200 200 Z',
|
// path: 'M 0 0 L 0 200 L 200 200 Z',
|
||||||
fill: '#5b9bd5',
|
// fill: '#5b9bd5',
|
||||||
fixedRatio: false,
|
// fixedRatio: false,
|
||||||
opacity: 0.7,
|
// opacity: 0.7,
|
||||||
rotate: 0
|
// rotate: 0
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'shape',
|
// type: 'shape',
|
||||||
id: 'ookHrf',
|
// id: 'ookHrf',
|
||||||
left: 0,
|
// left: 0,
|
||||||
top: 0,
|
// top: 0,
|
||||||
width: 300,
|
// width: 300,
|
||||||
height: 320,
|
// height: 320,
|
||||||
viewBox: [200, 200],
|
// viewBox: [200, 200],
|
||||||
path: 'M 0 0 L 0 200 L 200 200 Z',
|
// path: 'M 0 0 L 0 200 L 200 200 Z',
|
||||||
fill: '#5b9bd5',
|
// fill: '#5b9bd5',
|
||||||
fixedRatio: false,
|
// fixedRatio: false,
|
||||||
flipV: true,
|
// flipV: true,
|
||||||
rotate: 0
|
// rotate: 0
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'text',
|
// type: 'text',
|
||||||
id: 'idn7Mx',
|
// id: 'idn7Mx',
|
||||||
left: 355,
|
// left: 355,
|
||||||
top: 65.25,
|
// top: 65.25,
|
||||||
width: 450,
|
// width: 450,
|
||||||
height: 188,
|
// height: 188,
|
||||||
lineHeight: 1.2,
|
// lineHeight: 1.2,
|
||||||
content: '<p><strong><span style=\"font-size: 112px;\">PPTist</span></strong></p>',
|
// content: '<p><strong><span style=\"font-size: 112px;\">PPTist</span></strong></p>',
|
||||||
rotate: 0,
|
// rotate: 0,
|
||||||
defaultFontName: 'Microsoft Yahei',
|
// defaultFontName: 'Microsoft Yahei',
|
||||||
defaultColor: '#333'
|
// defaultColor: '#333'
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'text',
|
// type: 'text',
|
||||||
id: '7stmVP',
|
// id: '7stmVP',
|
||||||
left: 355,
|
// left: 355,
|
||||||
top: 253.25,
|
// top: 253.25,
|
||||||
width: 585,
|
// width: 585,
|
||||||
height: 56,
|
// height: 56,
|
||||||
content: '<p><span style=\"font-size: 24px;\">基于 Vue 3.x + TypeScript 的在线演示文稿应用</span></p>',
|
// content: '<p><span style=\"font-size: 24px;\">基于 Vue 3.x + TypeScript 的在线演示文稿应用</span></p>',
|
||||||
rotate: 0,
|
// rotate: 0,
|
||||||
defaultFontName: 'Microsoft Yahei',
|
// defaultFontName: 'Microsoft Yahei',
|
||||||
defaultColor: '#333'
|
// defaultColor: '#333'
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'line',
|
// type: 'line',
|
||||||
id: 'FnpZs4',
|
// id: 'FnpZs4',
|
||||||
left: 361,
|
// left: 361,
|
||||||
top: 238,
|
// top: 238,
|
||||||
start: [0, 0],
|
// start: [0, 0],
|
||||||
end: [549, 0],
|
// end: [549, 0],
|
||||||
points: ['', ''],
|
// points: ['', ''],
|
||||||
color: '#5b9bd5',
|
// color: '#5b9bd5',
|
||||||
style: 'solid',
|
// style: 'solid',
|
||||||
width: 2,
|
// width: 2,
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
background: {
|
// background: {
|
||||||
type: 'solid',
|
// type: 'solid',
|
||||||
color: '#ffffff',
|
// color: '#ffffff',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
id: 'test-slide-2',
|
// id: 'test-slide-2',
|
||||||
elements: [
|
// elements: [
|
||||||
{
|
// {
|
||||||
type: 'text',
|
// type: 'text',
|
||||||
id: 'ptNnUJ',
|
// id: 'ptNnUJ',
|
||||||
left: 145,
|
// left: 145,
|
||||||
top: 148,
|
// top: 148,
|
||||||
width: 711,
|
// width: 711,
|
||||||
height: 77,
|
// height: 77,
|
||||||
lineHeight: 1.2,
|
// lineHeight: 1.2,
|
||||||
content: '<p style=\"text-align: center;\"><strong><span style=\"font-size: 48px;\">在此处添加标题</span></strong></p>',
|
// content: '<p style=\"text-align: center;\"><strong><span style=\"font-size: 48px;\">在此处添加标题</span></strong></p>',
|
||||||
rotate: 0,
|
// rotate: 0,
|
||||||
defaultFontName: 'Microsoft Yahei',
|
// defaultFontName: 'Microsoft Yahei',
|
||||||
defaultColor: '#333',
|
// defaultColor: '#333',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'text',
|
// type: 'text',
|
||||||
id: 'mRHvQN',
|
// id: 'mRHvQN',
|
||||||
left: 207.50000000000003,
|
// left: 207.50000000000003,
|
||||||
top: 249.84259259259264,
|
// top: 249.84259259259264,
|
||||||
width: 585,
|
// width: 585,
|
||||||
height: 56,
|
// height: 56,
|
||||||
content: '<p style=\"text-align: center;\"><span style=\"font-size: 24px;\">在此处添加副标题</span></p>',
|
// content: '<p style=\"text-align: center;\"><span style=\"font-size: 24px;\">在此处添加副标题</span></p>',
|
||||||
rotate: 0,
|
// rotate: 0,
|
||||||
defaultFontName: 'Microsoft Yahei',
|
// defaultFontName: 'Microsoft Yahei',
|
||||||
defaultColor: '#333',
|
// defaultColor: '#333',
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'line',
|
// type: 'line',
|
||||||
id: '7CQDwc',
|
// id: '7CQDwc',
|
||||||
left: 323.09259259259267,
|
// left: 323.09259259259267,
|
||||||
top: 238.33333333333334,
|
// top: 238.33333333333334,
|
||||||
start: [0, 0],
|
// start: [0, 0],
|
||||||
end: [354.8148148148148, 0],
|
// end: [354.8148148148148, 0],
|
||||||
points: ['', ''],
|
// points: ['', ''],
|
||||||
color: '#5b9bd5',
|
// color: '#5b9bd5',
|
||||||
style: 'solid',
|
// style: 'solid',
|
||||||
width: 4
|
// width: 4
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'shape',
|
// type: 'shape',
|
||||||
id: '09wqWw',
|
// id: '09wqWw',
|
||||||
left: -27.648148148148138,
|
// left: -27.648148148148138,
|
||||||
top: 432.73148148148147,
|
// top: 432.73148148148147,
|
||||||
width: 1056.2962962962963,
|
// width: 1056.2962962962963,
|
||||||
height: 162.96296296296296,
|
// height: 162.96296296296296,
|
||||||
viewBox: [200, 200],
|
// viewBox: [200, 200],
|
||||||
path: 'M 0 20 C 40 -40 60 60 100 20 C 140 -40 160 60 200 20 L 200 180 C 140 240 160 140 100 180 C 40 240 60 140 0 180 L 0 20 Z',
|
// path: 'M 0 20 C 40 -40 60 60 100 20 C 140 -40 160 60 200 20 L 200 180 C 140 240 160 140 100 180 C 40 240 60 140 0 180 L 0 20 Z',
|
||||||
fill: '#5b9bd5',
|
// fill: '#5b9bd5',
|
||||||
fixedRatio: false,
|
// fixedRatio: false,
|
||||||
rotate: 0
|
// rotate: 0
|
||||||
}
|
// }
|
||||||
],
|
// ],
|
||||||
background: {
|
// background: {
|
||||||
type: 'solid',
|
// type: 'solid',
|
||||||
color: '#fff',
|
// color: '#fff',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
id: 'test-slide-3',
|
// id: 'test-slide-3',
|
||||||
elements: [
|
// elements: [
|
||||||
{
|
// {
|
||||||
type: 'shape',
|
// type: 'shape',
|
||||||
id: 'vSheCJ',
|
// id: 'vSheCJ',
|
||||||
left: 183.5185185185185,
|
// left: 183.5185185185185,
|
||||||
top: 175.5092592592593,
|
// top: 175.5092592592593,
|
||||||
width: 605.1851851851851,
|
// width: 605.1851851851851,
|
||||||
height: 185.18518518518516,
|
// height: 185.18518518518516,
|
||||||
viewBox: [200, 200],
|
// viewBox: [200, 200],
|
||||||
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
// path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
||||||
fill: '#5b9bd5',
|
// fill: '#5b9bd5',
|
||||||
fixedRatio: false,
|
// fixedRatio: false,
|
||||||
rotate: 0
|
// rotate: 0
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'shape',
|
// type: 'shape',
|
||||||
id: 'Mpwv7x',
|
// id: 'Mpwv7x',
|
||||||
left: 211.29629629629628,
|
// left: 211.29629629629628,
|
||||||
top: 201.80555555555557,
|
// top: 201.80555555555557,
|
||||||
width: 605.1851851851851,
|
// width: 605.1851851851851,
|
||||||
height: 185.18518518518516,
|
// height: 185.18518518518516,
|
||||||
viewBox: [200, 200],
|
// viewBox: [200, 200],
|
||||||
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
// path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
||||||
fill: '#5b9bd5',
|
// fill: '#5b9bd5',
|
||||||
fixedRatio: false,
|
// fixedRatio: false,
|
||||||
rotate: 0,
|
// rotate: 0,
|
||||||
opacity: 0.7
|
// opacity: 0.7
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
type: 'text',
|
// type: 'text',
|
||||||
id: 'WQOTAp',
|
// id: 'WQOTAp',
|
||||||
left: 304.9074074074074,
|
// left: 304.9074074074074,
|
||||||
top: 198.10185185185182,
|
// top: 198.10185185185182,
|
||||||
width: 417.9629629629629,
|
// width: 417.9629629629629,
|
||||||
height: 140,
|
// height: 140,
|
||||||
content: '<p style=\"text-align: center;\"><strong><span style=\"font-size: 80px;\"><span style=\"color: rgb(255, 255, 255);\">感谢观看</span></span></strong></p>',
|
// content: '<p style=\"text-align: center;\"><strong><span style=\"font-size: 80px;\"><span style=\"color: rgb(255, 255, 255);\">感谢观看</span></span></strong></p>',
|
||||||
rotate: 0,
|
// rotate: 0,
|
||||||
defaultFontName: 'Microsoft Yahei',
|
// defaultFontName: 'Microsoft Yahei',
|
||||||
defaultColor: '#333',
|
// defaultColor: '#333',
|
||||||
wordSpace: 5
|
// wordSpace: 5
|
||||||
}
|
// }
|
||||||
],
|
// ],
|
||||||
background: {
|
// background: {
|
||||||
type: 'solid',
|
// type: 'solid',
|
||||||
color: '#fff',
|
// color: '#fff',
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
// {"id":"-3hMiOtk8M","elements":[],"background":{"type":"solid","color":"#fff"}},
|
||||||
]
|
]
|
|
@ -6,6 +6,8 @@ import { slides } from '../mocks/slides'
|
||||||
import { theme } from '../mocks/theme'
|
import { theme } from '../mocks/theme'
|
||||||
import { layouts } from '../mocks/layout'
|
import { layouts } from '../mocks/layout'
|
||||||
|
|
||||||
|
import PPTApi from '../api/store'
|
||||||
|
|
||||||
interface RemovePropData {
|
interface RemovePropData {
|
||||||
id: string
|
id: string
|
||||||
propName: string | string[]
|
propName: string | string[]
|
||||||
|
@ -155,14 +157,13 @@ export const useSlidesStore = defineStore('slides', {
|
||||||
this.slides = slides
|
this.slides = slides
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteSlide(slideId: string | string[]) {
|
async deleteSlide(slideId: string | string[]) {
|
||||||
const slidesId = Array.isArray(slideId) ? slideId : [slideId]
|
const slidesId = Array.isArray(slideId) ? slideId : [slideId]
|
||||||
const slides: Slide[] = JSON.parse(JSON.stringify(this.slides))
|
const slides: Slide[] = JSON.parse(JSON.stringify(this.slides))
|
||||||
|
|
||||||
const deleteSlidesIndex = []
|
const deleteSlidesIndex = []
|
||||||
for (const deletedId of slidesId) {
|
for (const deletedId of slidesId) {
|
||||||
const index = slides.findIndex(item => item.id === deletedId)
|
const index = slides.findIndex(item => item.id === deletedId)
|
||||||
deleteSlidesIndex.push(index)
|
|
||||||
|
|
||||||
const deletedSlideSection = slides[index].sectionTag
|
const deletedSlideSection = slides[index].sectionTag
|
||||||
if (deletedSlideSection) {
|
if (deletedSlideSection) {
|
||||||
|
@ -172,8 +173,13 @@ export const useSlidesStore = defineStore('slides', {
|
||||||
slides[index + 1].sectionTag = deletedSlideSection
|
slides[index + 1].sectionTag = deletedSlideSection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 后端先删除,再更新页面数据
|
||||||
slides.splice(index, 1)
|
const isDel = await PPTApi.delSlide(deletedId)
|
||||||
|
if (isDel) {
|
||||||
|
// 后端删除成功,更新页面数据
|
||||||
|
deleteSlidesIndex.push(index)
|
||||||
|
slides.splice(index, 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let newIndex = Math.min(...deleteSlidesIndex)
|
let newIndex = Math.min(...deleteSlidesIndex)
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
importPPTXFile(files)
|
importPPTXFile(files)
|
||||||
mainMenuVisible = false
|
mainMenuVisible = false
|
||||||
}">
|
}">
|
||||||
<PopoverMenuItem>导入 pptx 文件(测试版)</PopoverMenuItem>
|
<PopoverMenuItem>导入 pptx 文件</PopoverMenuItem>
|
||||||
</FileInput>
|
</FileInput>
|
||||||
<PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem>
|
<PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem>
|
||||||
<PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem>
|
<PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem>
|
||||||
<PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem>
|
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem> -->
|
||||||
<PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem>
|
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> -->
|
||||||
<PopoverMenuItem @click="mainMenuVisible = false; hotkeyDrawerVisible = true">快捷操作</PopoverMenuItem>
|
<PopoverMenuItem @click="mainMenuVisible = false; hotkeyDrawerVisible = true">快捷操作</PopoverMenuItem>
|
||||||
</template>
|
</template>
|
||||||
<div class="menu-item"><IconHamburgerButton class="icon" /></div>
|
<div class="menu-item"><IconHamburgerButton class="icon" /></div>
|
||||||
|
@ -57,9 +57,12 @@
|
||||||
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
|
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
|
||||||
<IconDownload class="icon" />
|
<IconDownload class="icon" />
|
||||||
</div>
|
</div>
|
||||||
<a class="github-link" v-tooltip="'Copyright © 2020-PRESENT pipipi-pikachu'" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
|
<div class="menu-item" v-tooltip="`${userStore.user.parentDeptName}-${userStore.user.nickName}`">
|
||||||
|
<el-avatar size="small" :src="avatar" />
|
||||||
|
</div>
|
||||||
|
<!-- <a class="github-link" v-tooltip="'Copyright © 2020-PRESENT pipipi-pikachu'" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
|
||||||
<div class="menu-item"><IconGithub class="icon" /></div>
|
<div class="menu-item"><IconGithub class="icon" /></div>
|
||||||
</a>
|
</a> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
|
@ -92,6 +95,9 @@ import Input from '../../../components/Input.vue'
|
||||||
import Popover from '../../../components/Popover.vue'
|
import Popover from '../../../components/Popover.vue'
|
||||||
import PopoverMenuItem from '../../../components/PopoverMenuItem.vue'
|
import PopoverMenuItem from '../../../components/PopoverMenuItem.vue'
|
||||||
|
|
||||||
|
import useUserStore from '@/store/modules/user' // 外部-用户信息
|
||||||
|
|
||||||
|
const userStore = useUserStore() // 外部-用户信息
|
||||||
const mainStore = useMainStore()
|
const mainStore = useMainStore()
|
||||||
const slidesStore = useSlidesStore()
|
const slidesStore = useSlidesStore()
|
||||||
const { title } = storeToRefs(slidesStore)
|
const { title } = storeToRefs(slidesStore)
|
||||||
|
@ -104,7 +110,7 @@ const hotkeyDrawerVisible = ref(false)
|
||||||
const editingTitle = ref(false)
|
const editingTitle = ref(false)
|
||||||
const titleInputRef = ref<InstanceType<typeof Input>>()
|
const titleInputRef = ref<InstanceType<typeof Input>>()
|
||||||
const titleValue = ref('')
|
const titleValue = ref('')
|
||||||
|
const avatar = ref(import.meta.env.VITE_APP_BASE_API+userStore.avatar) // 用户头像
|
||||||
const startEditTitle = () => {
|
const startEditTitle = () => {
|
||||||
titleValue.value = title.value
|
titleValue.value = title.value
|
||||||
editingTitle.value = true
|
editingTitle.value = true
|
||||||
|
|
|
@ -50,4 +50,10 @@ export class school {
|
||||||
// 获取学校管理审核
|
// 获取学校管理审核
|
||||||
static checkSchool = data => ApiService.publicHttp(`/smarttalk/audit/checkSchool`,data,'post')
|
static checkSchool = data => ApiService.publicHttp(`/smarttalk/audit/checkSchool`,data,'post')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Other {
|
||||||
|
static baseUrl = "/common/upload"
|
||||||
|
// 测试
|
||||||
|
static uploadFile = data => ApiService.publicHttp(this.baseUrl, data, 'post', null, 'file')
|
||||||
}
|
}
|
|
@ -108,4 +108,21 @@ export function pyOCRAPI(path) {
|
||||||
imageBas64: path,
|
imageBas64: path,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc: 菁优网转发
|
||||||
|
* @return: {*}
|
||||||
|
* @param {*} url
|
||||||
|
* @param {*} config
|
||||||
|
*/
|
||||||
|
export function getJYPath(url,config) {
|
||||||
|
config.params = config.params?config.params:{}
|
||||||
|
config.params["getjypath"] = url;
|
||||||
|
return request({
|
||||||
|
url: "/jy/proxy",
|
||||||
|
method: config.method||"get",
|
||||||
|
params: config.params
|
||||||
|
})
|
||||||
}
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询entpcourse列表
|
||||||
|
export function listEntpcourse(query) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询entpcourse详细
|
||||||
|
export function getEntpcourse(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增entpcourse
|
||||||
|
export function addEntpcourse(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改entpcourse
|
||||||
|
export function updateEntpcourse(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除entpcourse
|
||||||
|
export function delEntpcourse(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcourse/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function lpmChat(query) {
|
||||||
|
return request({
|
||||||
|
url: '/llm/chatToSD',
|
||||||
|
method: 'POST',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
|
@ -85,6 +85,22 @@ export function updateFileByArray(data) {
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// zdg: 批量更新pptist - 新
|
||||||
|
export function batchUpdateNew(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcoursefile/batch/update',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// zdg: 批量新增pptist - 新
|
||||||
|
export function batchAddNew(data) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcoursefile/batch/add',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 修改entpcoursefile
|
// 修改entpcoursefile
|
||||||
export function updateFile2Redis(data) {
|
export function updateFile2Redis(data) {
|
||||||
|
@ -102,6 +118,14 @@ export function delEntpcoursefile(id) {
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 删除entpcoursefile - new
|
||||||
|
export function delEntpcoursefileNew(id) {
|
||||||
|
return request({
|
||||||
|
url: '/education/entpcoursefile/delete',
|
||||||
|
method: 'get',
|
||||||
|
params: {id}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 保存base64图片,返回url
|
// 保存base64图片,返回url
|
||||||
export function saveEntpCourseBase64File(data) {
|
export function saveEntpCourseBase64File(data) {
|
||||||
|
|
|
@ -2,12 +2,21 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
// 查询知识点列表
|
// 查询知识点列表
|
||||||
export function listKnowlegepoint(query) {
|
export function listKnowlegepoint(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/point/list',
|
url: '/point/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询知识点列表
|
||||||
|
export function listKnowlegepointFormat(query) {
|
||||||
|
return request({
|
||||||
|
url: '/point/formatList',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 查询一级知识点查下级所有层级
|
// 查询一级知识点查下级所有层级
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<div id="aptContainer"></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
modelValue:[Object,String], // json数据
|
||||||
|
width: { // 宽度
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
height: { // 高度
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
domId: { // 渲染元素ID
|
||||||
|
type: String,
|
||||||
|
default: 'aptContainer'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
description: 'apt预览组件-konva',
|
||||||
|
// konva相关对象 舞台 图层
|
||||||
|
konvaStage:null, konvaLayer:null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() { // 页面渲染完
|
||||||
|
this.initKonva()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue: {
|
||||||
|
immediate: true, // 初始是否触发
|
||||||
|
handler(v) { // 参数1新值,参数2 旧值
|
||||||
|
!!v && this.loadJson()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 初始化Konva
|
||||||
|
initKonva() {
|
||||||
|
if(!this.width || !this.height) return false // 没有默认宽度高度直接使用数据中的
|
||||||
|
this.konvaStage = new Konva.Stage({
|
||||||
|
container: "aptContainer", // id of container <div>
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 加载数据
|
||||||
|
loadJson(data = this.modelValue) {
|
||||||
|
if (!data) return false
|
||||||
|
this.konvaStage = Konva.Node.create(data, this.domId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.aptContainer{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<el-link :href="href" target="_blank" type="primary">
|
||||||
|
<slot name="content">{{content}}</slot>
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
href: { // 跳转地址
|
||||||
|
type: String,
|
||||||
|
default: 'https://beian.miit.gov.cn'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
description: '备案号-添加',
|
||||||
|
content: '备案号:苏ICP备2024097972号-1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,6 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- 表单-组件(自定义) -->
|
<!-- 表单-组件(自定义) -->
|
||||||
<slot>
|
|
||||||
<el-row v-bind="rows||{}">
|
<el-row v-bind="rows||{}">
|
||||||
<!-- 其他内容-start -->
|
<!-- 其他内容-start -->
|
||||||
<slot name="start"></slot>
|
<slot name="start"></slot>
|
||||||
|
@ -80,7 +79,6 @@
|
||||||
<!-- 其他内容-end -->
|
<!-- 其他内容-end -->
|
||||||
<slot name="end"></slot>
|
<slot name="end"></slot>
|
||||||
</el-row>
|
</el-row>
|
||||||
</slot>
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
@ -161,7 +159,9 @@ export default {
|
||||||
// 确定--提交
|
// 确定--提交
|
||||||
confirm() {
|
confirm() {
|
||||||
const formRefs = this.$refs.form
|
const formRefs = this.$refs.form
|
||||||
|
const isBool = this.$attrs.confirm && typeof this.$attrs.confirm === 'function'
|
||||||
this.$emit('confirm', formRefs)
|
this.$emit('confirm', formRefs)
|
||||||
|
if (isBool) this.$attrs.confirm(formRefs)
|
||||||
},
|
},
|
||||||
// 重置
|
// 重置
|
||||||
resetFields() {
|
resetFields() {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-pagination v-bind="pageOpt"
|
||||||
|
@change="(...agrs) => $emit('pageChange',...agrs)"
|
||||||
|
v-model:current-page="curPage" v-model:page-size="limit" />
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 功能说明:自定义-分页器
|
||||||
|
export default {
|
||||||
|
name: 'cPage',
|
||||||
|
props: {
|
||||||
|
page: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
def: {
|
||||||
|
// size: 'small',
|
||||||
|
background: false,
|
||||||
|
pageSizes: [10, 20, 30, 40, 50, 100],
|
||||||
|
layout: 'total, sizes, prev, pager, next',
|
||||||
|
// layout: 'total, sizes, prev, pager, next, jumper',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pageOpt() {
|
||||||
|
return Object.assign({},this.def, this.page)
|
||||||
|
},
|
||||||
|
curPage: {
|
||||||
|
get() { return this.page.curPage },
|
||||||
|
set(val) { this.page.curPage = val }
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
get() { return this.page.limit },
|
||||||
|
set(val) { this.page.limit = val }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {},
|
||||||
|
methods: {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,280 @@
|
||||||
|
<template>
|
||||||
|
<!-- 组件 -->
|
||||||
|
<div>
|
||||||
|
<slot name="top">
|
||||||
|
<div class="c-search" :hide="!isFold||isDef" v-if="fItems.length" :style="$attrs.style">
|
||||||
|
<transition name="el-zoom-in-top">
|
||||||
|
<c-form v-show="isFold" ref="search" v-if="!noSearch && search" :form="search"
|
||||||
|
:itemOption="fItems" :option="fOption" :onEnter="fOnEnter" :rows="fRows" :cols="fCols">
|
||||||
|
<template v-for="item in formSlots" #[item]="data">
|
||||||
|
<slot :name="`f${item}`" v-bind="data"></slot>
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<slot name="topAdd" v-if="$slots.topAdd"></slot>
|
||||||
|
<slot name="search" v-if="!$attrs.searchBtnNe">
|
||||||
|
<el-col style="text-align: center;margin: 5px 0 15px;">
|
||||||
|
<el-button icon="Search" type="primary" @click="$emit('change','query')">查询</el-button>
|
||||||
|
<el-button type="warning" @click="$emit('change','reset')">重置</el-button>
|
||||||
|
</el-col>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</c-form>
|
||||||
|
</transition>
|
||||||
|
<div v-if="!isDef" :class="['fold',{hide:!isFold}]" @click="isFold=!isFold">
|
||||||
|
<!-- <i :class="'el-icon-arrow-' + (isFold?'down':'up')"></i> -->
|
||||||
|
<el-icon><component :is="isFold?'ArrowUp':'ArrowDown'"></component></el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
<slot name="start"></slot>
|
||||||
|
<div :class="['m-list',{'ne':!isMain, hide:!isFold}]">
|
||||||
|
<slot name="tStart"></slot>
|
||||||
|
<el-table ref="table" v-bind="tAttrs" v-on="tOns">
|
||||||
|
<template #empty><slot name="empty">{{emptyText}}</slot></template>
|
||||||
|
<template #append><slot name="append"></slot></template>
|
||||||
|
<slot>
|
||||||
|
<template v-for="(item, index) in optionItems" :key="index">
|
||||||
|
<el-table-column v-bind="item">
|
||||||
|
<!-- 列-表头-自定义 -->
|
||||||
|
<template #header="{column, $index}">
|
||||||
|
<slot :name="item.prop+'_header'" :column="column" :$index="$index">
|
||||||
|
<slot name="header" :column="column" :$index="$index">{{ item.label }}</slot>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<!-- 列内容-自定义 -->
|
||||||
|
<template #default="{row, column, $index}" v-if="item.type!='selection'">
|
||||||
|
<slot :name="item.prop" :row="row" :column="column" :$index="$index" :value="row[item.prop]">
|
||||||
|
<span v-if="item.attrs" v-bind="item.attrs">{{ defVal(row,item, $index, $attrs, column) }}</span>
|
||||||
|
<slot v-else :row="row" :column="column" :$index="$index" :value="row[item.prop]">{{ defVal(row,item, $index, $attrs, column) }}</slot>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
</slot>
|
||||||
|
</el-table>
|
||||||
|
<slot name="end"></slot>
|
||||||
|
<slot name="page">
|
||||||
|
<c-page v-if="!noPage" :page="page" v-on="$attrs" class="c-page" border="false"></c-page>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import cForm from './cForm.vue'
|
||||||
|
export default {
|
||||||
|
// inheritAttrs: false,
|
||||||
|
components: { cForm },
|
||||||
|
name: 'cTable',
|
||||||
|
props: {
|
||||||
|
option: Array, // 列配置
|
||||||
|
search: Object, // 搜索
|
||||||
|
fItems: { // 搜索字段配置
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
fOption: { // 搜索-表单配置
|
||||||
|
type: Object,
|
||||||
|
default: () => ({ labelW: '80px' })
|
||||||
|
// default: () => ({ inline: true, labelW: '80px', size: 'small' })
|
||||||
|
},
|
||||||
|
fOnEnter: Function, // 搜索--回车事件-公共
|
||||||
|
fRows: { // 搜索-行
|
||||||
|
type: Object,
|
||||||
|
default: _ => ({ gutter: 20 })
|
||||||
|
},
|
||||||
|
fCols: { // 搜索-非内联 inline
|
||||||
|
type: Object,
|
||||||
|
default: _ => ({ span: 8, xs: 24, sm: 12, md: 8, lg: 6 })
|
||||||
|
},
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: '暂无数据'
|
||||||
|
},
|
||||||
|
isDef: { // 默认样式-无美化
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
isMain: { // 间距
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
fold: { // 是否打开搜索
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
data: Object, // 数据
|
||||||
|
page: Object, // 分页
|
||||||
|
noSearch: Boolean, // 关闭搜索
|
||||||
|
noPage: Boolean, // 关闭分页
|
||||||
|
noDef: Boolean, // 取消默认配置
|
||||||
|
isMaxHeight: Boolean, // 是否最大高度
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 搜索-插槽 打开搜索
|
||||||
|
formSlots: [], isFold: this.fold, height: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
optionItems() { // 默认列
|
||||||
|
const list = this.option || []
|
||||||
|
return list.map(o => {
|
||||||
|
const def = { align: 'center', showOverflowTooltip: true }
|
||||||
|
if (o.width && o.showOverflowTooltip == undefined){
|
||||||
|
o.showOverflowTooltip = true // 设置宽度时,内容超出默认隐藏
|
||||||
|
}
|
||||||
|
return this.noDef ? o : Object.assign(def, o)
|
||||||
|
})
|
||||||
|
.filter(o => o.show ?? true) // 过滤掉隐藏的-show
|
||||||
|
.filter(o => o.visible ?? true) // 过滤掉隐藏的-visible
|
||||||
|
},
|
||||||
|
tAttrs() { // $attrs 过滤掉其他属性
|
||||||
|
const attrs = {...this.$attrs}
|
||||||
|
Object.keys(attrs).forEach(key => {
|
||||||
|
// 空值过滤掉
|
||||||
|
if (!attrs[key]??null) delete attrs[key]
|
||||||
|
// 分页相关属性 过滤掉
|
||||||
|
else if (/^page.*/.test(key)) delete attrs[key]
|
||||||
|
})
|
||||||
|
// 其他属性处理 如 'style', 'class', 't-style', 't-class'
|
||||||
|
const tArr = Object.keys(attrs).filter(k => k.startsWith('t-'))
|
||||||
|
if (tArr.length) tArr.forEach(k => attrs[k.slice(2)] = attrs[k])
|
||||||
|
else ['style', 'class'].forEach(k => delete attrs[k])
|
||||||
|
// 高度处理
|
||||||
|
const hArrs = ['height', 'max-height', 'maxHeight']
|
||||||
|
const isH = Object.keys(attrs).some(k => hArrs.includes(k))
|
||||||
|
// 默认高度 默认设置最大高度
|
||||||
|
if (!isH) attrs[this.isMaxHeight?'max-height':'height'] = 500
|
||||||
|
// 设置数据
|
||||||
|
if (this.data) attrs.data = this.data
|
||||||
|
return attrs
|
||||||
|
},
|
||||||
|
tOns() { // $on 过滤掉其他属性
|
||||||
|
const attrs = {...this.$attrs}
|
||||||
|
return Object.keys(attrs).reduce((p, c, a ) => {
|
||||||
|
if(typeof attrs[c]=='function')p[c]=attrs[c]; return p
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {},
|
||||||
|
mounted() {
|
||||||
|
this.getFormSlots()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 获取插槽名-搜索-插槽
|
||||||
|
getFormSlots() {
|
||||||
|
const props = this.fItems.map(o => o.prop)
|
||||||
|
const keys = Object.keys(this.$slots)
|
||||||
|
this.formSlots = keys.filter(k => {
|
||||||
|
const isForm = /^f.*/.test(k) // 是否为搜索插槽
|
||||||
|
const isProp = props.some(p => k.endsWith(p)) // 该配置是否在配置中存在-不存在过滤掉
|
||||||
|
return isForm && isProp
|
||||||
|
}).map(k => k.replace(/^f/, '')) // 返回表单内部自定义插槽名
|
||||||
|
},
|
||||||
|
// 默认值处理
|
||||||
|
defVal(row, item, rowInd, attr, column) {
|
||||||
|
switch (item.type) {
|
||||||
|
case 'index': { // 默认-ind
|
||||||
|
const { curPage, limit, offset } = attr.page || attr
|
||||||
|
const start = offset || (curPage - 1) * limit || 0
|
||||||
|
return rowInd + 1 + start
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return item.format ? item.format(row[item.prop], row, column, rowInd) : row[item.prop]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
// @import '~@/assets/styles/cmixin';
|
||||||
|
// $w 宽| $h 高| $bg 背景| $tBr 块圆角度 $tBg 块背景
|
||||||
|
@mixin scrollBar($w:8px,$h:8px,$tBr:20px,
|
||||||
|
$bg:#d3dce6,$tBg:#99a9bf, $tHbg: #409eff) {
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track-piece {
|
||||||
|
background: $bg;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: $w;
|
||||||
|
height: $h;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: $tBg;
|
||||||
|
border-radius: $tBr;
|
||||||
|
@if $tHbg {
|
||||||
|
&:hover{
|
||||||
|
background: $tHbg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table){
|
||||||
|
|
||||||
|
.el-table__expand-column{
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.el-table__body-wrapper{
|
||||||
|
@include scrollBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// css调整
|
||||||
|
.c-search{
|
||||||
|
--bg: #fff;
|
||||||
|
padding-top: 10px;
|
||||||
|
// padding: 20px 20px 0;
|
||||||
|
background: var(--bg);
|
||||||
|
margin-bottom: 30px;
|
||||||
|
border-radius: 4px;
|
||||||
|
&[hide=true]{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.fold{
|
||||||
|
--box-color: rgba(24,144,255,0.2);
|
||||||
|
--box-hover-color: rgba(24,144,255,0.6);
|
||||||
|
width: 100px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
background: var(--bg);
|
||||||
|
border-radius: 0 0 50px 50px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%,5px);
|
||||||
|
box-shadow: 0px 3px 3px var(--box-color);
|
||||||
|
i{font-size: inherit;}
|
||||||
|
&:hover{
|
||||||
|
box-shadow: 0px 3px 3px var(--box-hover-color);
|
||||||
|
i{color: #1890ff;}
|
||||||
|
}
|
||||||
|
&.hide{
|
||||||
|
top: 12px;
|
||||||
|
border-radius: 50px 50px 0 0;
|
||||||
|
box-shadow: 0 -3px 3px var(--box-color);
|
||||||
|
&:hover{box-shadow: 0 -3px 3px var(--box-hover-color);}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.el-form){
|
||||||
|
.el-form-item{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 改样式
|
||||||
|
.m-list{
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
&.ne{padding: 0;}
|
||||||
|
&.hide{
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.c-page{
|
||||||
|
background: #fff;
|
||||||
|
margin-top: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -71,4 +71,9 @@ defineExpose({
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.format-work-desc > :is(div):first-child){
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -40,11 +40,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted, watch } from 'vue'
|
||||||
import { conversation, completion } from '@/api/mode/index'
|
import { completion } from '@/api/mode/index'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
import { dataSetJson } from '@/utils/comm.js'
|
||||||
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
const textarea = ref('')
|
const textarea = ref('')
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ const props = defineProps({
|
||||||
return { name: '11' }
|
return { name: '11' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modeType: {
|
type: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,7 @@ const send = () =>{
|
||||||
msg: textarea.value
|
msg: textarea.value
|
||||||
})
|
})
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
getConversation(textarea.value)
|
getCompletion(textarea.value)
|
||||||
textarea.value = ''
|
textarea.value = ''
|
||||||
}
|
}
|
||||||
const curNode = reactive({})
|
const curNode = reactive({})
|
||||||
|
@ -86,42 +87,53 @@ const params = reactive(
|
||||||
dataset_id: ''
|
dataset_id: ''
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// 获取会话ID
|
|
||||||
const getConversation = (val) => {
|
|
||||||
|
|
||||||
getCompletion(val)
|
|
||||||
}
|
|
||||||
// 大模型对话
|
// 大模型对话
|
||||||
const getCompletion = async (val) => {
|
const getCompletion = async (val) => {
|
||||||
try {
|
try {
|
||||||
|
params.prompt = `按照${val}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value} 对${curNode.itemtitle}进行教学分析`
|
||||||
params.prompt = `根据${curNode.edustage}${curNode.edusubject}课标${props.item.name},${val}`
|
|
||||||
const { data } = await completion(params)
|
const { data } = await completion(params)
|
||||||
let answer = data.answer
|
let answer = data.answer
|
||||||
msgList.value.push({
|
msgList.value.push({
|
||||||
type: 'robot',
|
type: 'robot',
|
||||||
msg: answer,
|
msg: answer,
|
||||||
})
|
})
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
loaded.value = false
|
loaded.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveAdjust = (item) =>{
|
const saveAdjust = (item) =>{
|
||||||
emit('saveAdjust', item.msg)
|
|
||||||
isDialog.value = false
|
isDialog.value = false
|
||||||
ElMessage.success('操作成功')
|
ElMessage.success('操作成功')
|
||||||
|
emitter.on('saveAdjust', item.msg)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modeType = ref('课标')
|
||||||
|
watch(() => props.type, (newVal) => {
|
||||||
|
if (newVal == 1){
|
||||||
|
modeType.value = '课标'
|
||||||
|
}
|
||||||
|
if (newVal == 2){
|
||||||
|
modeType.value = '教材'
|
||||||
|
}
|
||||||
|
if (newVal == 2){
|
||||||
|
modeType.value = '考试'
|
||||||
|
}
|
||||||
|
|
||||||
|
}, { immediate: false })
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data);
|
||||||
let text = props.modeType == 1 ? '课标': props.modeType == 2 ? '教材' : '考试'
|
let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
|
||||||
|
|
||||||
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
params.dataset_id = dataSetJson[jsonKey]
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -152,7 +164,7 @@ onMounted(() => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
}
|
}
|
||||||
.chart-item {
|
.chart-item {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
@ -257,4 +269,4 @@ onMounted(() => {
|
||||||
transform: scale(0.01);
|
transform: scale(0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
import { ref, watch} from 'vue'
|
import { ref, watch} from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { editTempResult } from '@/api/mode/index.js'
|
import { editTempResult } from '@/api/mode/index.js'
|
||||||
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
const textarea = ref('')
|
const textarea = ref('')
|
||||||
|
|
||||||
|
@ -49,12 +50,12 @@ watch(() => props.item.answer, (newVal) => {
|
||||||
}
|
}
|
||||||
},{ deep: true })
|
},{ deep: true })
|
||||||
|
|
||||||
const emit = defineEmits(['saveEdit'])
|
|
||||||
const onSave = () =>{
|
const onSave = () =>{
|
||||||
editTempResult({id: props.item.reultId, content: textarea.value}).then( res =>{
|
editTempResult({id: props.item.resultId, content: textarea.value}).then( res =>{
|
||||||
isDialog.value = false
|
isDialog.value = false
|
||||||
ElMessage.success('操作成功')
|
ElMessage.success('操作成功')
|
||||||
emit('saveEdit', textarea.value)
|
emitter.emit('onGetChild', textarea.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-header flex">
|
|
||||||
<div class="header-left flex">
|
|
||||||
<el-button link @click="showDialog = true">
|
|
||||||
{{ curNode.edustage}}{{ curNode.edusubject }}{{ type == 1 ? '课标研读': '教材分析'}}<i class="iconfont icon-xiangxia"></i>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="header-right flex">
|
|
||||||
<el-dropdown @command="changeTemplate" :hide-on-click="false">
|
|
||||||
<span class="el-dropdown-link">
|
|
||||||
{{ curTemplate.name }}
|
|
||||||
<i class="iconfont icon-xiangxia"></i>
|
|
||||||
</span>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.name
|
|
||||||
}}</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" link @click="onAdd">
|
|
||||||
<el-icon>
|
|
||||||
<Plus />
|
|
||||||
</el-icon>
|
|
||||||
添加提示词
|
|
||||||
</el-button>
|
|
||||||
<!-- <el-button type="primary" link>保存模板</el-button> -->
|
|
||||||
<el-button type="primary" @click="aiRead">一键研读</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--添加提示词-->
|
|
||||||
<keywordDialog v-model="wordDialog" :modeType="type" :isAdd="wordDialog" :item="curTemplate" />
|
|
||||||
<Dialog v-model="showDialog" :modeType="type" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
|
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
|
||||||
import { ElMessageBox } from 'element-plus'
|
|
||||||
import { modelList } from '@/api/mode/index'
|
|
||||||
import Dialog from './dialog.vue'
|
|
||||||
import keywordDialog from './keyword-dialog.vue'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
|
|
||||||
const wordDialog = ref(false)
|
|
||||||
const props = defineProps({
|
|
||||||
type: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
watch(() => props.type, (newValue) => {
|
|
||||||
console.log(newValue, 'newValue2');
|
|
||||||
}, { immediate: true });
|
|
||||||
|
|
||||||
const emit = defineEmits(['changeTemp', 'onRead'])
|
|
||||||
|
|
||||||
const showDialog = ref(false)
|
|
||||||
|
|
||||||
const aiRead = () => {
|
|
||||||
emit('onRead')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 当前模板名称
|
|
||||||
const curTemplate = reactive({ name: '', id: '' })
|
|
||||||
// 模板列表
|
|
||||||
const templateList = ref([])
|
|
||||||
// 获取模板列表
|
|
||||||
const getTemplateList = () => {
|
|
||||||
modelList({ model: props.type, type: 1, pageNum: 1, pageSize: 10000 }).then(res => {
|
|
||||||
templateList.value = res.rows
|
|
||||||
Object.assign(curTemplate, res.rows[0]);
|
|
||||||
emit('changeTemp', res.rows[0].id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 模板切换
|
|
||||||
const changeTemplate = (val) => {
|
|
||||||
|
|
||||||
ElMessageBox.confirm(
|
|
||||||
'切换模板将清除当前研读结果?',
|
|
||||||
'提示',
|
|
||||||
{
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
Object.assign(curTemplate, val);
|
|
||||||
emit('changeTemp', curTemplate.id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter.on('onGetMain', () =>{
|
|
||||||
getTemplateList()
|
|
||||||
})
|
|
||||||
|
|
||||||
const onAdd = () => {
|
|
||||||
wordDialog.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('onGetMain')
|
|
||||||
})
|
|
||||||
const curNode = reactive({})
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
getTemplateList()
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container-header {
|
|
||||||
height: 45px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 5px 5px 0 0;
|
|
||||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
|
||||||
|
|
||||||
.header-left {
|
|
||||||
width: 50%;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-right {
|
|
||||||
width: 50%;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiangxia {
|
|
||||||
margin-left: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="mode" :show-close="false" width="600" destroy-on-close>
|
<el-dialog v-model="mode" :show-close="false" width="600" append-to-body destroy-on-close>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="custom-header flex">
|
<div class="custom-header flex">
|
||||||
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
||||||
|
@ -12,12 +12,10 @@
|
||||||
<el-form-item label="名称">
|
<el-form-item label="名称">
|
||||||
<el-input v-model="form.name" />
|
<el-input v-model="form.name" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="提示词" v-if="item.ex3 == '1' ? false : true">
|
<el-form-item label="提示词" v-if="item.ex3 == '1' ? false : true">
|
||||||
<el-input v-model="form.prompt" type="textarea" />
|
<el-input v-model="form.prompt" type="textarea" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
|
@ -52,14 +50,9 @@ const props = defineProps({
|
||||||
return { ex3: '' }
|
return { ex3: '' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tempId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
prompt: '',
|
prompt: '',
|
||||||
|
@ -73,16 +66,17 @@ watch(() => props.isAdd, (newVal) => {
|
||||||
|
|
||||||
}, { immediate: false })
|
}, { immediate: false })
|
||||||
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const saveAdd = async () => {
|
const saveAdd = async () => {
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
if (props.item.ex3 == '1') {
|
if (props.item.ex3 == '1') {
|
||||||
|
|
||||||
if (props.isAdd) {
|
if (props.isAdd) {
|
||||||
try {
|
try {
|
||||||
// 系统预设模板 copy一份
|
// 系统预设模板 copy一份
|
||||||
const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
|
const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
|
||||||
emitter.emit('onGetChild')
|
emitter.emit('onGetMain')
|
||||||
ElMessage.success(msg)
|
ElMessage.success(msg)
|
||||||
mode.value = false
|
mode.value = false
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -138,7 +132,6 @@ const onAddChildTemp = async (parentId) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -56,10 +56,10 @@ const title = computed(() => {
|
||||||
const radio = ref(1)
|
const radio = ref(1)
|
||||||
const radioList = ref([
|
const radioList = ref([
|
||||||
{ label: '浏览研读', value: 1 },
|
{ label: '浏览研读', value: 1 },
|
||||||
{ label: '跨学科研读', value: 2 },
|
// { label: '跨学科研读', value: 2 },
|
||||||
{ label: '跨学段研读', value: 3 },
|
// { label: '跨学段研读', value: 3 },
|
||||||
{ label: '课标修订研读', value: 4 },
|
// { label: '课标修订研读', value: 4 },
|
||||||
{ label: '自由研读', value: 5 },
|
// { label: '自由研读', value: 5 },
|
||||||
])
|
])
|
||||||
const list = ref([
|
const list = ref([
|
||||||
{
|
{
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<div class="container-left-page flex">
|
||||||
|
<div class="container-left-header flex">
|
||||||
|
<el-button link @click="onClick">
|
||||||
|
{{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : '教材分析' }}<i
|
||||||
|
class="iconfont icon-xiangxia"></i>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="container-left-pdf">
|
||||||
|
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
|
||||||
|
</div>
|
||||||
|
<!--弹窗-->
|
||||||
|
<LeftDialog v-model="showDialog" :modeType="type" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, nextTick } from 'vue'
|
||||||
|
import { sessionStore } from '@/utils/store'
|
||||||
|
import PDF from '@/components/PdfJs/index.vue'
|
||||||
|
import LeftDialog from './left-dialog.vue'
|
||||||
|
|
||||||
|
const props = defineProps(['curNode', 'type'])
|
||||||
|
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const onClick = () => {
|
||||||
|
if (props.type == 1) return
|
||||||
|
showDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载PDF
|
||||||
|
const pdfUrl = ref('')
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick()
|
||||||
|
let data = sessionStore.get('subject.curBook')
|
||||||
|
let fileurl = data.fileurl
|
||||||
|
if(props.type == 1){
|
||||||
|
fileurl = `${data.edustage}-${data.edusubject}-课标.txt`
|
||||||
|
}
|
||||||
|
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container-left-page {
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
.container-left-header {
|
||||||
|
height: 45px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 5px 0 0 0;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
.icon-xiangxia {
|
||||||
|
margin-left: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-left-pdf {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,26 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-pdf">
|
|
||||||
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, nextTick } from 'vue';
|
|
||||||
import PDF from '@/components/PdfJs/index.vue'
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
|
|
||||||
const pdfUrl = ref('')
|
|
||||||
|
|
||||||
onMounted(async () =>{
|
|
||||||
await nextTick()
|
|
||||||
const { fileurl } = sessionStore.get('subject.curBook')
|
|
||||||
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt','.pdf')
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container-pdf{
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,505 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="read-container">
|
|
||||||
|
|
||||||
<el-scrollbar height="100%">
|
|
||||||
<div class="template-list">
|
|
||||||
<el-row v-for="(item, index) in childTempList">
|
|
||||||
<el-col :span="24">
|
|
||||||
<div class="template-item" v-loading="item.loading">
|
|
||||||
<div class="item-header">
|
|
||||||
<div>
|
|
||||||
<span class="blue">#</span>{{ item.name }}
|
|
||||||
</div>
|
|
||||||
<el-popover placement="bottom-end" trigger="hover" popper-class="template-custom-popover">
|
|
||||||
<template #reference>
|
|
||||||
<el-button link type="primary">
|
|
||||||
<i class="iconfont icon-shenglvehao"></i></el-button>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<el-button type="primary" link @click="editKeyWord(item)">编辑</el-button>
|
|
||||||
<el-button type="primary" link @click="removeItem(item)">移除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
<div class="item-text">
|
|
||||||
{{ item.prompt }}
|
|
||||||
</div>
|
|
||||||
<div class="item-text text-answer" v-if="item.answer">
|
|
||||||
<div class="item-icon">
|
|
||||||
<i class="iconfont icon-ai"></i>
|
|
||||||
</div>
|
|
||||||
<div class="item-answer" v-html="item.answer"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ai-btn" v-if="item.answer">
|
|
||||||
<el-button type="primary" link @click="againResult(index, item)">
|
|
||||||
<i class="iconfont icon-ai1"></i>
|
|
||||||
重新研读
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" link @click="onAdjust(index, item)">
|
|
||||||
<i class="iconfont icon-duihua"></i>
|
|
||||||
AI对话调整
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" link @click="onEdit(index, item)">
|
|
||||||
<i class="iconfont icon-bianji1"></i>
|
|
||||||
手动编辑结果
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-empty v-if="!childTempList.length" description="暂无模板数据" />
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
<!--编辑结果-->
|
|
||||||
<EditDialog v-model="isEdit" :item="editItem" @saveEdit="saveEdit" />
|
|
||||||
<!--AI 对话调整-->
|
|
||||||
<AdjustDialog v-model="isAdjust" :modeType="modeType" :item="editItem" @saveAdjust="saveAdjust" />
|
|
||||||
<!--编辑提示词-->
|
|
||||||
<keywordDialog v-model="isEditKeyWord" :isAdd="isAdd" :item="keywordItem" :tempId="tempId" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue';
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
import EditDialog from './edit-dialog.vue'
|
|
||||||
import AdjustDialog from './adjust-dialog.vue'
|
|
||||||
import keywordDialog from './keyword-dialog.vue';
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import { tempSave, completion, modelList, removeChildTemp, tempResult } from '@/api/mode/index'
|
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
tempId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
modeType: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
emitter.on('onGetChild', () =>{
|
|
||||||
getChildTemplate()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取子模板
|
|
||||||
const tempLoading = ref(false)
|
|
||||||
const childTempList = ref([])
|
|
||||||
const getChildTemplate = () => {
|
|
||||||
|
|
||||||
tempLoading.value = true
|
|
||||||
modelList({ model: props.modeType, type: 2, parentId: props.tempId }).then(res => {
|
|
||||||
childTempList.value = res.rows
|
|
||||||
getTempResult()
|
|
||||||
}).finally(() => {
|
|
||||||
tempLoading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 查询模板结果
|
|
||||||
const getTempResult = () => {
|
|
||||||
tempResult({ mainModelId: props.tempId }).then(res => {
|
|
||||||
let rows = res.rows
|
|
||||||
childTempList.value.forEach(item => {
|
|
||||||
rows.forEach(el => {
|
|
||||||
if (item.id == el.modelId) {
|
|
||||||
item.answer = el.content
|
|
||||||
item.reultId = el.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEdit = ref(false)
|
|
||||||
watch(() => props.tempId, (newVal) => {
|
|
||||||
if (newVal) {
|
|
||||||
// isEdit.value = true
|
|
||||||
getChildTemplate()
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// 获取会话ID
|
|
||||||
const params = reactive(
|
|
||||||
{
|
|
||||||
prompt: '',
|
|
||||||
dataset_id: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const curNode = reactive({})
|
|
||||||
const getConversation = () => {
|
|
||||||
|
|
||||||
getCompletion()
|
|
||||||
}
|
|
||||||
// 大模型对话
|
|
||||||
const getCompletion = async () => {
|
|
||||||
for (let item of childTempList.value) {
|
|
||||||
try {
|
|
||||||
item.loading = true
|
|
||||||
params.prompt = `根据${curNode.edustage}${curNode.edusubject}课标,提炼出${item.name}`
|
|
||||||
const { data } = await completion(params)
|
|
||||||
let answer = data.answer
|
|
||||||
item.oldAnswer = answer
|
|
||||||
item.answer = getResult(answer);
|
|
||||||
onSaveTemp(item)
|
|
||||||
} finally {
|
|
||||||
item.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存模板
|
|
||||||
const onSaveTemp = (item) => {
|
|
||||||
const data = {
|
|
||||||
mainModelId: props.tempId,
|
|
||||||
modelId: item.id,
|
|
||||||
examDocld: '',
|
|
||||||
content: item.oldAnswer
|
|
||||||
}
|
|
||||||
tempSave(data).then(res => {
|
|
||||||
console.log(res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 重新研读
|
|
||||||
const againResult = async (index, item) => {
|
|
||||||
try {
|
|
||||||
childTempList.value[index].loading = true
|
|
||||||
params.prompt = `根据${curNode.edustage}${curNode.edusubject}课标,提炼出${item.name}`
|
|
||||||
const { data } = await completion(params)
|
|
||||||
let answer = data.answer
|
|
||||||
childTempList.value[index].oldAnswer = answer
|
|
||||||
childTempList.value[index].answer = getResult(answer);
|
|
||||||
} finally {
|
|
||||||
childTempList.value[index].loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 分析获取课标对话结果
|
|
||||||
let getResult = (text) => {
|
|
||||||
text = text.replace(/^\n\n(.*?)\n\n$/s, '<div>$1</div>');
|
|
||||||
text = text.replace(/^\n(.*?)\n$/s, '<p>$1</p>');
|
|
||||||
text = text.replace(/\*\*(.*?)\*\*/g, "<div class='text-tit'>$1</div>");
|
|
||||||
text = text.replace(/(\d+\..*?)\n/g, "<div class='text-num'>$1</div>\n");
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑
|
|
||||||
const curIndex = ref(-1)
|
|
||||||
const editItem = reactive({})
|
|
||||||
const onEdit = (index, item) => {
|
|
||||||
curIndex.value = index
|
|
||||||
Object.assign(editItem, item)
|
|
||||||
isEdit.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存编辑
|
|
||||||
const saveEdit = (data) => {
|
|
||||||
// childTempList.value[curIndex.value].oldAnswer = data
|
|
||||||
// let answer = getResult(data);
|
|
||||||
// childTempList.value[curIndex.value].answer = answer
|
|
||||||
getChildTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAdjust = ref(false)
|
|
||||||
const onAdjust = (index, item) => {
|
|
||||||
curIndex.value = index
|
|
||||||
Object.assign(editItem, item)
|
|
||||||
isAdjust.value = true
|
|
||||||
}
|
|
||||||
const saveAdjust = (item) => {
|
|
||||||
childTempList.value[curIndex.value].oldAnswer = item
|
|
||||||
let answer = getResult(item);
|
|
||||||
childTempList.value[curIndex.value].answer = answer
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑提示词
|
|
||||||
const keywordItem = reactive({})
|
|
||||||
const isEditKeyWord = ref(false)
|
|
||||||
const isAdd = ref(true)
|
|
||||||
const editKeyWord = (item) => {
|
|
||||||
console.log(item)
|
|
||||||
isAdd.value = false
|
|
||||||
isEditKeyWord.value = true
|
|
||||||
Object.assign(keywordItem, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除子模板
|
|
||||||
const removeItem = async (item) => {
|
|
||||||
if (item.ex3 != '1') {
|
|
||||||
ElMessageBox.confirm(
|
|
||||||
'确认是否移除?',
|
|
||||||
'提示',
|
|
||||||
{
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
}
|
|
||||||
).then(() => {
|
|
||||||
console.log(item)
|
|
||||||
removeChildTemp(item.id).then(res => {
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
getChildTemplate()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
isAdd.value = false
|
|
||||||
Object.assign(keywordItem, item)
|
|
||||||
isEditKeyWord.value = true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// const { msg } = await removeChildTemp(item.id)
|
|
||||||
// ElMessage.success(msg)
|
|
||||||
// getChildTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
let text = props.modeType == 1 ? '课标' : props.modeType == 2 ? '教材' : '考试'
|
|
||||||
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
|
||||||
|
|
||||||
})
|
|
||||||
// 解绑
|
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('onGetChild')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
getConversation
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.read-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px 0;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.el-scrollbar {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-dropdown-link {
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
.el-icon--right {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.add-btn {
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.icon-jiahao {
|
|
||||||
margin-right: 3px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-con {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.template-list {
|
|
||||||
|
|
||||||
.template-item {
|
|
||||||
background: #fff;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
.item-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #000;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.blue {
|
|
||||||
font-size: 22px;
|
|
||||||
color: #409eff;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-text {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
color: #606266;
|
|
||||||
|
|
||||||
.item-icon {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
text-align: center;
|
|
||||||
background: #F6F6F6;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 10px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-answer {
|
|
||||||
flex-direction: column;
|
|
||||||
padding-top: 5px;
|
|
||||||
|
|
||||||
:deep(.text-tit) {
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.text-num) {
|
|
||||||
padding-left: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-answer {
|
|
||||||
color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-btn {
|
|
||||||
margin-top: 10px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-button) {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-ai1 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.template-item-result {
|
|
||||||
background: #DDEAFD !important;
|
|
||||||
|
|
||||||
.result-item-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3D3D3D;
|
|
||||||
|
|
||||||
.icon-xiaoxi {
|
|
||||||
color: #5881D5;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-icon-btn {
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #cfe0fa
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
margin-right: 3px;
|
|
||||||
color: #3498fc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
height: 1px;
|
|
||||||
background: #D8D8D8;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.other-msg {
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.other-user {
|
|
||||||
align-items: center;
|
|
||||||
color: #BA4B0F;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
.icon-touxiang {
|
|
||||||
color: #BA4B0F;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.other-text {
|
|
||||||
color: #191919;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shenglvehao {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-popover) {
|
|
||||||
min-width: 50px;
|
|
||||||
width: 50px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pl-25 {
|
|
||||||
padding-left: 25px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<style>
|
|
||||||
.template-custom-popover {
|
|
||||||
width: 110px !important;
|
|
||||||
min-width: 110px !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
<template>
|
||||||
|
<div class="container-right-page flex">
|
||||||
|
<!--头部-->
|
||||||
|
<div class="container-right-header flex">
|
||||||
|
<el-dropdown @command="changeTemplate" :hide-on-click="false">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
{{ curTemplate.name }}
|
||||||
|
<i class="iconfont icon-xiangxia"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.name
|
||||||
|
}}</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<div>
|
||||||
|
<el-button type="danger" link @click="removeItem(curTemplate, false)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link @click="onAdd">
|
||||||
|
<i class="iconfont icon-jiahao"></i>
|
||||||
|
添加提示词
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :disabled="!(childTempList.length)" @click="getCompletion">一键研读</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--List-->
|
||||||
|
<div class="container-right-list">
|
||||||
|
<template v-for="(item, index) in childTempList">
|
||||||
|
<div class="template-item" v-loading="item.loading">
|
||||||
|
<div class="item-header">
|
||||||
|
<div>
|
||||||
|
<span class="blue">#</span>{{ item.name }}
|
||||||
|
</div>
|
||||||
|
<el-popover placement="bottom-end" trigger="hover" popper-class="template-custom-popover">
|
||||||
|
<template #reference>
|
||||||
|
<el-button link type="primary">
|
||||||
|
<i class="iconfont icon-shenglvehao"></i></el-button>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<el-button type="primary" link @click="editKeyWord(item)">编辑</el-button>
|
||||||
|
<el-button type="primary" link @click="removeItem(item, true)">移除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div class="item-text">
|
||||||
|
{{ item.prompt }}
|
||||||
|
</div>
|
||||||
|
<div class="item-text text-answer" v-if="item.answer">
|
||||||
|
<div class="item-icon">
|
||||||
|
<i class="iconfont icon-ai"></i>
|
||||||
|
</div>
|
||||||
|
<div class="item-answer" v-html="item.answer"></div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-btn" v-if="item.answer">
|
||||||
|
<el-button type="primary" link @click="againResult(index, item)">
|
||||||
|
<i class="iconfont icon-ai1"></i>
|
||||||
|
重新研读
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link @click="onAdjust(index, item)">
|
||||||
|
<i class="iconfont icon-duihua"></i>
|
||||||
|
AI对话调整
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" link @click="onEdit(index, item)">
|
||||||
|
<i class="iconfont icon-bianji1"></i>
|
||||||
|
手动编辑结果
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<el-empty v-if="!childTempList.length" description="暂无模板数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--编辑结果-->
|
||||||
|
<EditDialog v-model="isEdit" :item="editItem" />
|
||||||
|
<!--AI 对话调整-->
|
||||||
|
<AdjustDialog v-model="isAdjust" :type="type" :item="editItem" />
|
||||||
|
<!--添加、编辑提示词-->
|
||||||
|
<keywordDialog v-model="isWordDialog" :isAdd="isAdd" :item="editItem" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { tempSave, completion, modelList, removeChildTemp, tempResult, editTempResult } from '@/api/mode/index'
|
||||||
|
import { sessionStore } from '@/utils/store'
|
||||||
|
import keywordDialog from './keyword-dialog.vue';
|
||||||
|
import AdjustDialog from './adjust-dialog.vue'
|
||||||
|
import EditDialog from './edit-dialog.vue'
|
||||||
|
import emitter from '@/utils/mitt';
|
||||||
|
import { dataSetJson } from '@/utils/comm.js'
|
||||||
|
|
||||||
|
const props = defineProps(['curNode', 'type'])
|
||||||
|
|
||||||
|
/*****************提示词相关****************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isWordDialog : 提示词弹窗
|
||||||
|
* isAdd : 是否添加 默认false
|
||||||
|
* editItem: 当前操作的item (添加的时候不需要这个)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const isWordDialog = ref(false)
|
||||||
|
const isAdd = ref(false)
|
||||||
|
const editItem = reactive({})
|
||||||
|
const onAdd = () => {
|
||||||
|
isAdd.value = true
|
||||||
|
Object.assign(editItem, curTemplate)
|
||||||
|
isWordDialog.value = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const editKeyWord = (item, val) => {
|
||||||
|
/**
|
||||||
|
* isAdd: 字模板中的移除 为编辑 头部删除 添加提示词为新增
|
||||||
|
*/
|
||||||
|
isAdd.value = val
|
||||||
|
Object.assign(editItem, item)
|
||||||
|
isWordDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************模板相关**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* curTemplate :当前模板
|
||||||
|
* templateList :主模板List
|
||||||
|
* childTempList : 子模板List
|
||||||
|
* getTemplateList() : 获取主模板
|
||||||
|
* getChildTemplate() :获取子模板
|
||||||
|
*/
|
||||||
|
const tempLoading = ref(false)
|
||||||
|
const curTemplate = reactive({ name: '', id: '' })
|
||||||
|
const templateList = ref([])
|
||||||
|
const childTempList = ref([])
|
||||||
|
const getTemplateList = () => {
|
||||||
|
modelList({ model: props.type, type: 1, pageNum: 1, pageSize: 10000 }).then(res => {
|
||||||
|
templateList.value = res.rows
|
||||||
|
Object.assign(curTemplate, res.rows[0]);
|
||||||
|
getChildTemplate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getChildTemplate = () => {
|
||||||
|
tempLoading.value = true
|
||||||
|
modelList({ model: props.type, type: 2, parentId: curTemplate.id }).then(res => {
|
||||||
|
childTempList.value = res.rows
|
||||||
|
|
||||||
|
getTempResult()
|
||||||
|
}).finally(() => {
|
||||||
|
tempLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 查询模板结果
|
||||||
|
const getTempResult = () => {
|
||||||
|
tempResult({ mainModelId: curTemplate.id, pageNum: 1, pageSize: 10000 }).then(res => {
|
||||||
|
let rows = res.rows
|
||||||
|
childTempList.value.forEach(item => {
|
||||||
|
rows.forEach(el => {
|
||||||
|
if (item.id == el.modelId) {
|
||||||
|
item.answer = el.content
|
||||||
|
item.resultId = el.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模板切换
|
||||||
|
const changeTemplate = (val) => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'切换模板将清除当前研读结果?',
|
||||||
|
'提示',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
Object.assign(curTemplate, val);
|
||||||
|
getChildTemplate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除模板
|
||||||
|
const removeItem = async (item, isChild) => {
|
||||||
|
/**
|
||||||
|
* item: 当前操作的模板
|
||||||
|
* isChild: 子模板中的移除为 true
|
||||||
|
*/
|
||||||
|
if (item.ex3 != '1') {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'确认是否移除?',
|
||||||
|
'提示',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
removeChildTemp(item.id).then(res => {
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
if(isChild){
|
||||||
|
// 获取子模板
|
||||||
|
getChildTemplate()
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 获取主模板
|
||||||
|
getTemplateList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
editKeyWord(item,!isChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Ai对话调整
|
||||||
|
const curIndex = ref(-1)
|
||||||
|
const isAdjust = ref(false)
|
||||||
|
const onAdjust = (index, item) => {
|
||||||
|
curIndex.value = index
|
||||||
|
Object.assign(editItem, item)
|
||||||
|
isAdjust.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑结果
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const onEdit = (index, item) => {
|
||||||
|
curIndex.value = index
|
||||||
|
Object.assign(editItem, item)
|
||||||
|
isEdit.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const modeType = ref('课标')
|
||||||
|
watch(() => props.type, (newVal) => {
|
||||||
|
if (newVal == 1){
|
||||||
|
modeType.value = '课标'
|
||||||
|
}
|
||||||
|
if (newVal == 2){
|
||||||
|
modeType.value = '教材'
|
||||||
|
}
|
||||||
|
if (newVal == 2){
|
||||||
|
modeType.value = '考试'
|
||||||
|
}
|
||||||
|
|
||||||
|
}, { immediate: false })
|
||||||
|
|
||||||
|
|
||||||
|
// 重新研读
|
||||||
|
const params = reactive(
|
||||||
|
{
|
||||||
|
prompt: '',
|
||||||
|
dataset_id: ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 重新研读
|
||||||
|
const againResult = async (index, item) => {
|
||||||
|
try {
|
||||||
|
childTempList.value[index].loading = true
|
||||||
|
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value} 对${curNode.itemtitle}进行教学分析`
|
||||||
|
const { data } = await completion(params)
|
||||||
|
let answer = data.answer
|
||||||
|
childTempList.value[index].oldAnswer = answer
|
||||||
|
childTempList.value[index].answer = getResult(answer);
|
||||||
|
onEditSave(item)
|
||||||
|
} finally {
|
||||||
|
childTempList.value[index].loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 一键研读
|
||||||
|
const getCompletion = async () => {
|
||||||
|
for (let item of childTempList.value) {
|
||||||
|
try {
|
||||||
|
item.loading = true
|
||||||
|
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value} 对${curNode.itemtitle}进行教学分析`
|
||||||
|
const { data } = await completion(params)
|
||||||
|
let answer = data.answer
|
||||||
|
item.oldAnswer = answer
|
||||||
|
item.answer = getResult(answer);
|
||||||
|
onSaveTemp(item)
|
||||||
|
} finally {
|
||||||
|
item.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存 重新研读后的结果
|
||||||
|
const onEditSave = async (item) =>{
|
||||||
|
const { res } = await editTempResult({id: item.resultId, content: item.oldAnswer})
|
||||||
|
ElMessage.success(res)
|
||||||
|
getChildTemplate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 保存模板
|
||||||
|
const onSaveTemp = (item) => {
|
||||||
|
const data = {
|
||||||
|
mainModelId: curTemplate.id,
|
||||||
|
modelId: item.id,
|
||||||
|
examDocld: '',
|
||||||
|
content: item.oldAnswer
|
||||||
|
}
|
||||||
|
tempSave(data).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 替换分析结果
|
||||||
|
emitter.on('saveAdjust', (item) => {
|
||||||
|
childTempList.value[curIndex.value].oldAnswer = item
|
||||||
|
let answer = getResult(item);
|
||||||
|
childTempList.value[curIndex.value].answer = answer
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分析获取课标对话结果
|
||||||
|
let getResult = (text) => {
|
||||||
|
text = text.replace(/^\n\n(.*?)\n\n$/s, '<div>$1</div>');
|
||||||
|
text = text.replace(/^\n(.*?)\n$/s, '<p>$1</p>');
|
||||||
|
text = text.replace(/\*\*(.*?)\*\*/g, "<div class='text-tit'>$1</div>");
|
||||||
|
text = text.replace(/(\d+\..*?)\n/g, "<div class='text-num'>$1</div>\n");
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// 操作之后获取字模板
|
||||||
|
emitter.on('onGetChild', () => {
|
||||||
|
getChildTemplate()
|
||||||
|
})
|
||||||
|
// 操作之后获取主模板
|
||||||
|
emitter.on('onGetMain', () => {
|
||||||
|
getTemplateList()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const curNode = reactive({})
|
||||||
|
onMounted(() => {
|
||||||
|
getTemplateList()
|
||||||
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
Object.assign(curNode, data);
|
||||||
|
let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
|
||||||
|
params.dataset_id = dataSetJson[jsonKey]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 解绑
|
||||||
|
onUnmounted(() => {
|
||||||
|
emitter.off('onGetChild');
|
||||||
|
emitter.off('saveAdjust');
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container-right-page {
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.container-right-header {
|
||||||
|
height: 45px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: #fff;
|
||||||
|
border-left: solid #EBF0F4 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 15px;
|
||||||
|
border-radius: 0 5px 0 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-right-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 5px 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.template-item {
|
||||||
|
background: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.blue {
|
||||||
|
font-size: 22px;
|
||||||
|
color: #409eff;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-text {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: left;
|
||||||
|
color: #606266;
|
||||||
|
|
||||||
|
.item-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
background: #F6F6F6;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-answer {
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 5px;
|
||||||
|
|
||||||
|
:deep(.text-tit) {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.text-num) {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-answer {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-btn {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button) {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ai1 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.template-custom-popover {
|
||||||
|
width: 110px !important;
|
||||||
|
min-width: 110px !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,26 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-template flex">
|
<div class="page-template flex">
|
||||||
<!--头部-->
|
<el-row>
|
||||||
<Header :type="type" @changeTemp="changeTemp" @onRead="onRead"/>
|
|
||||||
<el-row :gutter="20" class="tempalte-main">
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<!--左侧pdf-->
|
<Left :curNode="curNode" :type="type" />
|
||||||
<Pdf />
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<!--右侧模板研读-->
|
<Right :curNode="curNode" :type="type" />
|
||||||
<Result ref="resultRef" :modeType="type" :tempId="tempId"/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import Header from './container/header.vue'
|
import { sessionStore } from '@/utils/store'
|
||||||
import Pdf from './container/pdf.vue'
|
import Left from './container/left.vue'
|
||||||
import Result from './container/result.vue'
|
import Right from './container/right.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
|
@ -29,22 +24,24 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const curNode = reactive({})
|
||||||
|
onMounted(() =>{
|
||||||
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
Object.assign(curNode, data);
|
||||||
|
})
|
||||||
|
|
||||||
const resultRef = ref()
|
|
||||||
const tempId = ref('')
|
|
||||||
const changeTemp = (id) =>{
|
|
||||||
tempId.value = id
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRead = () =>{
|
|
||||||
resultRef.value.getConversation()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-template {
|
.page-template {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
.el-row{
|
||||||
|
height: 100%;
|
||||||
|
.el-col{
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
.tempalte-main {
|
.tempalte-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="blockBox">
|
<div class="blockBox">
|
||||||
<el-button @click="currentType = 'selection'"><el-image src="/src/assets/images/mouse-pointer.png"
|
<el-button @click="currentType = 'selection'"><el-image src="../../../src/assets/images/mouse-pointer.png"
|
||||||
style="width: 14px; height: 14px; color: silver" /></el-button>
|
style="width: 14px; height: 14px; color: silver" /></el-button>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="type == 'design'">
|
<template v-if="type == 'design'">
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
<!-- 边框样式 -->
|
<!-- 边框样式 -->
|
||||||
<div class="blockBox">
|
<div class="blockBox">
|
||||||
<el-dropdown @command="updateStyle('lineDash', $event)" placement="top">
|
<el-dropdown @command="updateStyle('lineDash', $event)" placement="top">
|
||||||
<el-button><el-image src="/src/assets/images/borderstyle.png"
|
<el-button><el-image src="../../../src/assets/images/borderstyle.png"
|
||||||
style="width: 14px; height: 14px"></el-image></el-button>
|
style="width: 14px; height: 14px"></el-image></el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
<!-- 边框粗细 -->
|
<!-- 边框粗细 -->
|
||||||
<div class="blockBox">
|
<div class="blockBox">
|
||||||
<el-dropdown @command="updateStyle('lineWidth', $event)" placement="top">
|
<el-dropdown @command="updateStyle('lineWidth', $event)" placement="top">
|
||||||
<el-button><el-image src="/src/assets/images/borderwidth.png"
|
<el-button><el-image src="../../../src/assets/images/borderwidth.png"
|
||||||
style="width: 14px; height: 14px"></el-image></el-button>
|
style="width: 14px; height: 14px"></el-image></el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
|
|
|
@ -59,14 +59,14 @@ export const processList = (row, aloneOption=false) => {
|
||||||
for(; j<workDescArr.length; j++){
|
for(; j<workDescArr.length; j++){
|
||||||
const char = String.fromCharCode(65+j);
|
const char = String.fromCharCode(65+j);
|
||||||
if (aloneOption) {
|
if (aloneOption) {
|
||||||
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
|
tmp += `<div class="format-work-desc" style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(j%2 == 0){
|
if(j%2 == 0){
|
||||||
tmp += `<div style='width:100%;display:flex;'>`;
|
tmp += `<div style='width:100%;display:flex;'>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp += `<div style='padding-left:10px;width:50%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`;
|
tmp += `<div class="format-work-desc" style='padding-left:10px;width:50%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`;
|
||||||
if(j%2 == 1){
|
if(j%2 == 1){
|
||||||
tmp += '</div>';
|
tmp += '</div>';
|
||||||
}
|
}
|
||||||
|
@ -153,14 +153,14 @@ export const processList = (row, aloneOption=false) => {
|
||||||
for(; j<optionsArr.length; j++){
|
for(; j<optionsArr.length; j++){
|
||||||
const char = String.fromCharCode(65+j);
|
const char = String.fromCharCode(65+j);
|
||||||
if (aloneOption) {
|
if (aloneOption) {
|
||||||
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${optionsArr[j]}</div>`;
|
tmp += `<div class="format-work-desc" style='width:100%;display:flex;padding: 2px 0'>${char}.${optionsArr[j]}</div>`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(j%2 == 0){
|
if(j%2 == 0){
|
||||||
tmp += `<div style='width:100%;display:flex;'>`;
|
tmp += `<div style='width:100%;display:flex;'>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${optionsArr[j]}</div>`;
|
tmp += `<div class="format-work-desc" style='padding-left: 10px; width: 50%'>${char}.${optionsArr[j]}</div>`;
|
||||||
if(j%2 == 1){
|
if(j%2 == 1){
|
||||||
tmp += '</div>';
|
tmp += '</div>';
|
||||||
}
|
}
|
||||||
|
@ -280,14 +280,14 @@ export const processList = (row, aloneOption=false) => {
|
||||||
for(; j<workDescArr.length; j++){
|
for(; j<workDescArr.length; j++){
|
||||||
const char = String.fromCharCode(65+j);
|
const char = String.fromCharCode(65+j);
|
||||||
if (aloneOption) {
|
if (aloneOption) {
|
||||||
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
|
tmp += `<div class="format-work-desc" style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(j%2 == 0){
|
if(j%2 == 0){
|
||||||
tmp += `<div style='width:100%;display:flex;'>`;
|
tmp += `<div style='width:100%;display:flex;'>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${workDescArr[j]}</div>`;
|
tmp += `<div class="format-work-desc" style='padding-left: 10px; width: 50%'>${char}.${workDescArr[j]}</div>`;
|
||||||
if(j%2 == 1){
|
if(j%2 == 1){
|
||||||
tmp += '</div>';
|
tmp += '</div>';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
// import tab from './tab'
|
// import tab from './tab'
|
||||||
// import auth from './auth'
|
// import auth from './auth'
|
||||||
// import cache from './cache'
|
// import cache from './cache'
|
||||||
import modal from './modal'
|
|
||||||
// import download from './download'
|
// import download from './download'
|
||||||
|
import modal from './modal'
|
||||||
|
// import './vue3-menus'
|
||||||
|
import vue3Menus from './vue3-menus'
|
||||||
|
// console.log('vue3Menus', defineComponent)
|
||||||
export default function installPlugins(app){
|
export default function installPlugins(app){
|
||||||
// 页签操作
|
// 页签操作
|
||||||
// app.config.globalProperties.$tab = tab
|
// app.config.globalProperties.$tab = tab
|
||||||
|
@ -15,4 +17,6 @@ export default function installPlugins(app){
|
||||||
app.config.globalProperties.$modal = modal
|
app.config.globalProperties.$modal = modal
|
||||||
// 下载文件
|
// 下载文件
|
||||||
// app.config.globalProperties.$download = download
|
// app.config.globalProperties.$download = download
|
||||||
|
// 右键菜单 支持组件|指令|函数 三种方式使用
|
||||||
|
vue3Menus(app)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import _ from 'lodash'
|
||||||
// import { diff } from 'jsondiffpatch'
|
// import { diff } from 'jsondiffpatch'
|
||||||
// const Remote = isNode?require('@electron/remote'):{} // 远程模块
|
// const Remote = isNode?require('@electron/remote'):{} // 远程模块
|
||||||
|
|
||||||
const exArrs = ['subject'] // 不需要同步key-排除
|
const exArrs = ['subject','env','curr'] // 不需要同步key-排除
|
||||||
|
|
||||||
export function shareStorePlugin({store}) {
|
export function shareStorePlugin({store}) {
|
||||||
store.$subscribe((mutation, state) => { // 自动同步
|
store.$subscribe((mutation, state) => { // 自动同步
|
||||||
|
|
|
@ -0,0 +1,437 @@
|
||||||
|
import { defineComponent, getCurrentInstance, ref, computed, watch, nextTick, createVNode, Teleport, Transition, render } from 'vue';
|
||||||
|
|
||||||
|
function styleInject(css, ref) {
|
||||||
|
if ( ref === void 0 ) ref = {};
|
||||||
|
var insertAt = ref.insertAt;
|
||||||
|
|
||||||
|
if (!css || typeof document === 'undefined') { return; }
|
||||||
|
|
||||||
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
|
var style = document.createElement('style');
|
||||||
|
style.type = 'text/css';
|
||||||
|
|
||||||
|
if (insertAt === 'top') {
|
||||||
|
if (head.firstChild) {
|
||||||
|
head.insertBefore(style, head.firstChild);
|
||||||
|
} else {
|
||||||
|
head.appendChild(style);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.styleSheet) {
|
||||||
|
style.styleSheet.cssText = css;
|
||||||
|
} else {
|
||||||
|
style.appendChild(document.createTextNode(css));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var css_248z = ".menus-fade-enter-active,\n.menus-fade-leave-active {\n transition: opacity 0.2s ease-in-out;\n}\n.menus-fade-enter-from,\n.menus-fade-leave-to {\n opacity: 0;\n}\n\n.v3-menus {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n\n.v3-menus-body {\n display: block;\n}\n\n.v3-menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n\n.v3-menus-divided {\n border-bottom-color: #ebeef5;\n}\n\n.v3-menus-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n\n.v3-menus-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-available {\n color: #606266;\n cursor: pointer;\n}\n\n.v3-menus-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n\n.v3-menus-active {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-tip {\n font-size: 9px;\n color: #999;\n}\n";
|
||||||
|
styleInject(css_248z);
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
menus: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
menusClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
itemClass: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
minWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 'none'
|
||||||
|
},
|
||||||
|
maxWidth: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 'none'
|
||||||
|
},
|
||||||
|
zIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
default: 'right'
|
||||||
|
},
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
type: [Object, Function, Array, Boolean, String],
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const vue3MenusComponent = defineComponent({
|
||||||
|
name: 'vue3-menus',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props,
|
||||||
|
|
||||||
|
setup(props, {
|
||||||
|
slots,
|
||||||
|
attrs
|
||||||
|
}) {
|
||||||
|
const windowWidth = globalThis.document.documentElement.clientWidth;
|
||||||
|
const windowHeight = globalThis.document.documentElement.clientHeight;
|
||||||
|
const {
|
||||||
|
proxy
|
||||||
|
} = getCurrentInstance();
|
||||||
|
const show = ref(props.open);
|
||||||
|
const self = {};
|
||||||
|
const menusRef = ref(null);
|
||||||
|
const activeIndex = ref(-1);
|
||||||
|
const left = ref(0);
|
||||||
|
const top = ref(0);
|
||||||
|
let direction = props.direction;
|
||||||
|
const hasIcon = computed(() => {
|
||||||
|
for (let index = 0; index < props.menus.length; index++) {
|
||||||
|
const menu = props.menus[index];
|
||||||
|
|
||||||
|
if (menu.icon !== undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const position = computed(() => {
|
||||||
|
return {
|
||||||
|
x: props.event.clientX,
|
||||||
|
y: props.event.clientY,
|
||||||
|
width: props.event.width || 0,
|
||||||
|
height: props.event.height || 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const style = computed(() => {
|
||||||
|
return {
|
||||||
|
left: `${left.value}px`,
|
||||||
|
top: `${top.value}px`,
|
||||||
|
minWidth: `${props.minWidth}px`,
|
||||||
|
maxWidth: props.maxWidth == 'none' ? props.maxWidth : `${props.maxWidth}px`,
|
||||||
|
zIndex: props.zIndex
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function leftOpen(menusWidth) {
|
||||||
|
left.value = position.value.x - menusWidth;
|
||||||
|
direction = 'left';
|
||||||
|
|
||||||
|
if (left.value < 0) {
|
||||||
|
direction = 'right';
|
||||||
|
|
||||||
|
if (position.value.width === 0 || position.value.width === undefined) {
|
||||||
|
left.value = 0;
|
||||||
|
} else {
|
||||||
|
left.value = position.value.x + position.value.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rightOpen(windowWidth, menusWidth) {
|
||||||
|
left.value = position.value.x + position.value.width;
|
||||||
|
direction = 'right';
|
||||||
|
|
||||||
|
if (left.value + menusWidth > windowWidth) {
|
||||||
|
direction = 'left';
|
||||||
|
|
||||||
|
if (position.value.width === 0 || position.value.width === undefined) {
|
||||||
|
left.value = windowWidth - menusWidth;
|
||||||
|
} else {
|
||||||
|
left.value = position.value.x - menusWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEvent() {
|
||||||
|
activeIndex.value = -1;
|
||||||
|
show.value = false;
|
||||||
|
|
||||||
|
if (self && self.instance) {
|
||||||
|
self.instance.close.bind(self.instance)();
|
||||||
|
self.instance = null;
|
||||||
|
self.index = null; // @ts-ignore
|
||||||
|
|
||||||
|
if (proxy.closeAll) {
|
||||||
|
// @ts-ignore
|
||||||
|
proxy.closeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.open, newVal => show.value = newVal);
|
||||||
|
watch(show, newVal => {
|
||||||
|
if (newVal) {
|
||||||
|
nextTick(() => {
|
||||||
|
const menusWidth = menusRef.value.offsetWidth;
|
||||||
|
const menusHeight = menusRef.value.offsetHeight;
|
||||||
|
|
||||||
|
if (direction === 'left') {
|
||||||
|
leftOpen(menusWidth);
|
||||||
|
} else {
|
||||||
|
rightOpen(windowWidth, menusWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
top.value = position.value.y;
|
||||||
|
|
||||||
|
if (position.value.y + menusHeight > windowHeight) {
|
||||||
|
if (position.value.height === 0 || position.value.height === undefined) {
|
||||||
|
top.value = position.value.y - menusHeight;
|
||||||
|
} else {
|
||||||
|
top.value = windowHeight - menusHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
globalThis.document.addEventListener('click', closeEvent);
|
||||||
|
globalThis.document.addEventListener('contextmenu', closeEvent);
|
||||||
|
globalThis.document.addEventListener('wheel', closeEvent);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
activeIndex.value = -1;
|
||||||
|
globalThis.document.removeEventListener('click', closeEvent);
|
||||||
|
globalThis.document.removeEventListener('contextmenu', closeEvent);
|
||||||
|
globalThis.document.removeEventListener('wheel', closeEvent);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
immediate: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function mouseEnter(event, menu, index) {
|
||||||
|
event.preventDefault();
|
||||||
|
activeIndex.value = index;
|
||||||
|
|
||||||
|
if (!menu || menu.disabled || menu.hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.instance) {
|
||||||
|
if (self.index === index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.instance.close.bind(self.instance)();
|
||||||
|
self.instance = null;
|
||||||
|
self.index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!menu.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enter = menu.enter && typeof menu.enter === 'function' ? menu.enter : null;
|
||||||
|
|
||||||
|
if (enter) {
|
||||||
|
const val = enter(menu, props.args);
|
||||||
|
|
||||||
|
if (val === false || val === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuItemClientRect = event.target.getBoundingClientRect();
|
||||||
|
const vm = createVNode(vue3MenusComponent, { ...props,
|
||||||
|
menus: menu.children,
|
||||||
|
direction: direction,
|
||||||
|
event: {
|
||||||
|
clientX: menuItemClientRect.x + 3,
|
||||||
|
clientY: menuItemClientRect.y - 8,
|
||||||
|
width: menuItemClientRect.width - 2 * 3,
|
||||||
|
height: menuItemClientRect.width
|
||||||
|
},
|
||||||
|
open: false
|
||||||
|
}, slots);
|
||||||
|
const container = globalThis.document.createElement('div');
|
||||||
|
render(vm, container);
|
||||||
|
vm.component.props.open = true; // @ts-ignore
|
||||||
|
|
||||||
|
vm.component.proxy.close = close;
|
||||||
|
self.instance = vm.component.proxy;
|
||||||
|
self.instance.container = container;
|
||||||
|
self.instance.props = vm.component.props;
|
||||||
|
self.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseClick(event, menu) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!menu || menu.disabled) {
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const click = menu.click && typeof menu.click === 'function' ? menu.click : null;
|
||||||
|
|
||||||
|
if (click) {
|
||||||
|
const val = click(menu, props.args);
|
||||||
|
|
||||||
|
if (val === false || val === null) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu.children) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
this.show = false;
|
||||||
|
|
||||||
|
if (this.self && this.self.instance) {
|
||||||
|
this.self.instance.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
render(null, this.container);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
default: $default,
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
suffix
|
||||||
|
} = slots;
|
||||||
|
const $class = ['v3-menus', attrs.class, props.menusClass];
|
||||||
|
return () => createVNode(Teleport, {
|
||||||
|
"to": 'body'
|
||||||
|
}, {
|
||||||
|
default: () => [createVNode(Transition, {
|
||||||
|
"name": 'menus-fade'
|
||||||
|
}, {
|
||||||
|
default: () => [!show.value ? null : createVNode("div", {
|
||||||
|
"ref": menusRef,
|
||||||
|
"class": $class,
|
||||||
|
"style": style.value,
|
||||||
|
"onWheel": e => e.preventDefault(),
|
||||||
|
"onContextmenu": e => e.preventDefault()
|
||||||
|
}, [createVNode("div", {
|
||||||
|
"class": 'v3-menus-body'
|
||||||
|
}, [props.menus.map((menu, index) => {
|
||||||
|
if (menu.hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($default) {
|
||||||
|
return createVNode("div", {
|
||||||
|
"onContextmenu": $event => mouseClick($event, menu),
|
||||||
|
"onClick": $event => mouseClick($event, menu),
|
||||||
|
"onMouseenter": $event => mouseEnter($event, menu, index)
|
||||||
|
}, [$default({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
})]);
|
||||||
|
} else {
|
||||||
|
let $class = [props.itemClass, 'v3-menus-item', menu.disabled ? 'v3-menus-disabled' : 'v3-menus-available'];
|
||||||
|
$class = $class.concat([menu.divided ? 'v3-menus-divided' : null, !menu.disabled && activeIndex.value === index ? 'v3-menus-active' : null]);
|
||||||
|
return createVNode("div", {
|
||||||
|
"style": menu.style,
|
||||||
|
"class": $class.join(' '),
|
||||||
|
"onClick": $event => mouseClick($event, menu),
|
||||||
|
"onMouseenter": $event => mouseEnter($event, menu, index),
|
||||||
|
"onContextmenu": $event => mouseClick($event, menu)
|
||||||
|
}, [hasIcon.value ? createVNode("div", {
|
||||||
|
"class": 'v3-menus-icon '
|
||||||
|
}, [icon ? icon({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
}) : createVNode("span", {
|
||||||
|
"innerHTML": menu.icon
|
||||||
|
}, null)]) : null, label ? createVNode("span", {
|
||||||
|
"class": 'v3-menus-label'
|
||||||
|
}, [label({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
})]) : createVNode("span", {
|
||||||
|
"class": 'v3-menus-label'
|
||||||
|
}, [menu.label]), menu.children || menu.tip ? createVNode("div", {
|
||||||
|
"class": 'v3-menus-suffix'
|
||||||
|
}, [suffix ? suffix({
|
||||||
|
menu,
|
||||||
|
activeIndex: activeIndex.value,
|
||||||
|
index
|
||||||
|
}) : menu.children ? '▶' : menu.tip ? createVNode("span", {
|
||||||
|
"class": 'v3-menus-tip'
|
||||||
|
}, [menu.tip]) : null]) : null]);
|
||||||
|
}
|
||||||
|
})])])]
|
||||||
|
})]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function mouseEvent(menus, args, event) {
|
||||||
|
let props = {};
|
||||||
|
if (Array.isArray(menus)) {
|
||||||
|
props = {
|
||||||
|
menus,
|
||||||
|
event,
|
||||||
|
args,
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
props = {
|
||||||
|
...menus,
|
||||||
|
args,
|
||||||
|
event,
|
||||||
|
open: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const vNode = createVNode(vue3MenusComponent, props);
|
||||||
|
const container = globalThis.document.createElement('div');
|
||||||
|
render(vNode, container);
|
||||||
|
vNode.component.props.open = true;
|
||||||
|
vNode.component.proxy.closeAll = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
render(null, container);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (props.prevent == undefined || props.prevent) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const directive = {
|
||||||
|
mounted(el, { value, arg }) {
|
||||||
|
const vnode = el.__vnode || {};
|
||||||
|
if (arg === undefined || arg === 'right') {
|
||||||
|
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
} else if (arg === 'left') {
|
||||||
|
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
} else if (arg === 'all') {
|
||||||
|
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props || {}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unmounted(el) {
|
||||||
|
el.removeEventListener("contextmenu", mouseEvent);
|
||||||
|
el.removeEventListener("click", mouseEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const install = function (app, options = {}) {
|
||||||
|
app.component(options.name || vue3MenusComponent.name, vue3MenusComponent);
|
||||||
|
app.directive('menus', directive);
|
||||||
|
app.config.globalProperties.$menusEvent = (event, menus, args) => mouseEvent(menus, args || {}, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const menusEvent = (event, menus, args) => mouseEvent(menus, args || {}, event);
|
||||||
|
|
||||||
|
function index (app) {
|
||||||
|
app.use(install);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { vue3MenusComponent as Vue3Menus, index as default, directive, menusEvent };
|
|
@ -1,16 +1,46 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { } from '@/api/classTask/index.js'
|
import { } from '@/api/classTask/index.js'
|
||||||
import { listClassmain } from '@/api/classManage/index'
|
import { listClassmain } from '@/api/classManage/index'
|
||||||
|
import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuestion/jyeoo"
|
||||||
|
|
||||||
const useClassTaskStore = defineStore('classTask',{
|
const useClassTaskStore = defineStore('classTask',{
|
||||||
state: () => ({
|
state: () => ({
|
||||||
classListIds: [],
|
classListIds: [],
|
||||||
|
entpCourseWorkTypeList: [
|
||||||
|
{value: 0, label: "不限"},
|
||||||
|
{value: 1, label: "单选题"},
|
||||||
|
{value: 2, label: "填空题"},
|
||||||
|
{value: 3, label: "多选题"},
|
||||||
|
{value: 4, label: "判断题"},
|
||||||
|
{value: 5, label: "主观题"},
|
||||||
|
{value: 6, label: "复合题"},
|
||||||
|
], // 习题查询条件 - 题型
|
||||||
|
entpCourseWorkGroupList: [{
|
||||||
|
Key: -1,
|
||||||
|
Value: '不限',
|
||||||
|
}, {
|
||||||
|
Key: 1,
|
||||||
|
Value: '真题',
|
||||||
|
}, {
|
||||||
|
Key: 0,
|
||||||
|
Value: '非真题',
|
||||||
|
}
|
||||||
|
], // 习题查询条件 - 题源
|
||||||
|
entpCourseWorkYearList: [
|
||||||
|
{label: '不限', value: '-1'},
|
||||||
|
{label: '2024', value: '2024'},
|
||||||
|
{label: '2023', value: '2023'},
|
||||||
|
{label: '2022', value: '2022'},
|
||||||
|
{label: '2021', value: '2021'},
|
||||||
|
{label: '2020', value: '2020'},
|
||||||
|
], // 习题查询条件 - 年份
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
listClassmain(params) {
|
listClassmain(params) {
|
||||||
// 获取班级列表
|
// 获取班级列表
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
listClassmain(params)
|
const education = params.edustage + params.edusubject;
|
||||||
|
listClassmain(education)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.classListIds = res.rows&&res.rows.map((item) => item.id)
|
this.classListIds = res.rows&&res.rows.map((item) => item.id)
|
||||||
resolve(res)
|
resolve(res)
|
||||||
|
@ -20,9 +50,58 @@ const useClassTaskStore = defineStore('classTask',{
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 根据学科和学段获取菁优网对应得年份、题源、题型
|
||||||
|
initJYInfo(params){
|
||||||
|
const education = params.edustage + params.edusubject;
|
||||||
|
Promise.all([getJYYear(), getJYSO(education), getJYCT(education)])
|
||||||
|
.then(results => {
|
||||||
|
console.log('更新第三方题源+题型succ:', results);
|
||||||
|
this.entpCourseWorkYearList = results[0];
|
||||||
|
this.entpCourseWorkTypeList = results[2];
|
||||||
|
this.entpCourseWorkGroupList = results[1];
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('更新第三方题源+题型err:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
persist: true
|
persist: true
|
||||||
})
|
})
|
||||||
export default useClassTaskStore
|
export default useClassTaskStore
|
||||||
|
|
||||||
|
const getJYYear = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
JYApiListOriginYear()
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getJYSO = (params) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
JYApiListSO(params)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getJYCT = (params) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
JYApiListCT(params)
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import { sessionStore } from '@/utils/store'
|
||||||
|
|
||||||
// 默认数据
|
// 默认数据
|
||||||
const defData = sessionStore.store || {}
|
const defData = sessionStore.store || {}
|
||||||
const exArrs = ['subject']
|
const exArrs = ['subject','env','curr']
|
||||||
exArrs.forEach(k => Object.keys(defData).includes(k) && (delete defData[k]))
|
exArrs.forEach(k => Object.keys(defData).includes(k) && (delete defData[k]))
|
||||||
|
|
||||||
// 延时
|
// 延时
|
||||||
|
|
|
@ -21,6 +21,29 @@ export function getFiles() {
|
||||||
}
|
}
|
||||||
return new Promise(cb)
|
return new Promise(cb)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* base64 转 blob
|
||||||
|
*/
|
||||||
|
export function base64ToBlob(base64Data) {
|
||||||
|
const contentType = base64Data?.match(/^data:([^;]+);base64,/)?.[1]||'image/png'
|
||||||
|
// 去除Base64编码数据中的前缀(如"data:image/png;base64,")
|
||||||
|
const byteCharacters = atob(base64Data.split(',')[1]);
|
||||||
|
const byteArrays = [];
|
||||||
|
for (let i = 0; i < byteCharacters.length; i++) {
|
||||||
|
byteArrays.push(byteCharacters.charCodeAt(i));
|
||||||
|
}
|
||||||
|
return new Blob([new Uint8Array(byteArrays)], { type: contentType });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayBufferToBlob(arrayBuffer, contentType) {
|
||||||
|
return new Blob([new Uint8Array(arrayBuffer)], { type: contentType });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function blobToFile(blob, fileName, contentType) {
|
||||||
|
fileName = fileName || 'file'
|
||||||
|
contentType = contentType || blob.type ||'image/png'
|
||||||
|
return new File([blob], fileName, { type: contentType });
|
||||||
|
}
|
||||||
|
|
||||||
// ============= 数学公式--相关 ===================
|
// ============= 数学公式--相关 ===================
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getJYPath } from "@/api/education/entpcoursework";
|
||||||
|
|
||||||
const JY_TOKEN = 'CA82641DA86072DEFD39E287335E035FDA6AEEC0549B58F54F4408734C8683FFAF0585CFA3B25091E588A03A65C66A80F5FF613F539D600954007A35DFFBFDC3C7BB982771C5E13F0918642CFD7596CE3718F06E5579238D92EC809AC6F4C82A9FE4B0E232A67DD3594D4DAC1C219CCBC4A7A093344446107EB11DC317526D0594249DEBBD82B740C794CF5A7065E1982B7779AF16AD25D7';
|
const JY_TOKEN = 'CA82641DA86072DEFD39E287335E035FDA6AEEC0549B58F54F4408734C8683FFAF0585CFA3B25091E588A03A65C66A80F5FF613F539D600954007A35DFFBFDC3C7BB982771C5E13F0918642CFD7596CE3718F06E5579238D92EC809AC6F4C82A9FE4B0E232A67DD3594D4DAC1C219CCBC4A7A093344446107EB11DC317526D0594249DEBBD82B740C794CF5A7065E1982B7779AF16AD25D7';
|
||||||
const JY_SUBJECT = [
|
const JY_SUBJECT = [
|
||||||
{id: 10, subject: 'math3', name: '小学数学'},
|
{id: 10, subject: 'math3', name: '小学数学'},
|
||||||
|
@ -26,8 +28,31 @@ const JY_SUBJECT = [
|
||||||
{id: 39, subject: 'history2', name: '高中历史'},
|
{id: 39, subject: 'history2', name: '高中历史'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc: 获取年份
|
||||||
|
* @return: {*}
|
||||||
|
*/
|
||||||
|
export const JYApiListOriginYear = async () => {
|
||||||
|
const arrYear = [{label: '不限', value: '-1'}];
|
||||||
|
let i = 0;
|
||||||
|
for( ; i < 5; i++) {
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
const s ={
|
||||||
|
label: `${year - i}`,
|
||||||
|
value: `${year - i}`,
|
||||||
|
}
|
||||||
|
arrYear.push(s);
|
||||||
|
};
|
||||||
|
//arrYear.push({label: '更早', value: '0'})
|
||||||
|
return arrYear;
|
||||||
|
}
|
||||||
|
|
||||||
export const JYApiListCT = async (_this, name = '高中历史') => {
|
/**
|
||||||
|
* @desc: 根据学科+学段获取菁优网-题型
|
||||||
|
* @return: {*}
|
||||||
|
* @param {*} name 学科+学科
|
||||||
|
*/
|
||||||
|
export const JYApiListCT = async (name = '高中历史') => {
|
||||||
if (name === '初中政治') {
|
if (name === '初中政治') {
|
||||||
name = '初中道德与法治';
|
name = '初中道德与法治';
|
||||||
}
|
}
|
||||||
|
@ -35,7 +60,8 @@ export const JYApiListCT = async (_this, name = '高中历史') => {
|
||||||
if(obj.length < 1) {
|
if(obj.length < 1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const res = await _this.$requestGetJYW(`/${obj[0].subject}/common`, {
|
getJYPath
|
||||||
|
const res = await getJYPath(`/${obj[0].subject}/common`, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Token ${JY_TOKEN}`
|
authorization: `Token ${JY_TOKEN}`
|
||||||
},
|
},
|
||||||
|
@ -63,22 +89,13 @@ export const JYApiListCT = async (_this, name = '高中历史') => {
|
||||||
return arrCT;
|
return arrCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const JYApiListOriginYear = () => {
|
|
||||||
const arrYear = [{label: '不限', value: '-1'}];
|
|
||||||
let i = 0;
|
|
||||||
for( ; i < 10; i++) {
|
|
||||||
const year = new Date().getFullYear();
|
|
||||||
const s ={
|
|
||||||
label: `${year - i}`,
|
|
||||||
value: `${year - i}`,
|
|
||||||
}
|
|
||||||
arrYear.push(s);
|
|
||||||
};
|
|
||||||
//arrYear.push({label: '更早', value: '0'})
|
|
||||||
return arrYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const JYApiListSO = async (_this, name = '高中历史') => {
|
/**
|
||||||
|
* @desc: 根据学科+学段获取菁优网-题源
|
||||||
|
* @return: {*}
|
||||||
|
* @param {*} name 学科+学科
|
||||||
|
*/
|
||||||
|
export const JYApiListSO = async (name = '高中历史') => {
|
||||||
if (name === '初中政治') {
|
if (name === '初中政治') {
|
||||||
name = '初中道德与法治';
|
name = '初中道德与法治';
|
||||||
}
|
}
|
||||||
|
@ -87,7 +104,7 @@ export const JYApiListSO = async (_this, name = '高中历史') => {
|
||||||
if(obj.length < 1) {
|
if(obj.length < 1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const res = await _this.$requestGetJYW(`/${obj[0].subject}/common`, {
|
const res = await getJYPath(`/${obj[0].subject}/common`, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Token ${JY_TOKEN}`
|
authorization: `Token ${JY_TOKEN}`
|
||||||
},
|
},
|
||||||
|
@ -103,8 +120,12 @@ export const JYApiListSO = async (_this, name = '高中历史') => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
export const JYApiListPoint = async (_this, name = '高中历史') => {
|
* @desc: 根据学科+学段获取菁优网-知识点
|
||||||
|
* @return: {*}
|
||||||
|
* @param {*} name 学科+学科
|
||||||
|
*/
|
||||||
|
export const JYApiListPoint = async (name = '高中历史') => {
|
||||||
if (name === '初中政治') {
|
if (name === '初中政治') {
|
||||||
name = '初中道德与法治';
|
name = '初中道德与法治';
|
||||||
}
|
}
|
||||||
|
@ -113,7 +134,7 @@ export const JYApiListPoint = async (_this, name = '高中历史') => {
|
||||||
if(obj.length < 1) {
|
if(obj.length < 1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const res = await _this.$requestGetJYW(`/${obj[0].subject}/point`, {
|
const res = await getJYPath(`/${obj[0].subject}/point`, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Token ${JY_TOKEN}`
|
authorization: `Token ${JY_TOKEN}`
|
||||||
},
|
},
|
||||||
|
@ -126,10 +147,12 @@ export const JYApiListPoint = async (_this, name = '高中历史') => {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc: 获取菁优网的版本内容
|
* @desc: 根据查询条件获取菁优网-教材版本
|
||||||
* @return: {*}
|
* @return: {*}
|
||||||
|
* @param {*} query {}
|
||||||
|
* @param {*} hasPoints
|
||||||
*/
|
*/
|
||||||
export const JYApiListVersion = async (_this, query, hasPoints=true) => {
|
export const JYApiListVersion = async (query, hasPoints=true) => {
|
||||||
const listVersion = {
|
const listVersion = {
|
||||||
status: 0,
|
status: 0,
|
||||||
msg: '',
|
msg: '',
|
||||||
|
@ -148,7 +171,7 @@ export const JYApiListVersion = async (_this, query, hasPoints=true) => {
|
||||||
listVersion.msg = `[${name}]未找到对应菁优网教材版本, 请检查学段或学科是否匹配!`;
|
listVersion.msg = `[${name}]未找到对应菁优网教材版本, 请检查学段或学科是否匹配!`;
|
||||||
return listVersion;
|
return listVersion;
|
||||||
}
|
}
|
||||||
const JYBook = await _this.$requestGetJYW(`/${result[0].subject}/book2`, {
|
const JYBook = await getJYPath(`/${result[0].subject}/book2`, {
|
||||||
headers: {
|
headers: {
|
||||||
// JYToken仅占位, 实际后续已未使用该token
|
// JYToken仅占位, 实际后续已未使用该token
|
||||||
authorization: `Token ${JY_TOKEN}`
|
authorization: `Token ${JY_TOKEN}`
|
||||||
|
|
|
@ -97,4 +97,34 @@ const getProgress = async (id) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { createOutline, getBackGround, createPPT, getProgress, createByOutline };
|
const getBackGroundV2 = async () => {
|
||||||
|
try {
|
||||||
|
const response = await req("/api/aipptV2/themeListV2", "GET");
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("请求失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const createPPTV2 = async (data) => {
|
||||||
|
try {
|
||||||
|
const response = await req("/api/aipptV2/createV2", "POST", data);
|
||||||
|
console.log("createOutline response:", response);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("请求失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getProgressV2 = async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await req(`/api/aipptV2/progressV2?sid=${id}`, "GET");
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("请求失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { createOutline, getBackGround, createPPT, getProgress, getBackGroundV2, createPPTV2, getProgressV2, createByOutline };
|
||||||
|
|
|
@ -117,4 +117,8 @@ export const coursewareTypeList = [
|
||||||
label:'素材',
|
label:'素材',
|
||||||
value:6
|
value:6
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label:'视频',
|
||||||
|
value:12
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -216,7 +216,11 @@ export const createWindow = async (type, data) => {
|
||||||
win.maximize();
|
win.maximize();
|
||||||
// win.setFullScreen(true) // 设置窗口为全屏
|
// win.setFullScreen(true) // 设置窗口为全屏
|
||||||
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') win.webContents.openDevTools() // 打开调试工具
|
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') win.webContents.openDevTools() // 打开调试工具
|
||||||
eventHandles(type, win) // 事件监听处理
|
let events = {} // 事件处理函数对象
|
||||||
|
Object.keys(data)
|
||||||
|
.filter(k => typeof data[k] === 'function')
|
||||||
|
.forEach(k => events[k] = data[k])
|
||||||
|
eventHandles(type, win, events) // 事件监听处理
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -286,17 +290,20 @@ export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) {
|
||||||
* 窗口创建-事件处理
|
* 窗口创建-事件处理
|
||||||
* @param {*} type 事件类型
|
* @param {*} type 事件类型
|
||||||
* @param {*} win 窗口对象
|
* @param {*} win 窗口对象
|
||||||
|
* @param {*} events 事件对象
|
||||||
*/
|
*/
|
||||||
const eventHandles = (type, win) => {
|
const eventHandles = (type, win, events) => {
|
||||||
const toolState = useToolState() // 获取store状态
|
const toolState = useToolState() // 获取store状态
|
||||||
const winAll = Remote.BrowserWindow.getAllWindows()
|
const winAll = Remote.BrowserWindow.getAllWindows()
|
||||||
const mainWin = winAll.find(o => o.type == 'main') // 主窗口对象
|
const mainWin = winAll.find(o => o.type == 'main') // 主窗口对象
|
||||||
// 公共方法
|
// 公共方法
|
||||||
const publicMethods = ({onClosed}={}) => {
|
const publicMethods = ({onClosed, closed, close}={}) => {
|
||||||
// 监听主窗口-关闭事件
|
// 监听主窗口-关闭事件
|
||||||
mainWin.once('close', () => {winPdf=null;win.destroy();})
|
mainWin.once('close', () => {winPdf=null;win.destroy();})
|
||||||
win.on('closed', () => {
|
win.on('closed', () => {
|
||||||
if(onClosed) onClosed() // 自定义关闭事件
|
if(!!onClosed) onClosed() // 自定义关闭事件
|
||||||
|
if(!!closed) closed() // 自定义关闭事件
|
||||||
|
if(!!close) close() // 自定义关闭事件
|
||||||
win = null
|
win = null
|
||||||
wins_tool = null
|
wins_tool = null
|
||||||
winChild=null
|
winChild=null
|
||||||
|
@ -385,8 +392,7 @@ const eventHandles = (type, win) => {
|
||||||
win&&win.destroy()
|
win&&win.destroy()
|
||||||
});
|
});
|
||||||
const on = {
|
const on = {
|
||||||
onClosed: () => {
|
...events
|
||||||
}
|
|
||||||
}
|
}
|
||||||
publicMethods(on) // 加载公共方法
|
publicMethods(on) // 加载公共方法
|
||||||
break
|
break
|
||||||
|
|
|
@ -416,6 +416,7 @@ const queryPushRecords = (row) => {
|
||||||
// 获取已推送历史
|
// 获取已推送历史
|
||||||
console.log(row,'查看该行推送历史')
|
console.log(row,'查看该行推送历史')
|
||||||
pushRecordsOpen.value = true;
|
pushRecordsOpen.value = true;
|
||||||
|
pushRecordsList.value = [];
|
||||||
pushRecordsLoading.value = true;
|
pushRecordsLoading.value = true;
|
||||||
homeworklist({
|
homeworklist({
|
||||||
entpcourseid: entpcourseid.value,
|
entpcourseid: entpcourseid.value,
|
||||||
|
@ -875,21 +876,6 @@ watch(() => courseObj.node, (newVal,oldVal) => {
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--
|
|
||||||
<style>
|
|
||||||
.el-table .hidden-row {
|
|
||||||
display: none !important;
|
|
||||||
/* color: #ccc !important; */
|
|
||||||
}
|
|
||||||
.el-table .father-row {
|
|
||||||
--el-table-tr-bg-color: #fff;
|
|
||||||
}
|
|
||||||
.el-table .son-row {
|
|
||||||
--el-table-tr-bg-color: #f0f0f08a;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-classTaskAssign {
|
.page-classTaskAssign {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|
|
@ -294,8 +294,8 @@ import FileUpload from "@/components/FileUpload/index.vue";
|
||||||
import whiteboard from '@/components/whiteboard/whiteboard.vue'
|
import whiteboard from '@/components/whiteboard/whiteboard.vue'
|
||||||
import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue'
|
import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue'
|
||||||
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
|
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
|
||||||
import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuestion/jyeoo"
|
|
||||||
|
import useClassTaskStore from '@/store/modules/classTask'
|
||||||
import {throttle,debounce } from '@/utils/comm'
|
import {throttle,debounce } from '@/utils/comm'
|
||||||
import { useToolState } from '@/store/modules/tool'
|
import { useToolState } from '@/store/modules/tool'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
@ -303,6 +303,11 @@ const userStore = useUserStore().user
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const toolStore = useToolState()
|
const toolStore = useToolState()
|
||||||
|
const {
|
||||||
|
entpCourseWorkTypeList,
|
||||||
|
entpCourseWorkGroupList,
|
||||||
|
entpCourseWorkYearList
|
||||||
|
} = useClassTaskStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bookobj: {
|
bookobj: {
|
||||||
|
@ -326,39 +331,11 @@ const props = defineProps({
|
||||||
const prevReadMsgDialogRef = ref(null);// 预览框ref
|
const prevReadMsgDialogRef = ref(null);// 预览框ref
|
||||||
|
|
||||||
const classWorkFormRef = ref(null);
|
const classWorkFormRef = ref(null);
|
||||||
const entpCourseWorkTypeList = ref([
|
|
||||||
{value: 0, label: "不限"},
|
|
||||||
{value: 1, label: "单选题"},
|
|
||||||
{value: 2, label: "填空题"},
|
|
||||||
{value: 3, label: "多选题"},
|
|
||||||
{value: 4, label: "判断题"},
|
|
||||||
{value: 5, label: "主观题"},
|
|
||||||
{value: 6, label: "复合题"},
|
|
||||||
]); // 习题查询条件 - 题型
|
|
||||||
|
|
||||||
const entpCourseWorkGroupList = ref([{
|
|
||||||
Key: -1,
|
|
||||||
Value: '不限',
|
|
||||||
}, {
|
|
||||||
Key: 1,
|
|
||||||
Value: '真题',
|
|
||||||
}, {
|
|
||||||
Key: 0,
|
|
||||||
Value: '非真题',
|
|
||||||
}]); // 习题查询条件 - 题源
|
|
||||||
|
|
||||||
const entpCourseWorkPointList = ref([
|
const entpCourseWorkPointList = ref([
|
||||||
{label: '不限', value: []},
|
{label: '不限', value: []},
|
||||||
]); // 习题查询条件 - 知识点
|
]); // 习题查询条件 - 知识点
|
||||||
const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
|
const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
|
||||||
const entpCourseWorkYearList =ref([
|
|
||||||
{label: '不限', value: '-1'},
|
|
||||||
{label: '2024', value: '2024'},
|
|
||||||
{label: '2023', value: '2023'},
|
|
||||||
{label: '2022', value: '2022'},
|
|
||||||
{label: '2021', value: '2021'},
|
|
||||||
{label: '2020', value: '2020'},
|
|
||||||
]); // 习题查询条件 - 年份
|
|
||||||
|
|
||||||
|
|
||||||
const paginationParams = reactive({
|
const paginationParams = reactive({
|
||||||
|
@ -1006,18 +983,6 @@ const initPageParams = () => {
|
||||||
|
|
||||||
|
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
// 获取当前学科下的试题题型
|
|
||||||
const name = userStore.edustage + userStore.edusubject;
|
|
||||||
const jyCT = await JYApiListCT(proxy, name);
|
|
||||||
if (jyCT.length == 0) {
|
|
||||||
ElMessage.error('获取题型失败!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entpCourseWorkTypeList.value = jyCT;
|
|
||||||
// 获取当前学科下的试题年份
|
|
||||||
entpCourseWorkYearList.value = JYApiListOriginYear();
|
|
||||||
entpCourseWorkGroupList.value = await JYApiListSO(proxy, name);
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// const refreshData = () => {
|
// const refreshData = () => {
|
||||||
|
|
|
@ -29,11 +29,11 @@ import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const emit = defineEmits(['itemClick'])
|
const emit = defineEmits(['itemClick'])
|
||||||
const items = shallowRef([
|
const items = shallowRef([
|
||||||
{ title: '自主搜题', description: '上千万高质量习题资源,历届考试真题,每道题均有习题解析', icon: '#icon-soutibao-',type:'default' },
|
{ title: '自主搜题', description: '上千万高质量习题资源,历届考试真题,每道题均有习题解析', icon: '#icon-soutibao-',type:'primary' },
|
||||||
{ title: '校本题库', description: '本校公共题库资源。', icon: '#icon-soutibao-',type:'default' },
|
{ title: '校本题库', description: '本校公共题库资源。', icon: '#icon-soutibao-',type:'primary' },
|
||||||
{ title: '个人题库', description: '老师上传维护自己的个人题库。', icon: '#icon-soutibao-',type:'default' },
|
{ title: '个人题库', description: '老师上传维护自己的个人题库。', icon: '#icon-soutibao-',type:'primary' },
|
||||||
{ title: '智能推荐', description: '通过对学生的薄弱知识点分析,推送不同难度的习题进行强化训练。', icon: '#icon-tubiao_wuxing-',type:'default' },
|
{ title: '智能推荐', description: '通过对学生的薄弱知识点分析,推送不同难度的习题进行强化训练。', icon: '#icon-tubiao_wuxing-',type:'primary' },
|
||||||
{ title: '课堂展示', description: '通过课堂白板绘制作业,提升学生的创作思维能力。', icon: '#icon-huaban',type:'primary' },
|
{ title: '课堂展示', description: '通过课堂白板绘制作业,提升学生的创作思维能力。', icon: '#icon-huaban',type:'danger' },
|
||||||
{ title: '常规作业', description: '推送pdf、视频、音频、图片,学生可以拍照上传。', icon: '#icon-zhaoxiangji',type:'danger' },
|
{ title: '常规作业', description: '推送pdf、视频、音频、图片,学生可以拍照上传。', icon: '#icon-zhaoxiangji',type:'danger' },
|
||||||
{ title: 'AI设计作业', description: '通过AI助手,根据课标、教材、考试等分析结果,智能创建作业。', icon: '#icon-jiqiren_o',type:'danger' },
|
{ title: 'AI设计作业', description: '通过AI助手,根据课标、教材、考试等分析结果,智能创建作业。', icon: '#icon-jiqiren_o',type:'danger' },
|
||||||
{ title: '习题上传', description: '自己上传个人题库。', icon: '#icon-shangchuan',type:'danger' },
|
{ title: '习题上传', description: '自己上传个人题库。', icon: '#icon-shangchuan',type:'danger' },
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
<div v-if="classWorkForm.worktype == '习题训练'" class="pageRight-list">
|
<div v-if="classWorkForm.worktype == '习题训练'" class="pageRight-list">
|
||||||
<div :style="{height: '100%', 'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}">
|
<div :style="{height: '100%', 'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}">
|
||||||
<template v-for="(item,index) in classWorkForm.quizlist" :key="item.id">
|
<template v-for="(item,index) in classWorkForm.quizlist" :key="item.id">
|
||||||
<div style="margin: 5px; background-color: white">
|
<div style="margin: 5px; background-color: white; text-align: left;">
|
||||||
<div v-html="item.titleFormat" style="padding: 15px 20px 5px 20px"></div>
|
<div v-html="item.titleFormat" style="padding: 15px 20px 5px 20px"></div>
|
||||||
<div style="display: flex;">
|
<div style="display: flex;">
|
||||||
<el-form-item label="分值">
|
<el-form-item label="分值">
|
||||||
|
@ -126,10 +126,10 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue'
|
import { onMounted, ref, watch, reactive, getCurrentInstance, nextTick } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus, Delete } from '@element-plus/icons-vue'
|
||||||
import { delClasswork } from '@/api/teaching/classwork'
|
import { delClasswork } from '@/api/teaching/classwork'
|
||||||
import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
|
import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
|
||||||
import { addClassworkReturnId } from '@/api/teaching/classwork'
|
import { addClassworkReturnId } from '@/api/teaching/classwork'
|
||||||
|
@ -144,10 +144,6 @@ import whiteboard from '@/components/whiteboard/whiteboard.vue'
|
||||||
import FileUpload from "@/components/FileUpload/index.vue";
|
import FileUpload from "@/components/FileUpload/index.vue";
|
||||||
import Right from './Right/index.vue'
|
import Right from './Right/index.vue'
|
||||||
|
|
||||||
import {
|
|
||||||
Delete
|
|
||||||
} from '@element-plus/icons-vue'
|
|
||||||
|
|
||||||
import SetHomework from '@/components/set-homework/index.vue'
|
import SetHomework from '@/components/set-homework/index.vue'
|
||||||
import { useGetHomework } from '@/hooks/useGetHomework'
|
import { useGetHomework } from '@/hooks/useGetHomework'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
|
|
|
@ -125,16 +125,26 @@ import { delEntpcoursework, updateEntpcoursework } from "@/api/education/entpCou
|
||||||
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
|
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
|
||||||
import QuesItem from "@/views/classTask/newClassTaskAssign/questionUpload/quesItem/index.vue";
|
import QuesItem from "@/views/classTask/newClassTaskAssign/questionUpload/quesItem/index.vue";
|
||||||
import { useHandleData } from "@/hooks/useHandleData";
|
import { useHandleData } from "@/hooks/useHandleData";
|
||||||
import { processList } from '@/hooks/useProcessList'
|
import { processList } from '@/hooks/useProcessList';
|
||||||
|
|
||||||
|
|
||||||
import { debounce } from '@/utils/comm'
|
import { debounce } from '@/utils/comm'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import useClassTaskStore from '@/store/modules/classTask'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
||||||
// 定义要发送的emit事件
|
// 定义要发送的emit事件
|
||||||
const emit = defineEmits(['addQuiz'])
|
const emit = defineEmits(['addQuiz'])
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const userStore = useUserStore().user
|
const userStore = useUserStore().user
|
||||||
|
const {
|
||||||
|
entpCourseWorkTypeList,
|
||||||
|
entpCourseWorkGroupList,
|
||||||
|
entpCourseWorkYearList
|
||||||
|
} = useClassTaskStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bookobj: {
|
bookobj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -142,37 +152,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const entpCourseWorkTypeList = ref([
|
|
||||||
{value: 0, label: "不限"},
|
|
||||||
{value: 1, label: "单选题"},
|
|
||||||
{value: 2, label: "填空题"},
|
|
||||||
{value: 3, label: "多选题"},
|
|
||||||
{value: 4, label: "判断题"},
|
|
||||||
{value: 5, label: "主观题"},
|
|
||||||
{value: 6, label: "复合题"},
|
|
||||||
]); // 习题查询条件 - 题型
|
|
||||||
|
|
||||||
const entpCourseWorkGroupList = ref([{
|
|
||||||
Key: -1,
|
|
||||||
Value: '不限',
|
|
||||||
}, {
|
|
||||||
Key: 1,
|
|
||||||
Value: '真题',
|
|
||||||
}, {
|
|
||||||
Key: 0,
|
|
||||||
Value: '非真题',
|
|
||||||
}]); // 习题查询条件 - 题源
|
|
||||||
|
|
||||||
const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
|
const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
|
||||||
const entpCourseWorkYearList =ref([
|
|
||||||
{label: '不限', value: '-1'},
|
|
||||||
{label: '2024', value: '2024'},
|
|
||||||
{label: '2023', value: '2023'},
|
|
||||||
{label: '2022', value: '2022'},
|
|
||||||
{label: '2021', value: '2021'},
|
|
||||||
{label: '2020', value: '2020'},
|
|
||||||
]); // 习题查询条件 - 年份
|
|
||||||
|
|
||||||
|
|
||||||
// 习题查询参数条件
|
// 习题查询参数条件
|
||||||
|
@ -220,7 +200,7 @@ const dlgImportSingle = reactive({
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
debounceQueryData(); // 查询习题列表
|
debounceQueryData(); // 查询习题列表
|
||||||
})
|
})
|
||||||
|
/** 前往习题上传页面 */
|
||||||
const goToQuestUpload = () => {
|
const goToQuestUpload = () => {
|
||||||
router.push({ path: '/model/questionUpload', query: { courseObj: JSON.stringify(props.bookobj) } });
|
router.push({ path: '/model/questionUpload', query: { courseObj: JSON.stringify(props.bookobj) } });
|
||||||
}
|
}
|
||||||
|
@ -336,7 +316,7 @@ const handleQueryFromEntpCourseWork= async (queryType) => {
|
||||||
} else if (clueres.rows[i].cluetag == 'mapview') {
|
} else if (clueres.rows[i].cluetag == 'mapview') {
|
||||||
clueres.rows[i].worktype = '学科定位';
|
clueres.rows[i].worktype = '学科定位';
|
||||||
}
|
}
|
||||||
console.log("clueres.rows[i].childlist",clueres.rows[i].childlist);
|
//console.log("clueres.rows[i].childlist",clueres.rows[i].childlist);
|
||||||
if (clueres.rows[i].childlist != '') {
|
if (clueres.rows[i].childlist != '') {
|
||||||
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
|
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
|
||||||
for (var j=0; j<clueres.rows[i].childArray.length; j++) {
|
for (var j=0; j<clueres.rows[i].childArray.length; j++) {
|
||||||
|
@ -488,6 +468,10 @@ watch(() => props.bookobj.levelSecondId, (newVal, oldVal) => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 100px);
|
height: calc(100% - 100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.format-work-desc > :is(div):first-child){
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
// :deep(.el-dialog .question-dialog){
|
// :deep(.el-dialog .question-dialog){
|
||||||
// height: 80vh !important;
|
// height: 80vh !important;
|
||||||
|
|
|
@ -99,6 +99,8 @@ const router = useRouter()
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const curLessionList = ref([]);
|
||||||
const activeParams = reactive({
|
const activeParams = reactive({
|
||||||
version: {},
|
version: {},
|
||||||
lession: {},
|
lession: {},
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<el-form ref="questFormRef" :model="questForm" :rules="MainRules" label-width="80px">
|
<el-form ref="questFormRef" :model="questForm" :rules="MainRules" label-width="80px">
|
||||||
<el-form-item label="题型" prop="worktype">
|
<el-form-item label="题型" prop="worktype">
|
||||||
<el-select v-model="questForm.worktype" placeholder="请选择题型" style="width:20%" :disabled="questForm.id==0?false:true">
|
<el-select v-model="questForm.worktype" placeholder="请选择题型" style="width:20%" :disabled="questForm.id==0?false:true">
|
||||||
<el-option v-for="item in fromOptions.type" :key="item.Key" :label="item.Value" :value="item.Value"></el-option>
|
<el-option v-for="item in fromOptions.type" :key="item.value" :label="item.label" :value="item.label"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-tag v-if="questForm.worktype=='填空题'" type="danger" style=" margin-left: 10px ">温馨提示:填空题题目的填空位置,下划线请连续输入3-10个 _
|
<el-tag v-if="questForm.worktype=='填空题'" type="danger" style=" margin-left: 10px ">温馨提示:填空题题目的填空位置,下划线请连续输入3-10个 _
|
||||||
符号。eg:今天___好日子。</el-tag>
|
符号。eg:今天___好日子。</el-tag>
|
||||||
|
@ -405,15 +405,24 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ElMessage, ElNotification } from 'element-plus'
|
import { ElMessage, ElNotification } from 'element-plus'
|
||||||
import { Search } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue'
|
import { onMounted, ref, watch, reactive, getCurrentInstance,nextTick } from 'vue'
|
||||||
|
|
||||||
import { getBindlist } from '@/api/education/knowledgePoint'
|
import { getBindlist, listKnowlegepointFormat } from '@/api/education/knowledgePoint'
|
||||||
|
import { listEvaluation } from '@/api/subject'
|
||||||
import { listEntpcoursework, getEntpcoursework, delEntpcoursework, addEntpcoursework, updateEntpcoursework, uploadEntpcourseworkFile } from "@/api/education/entpCourseWork";
|
import { listEntpcoursework, getEntpcoursework, delEntpcoursework, addEntpcoursework, updateEntpcoursework, uploadEntpcourseworkFile } from "@/api/education/entpCourseWork";
|
||||||
import { isJson } from "@/hooks/useProcessList";
|
import { isJson } from "@/hooks/useProcessList";
|
||||||
|
|
||||||
import Tinymce from "@/components/tinymce/tinymce.vue"; // 富文本编辑器
|
import Tinymce from "@/components/tinymce/tinymce.vue"; // 富文本编辑器
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import useClassTaskStore from '@/store/modules/classTask'
|
||||||
|
|
||||||
const userStore = useUserStore().user
|
const userStore = useUserStore().user
|
||||||
|
const {
|
||||||
|
entpCourseWorkTypeList,
|
||||||
|
entpCourseWorkGroupList,
|
||||||
|
entpCourseWorkYearList
|
||||||
|
} = useClassTaskStore();
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
// 定义要发送的emit事件
|
// 定义要发送的emit事件
|
||||||
const emit = defineEmits(['submit-exam-single-callback','cancel-exam-single-callback','cropper-exam-form-item'])
|
const emit = defineEmits(['submit-exam-single-callback','cancel-exam-single-callback','cropper-exam-form-item'])
|
||||||
|
@ -449,12 +458,13 @@ const fromOptions = reactive({
|
||||||
//题型
|
//题型
|
||||||
type: [
|
type: [
|
||||||
//{"Key": 0, "Value": "不限"},
|
//{"Key": 0, "Value": "不限"},
|
||||||
{"Key": 1, "Value": "单选题"},
|
{value: 0, label: "不限"},
|
||||||
{"Key": 4, "Value": "多选题"},
|
{value: 1, label: "单选题"},
|
||||||
{"Key": 2, "Value": "填空题"},
|
{value: 2, label: "填空题"},
|
||||||
{"Key": 5, "Value": "判断题"},
|
{value: 3, label: "多选题"},
|
||||||
{"Key": 6, "Value": "主观题"},
|
{value: 4, label: "判断题"},
|
||||||
{"Key": 3, "Value": "复合题"},
|
{value: 5, label: "主观题"},
|
||||||
|
{value: 6, label: "复合题"},
|
||||||
//{"Key": 4, "Value": "辨析评析题"},
|
//{"Key": 4, "Value": "辨析评析题"},
|
||||||
],
|
],
|
||||||
// 复合题型
|
// 复合题型
|
||||||
|
@ -475,7 +485,7 @@ const questForm = reactive({
|
||||||
id: 0,
|
id: 0,
|
||||||
title: '',
|
title: '',
|
||||||
worktype: '单选题',
|
worktype: '单选题',
|
||||||
worktagYear: '2024',
|
worktagYear: 2024,
|
||||||
//worktagArea: '',
|
//worktagArea: '',
|
||||||
worktag: '',
|
worktag: '',
|
||||||
workgroup: 0,
|
workgroup: 0,
|
||||||
|
@ -601,8 +611,8 @@ onMounted(() => {
|
||||||
lessionId: lessionid,
|
lessionId: lessionid,
|
||||||
fileAlias: '单题上传',
|
fileAlias: '单题上传',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取最新年份
|
// 获取最新年份
|
||||||
// yearList =
|
|
||||||
for(var i = 0; i < 15; i++) {
|
for(var i = 0; i < 15; i++) {
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
const s ={
|
const s ={
|
||||||
|
@ -611,10 +621,23 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
yearList.value.push(s)
|
yearList.value.push(s)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 更新第三方题型、题源
|
||||||
|
if (entpCourseWorkTypeList.length>0) {
|
||||||
|
const flagDict = ['单选题', '多选题', '判断题', '填空题'];
|
||||||
|
fromOptions.type = entpCourseWorkTypeList.filter(item => flagDict.includes(item.label));
|
||||||
|
}
|
||||||
|
if (entpCourseWorkGroupList.length>0) {
|
||||||
|
fromOptions.flag = entpCourseWorkGroupList;
|
||||||
|
}
|
||||||
|
// if (entpCourseWorkYearList.length>0) {
|
||||||
|
// yearList.value = entpCourseWorkYearList;
|
||||||
|
// }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const checkBoxChange=(v)=>{
|
const checkBoxChange=(v)=>{
|
||||||
// 单选 v: []
|
// 单选 v: []
|
||||||
console.log(v,'vvvvvv')
|
console.log(v,'vvvvvv')
|
||||||
|
@ -986,16 +1009,26 @@ const updateForm= async(item, submitIndex=0, submitType=1) =>{
|
||||||
|
|
||||||
lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId;
|
lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId;
|
||||||
console.log('lessionid', lessionid.value);
|
console.log('lessionid', lessionid.value);
|
||||||
|
|
||||||
// 拿到当前章节下得所有知识点
|
// 拿到当前章节下得所有知识点
|
||||||
const res = await getBindlist({ eid: lessionid.value })
|
if( props.bookobj.node.edustage == '高中' && (props.bookobj.node.edusubject == '语文' || props.bookobj.node.edusubject == '英语') ){
|
||||||
if (!res.data || res.data.length < 1) {
|
const res = await listEvaluation({ edusubject: props.bookobj.node.edusubject, edustage: props.bookobj.node.edustage, itemkey: "subject", pageSize: 10 });
|
||||||
ElMessage.warning('当前章节下未绑定知识点,暂不更新该试题知识点!');
|
const id = res.rows[0]?.id;
|
||||||
curKnowledgePointList.value = [];
|
if (id) {
|
||||||
|
const res = await listKnowlegepointFormat({evalId: id, pageNum: 1, pageSize: 5000,});
|
||||||
|
curKnowledgePointList.value = updateKnowledgePoint(res.rows);
|
||||||
|
//console.log('updateKnowledgePoint->', res.rows);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const res = await getBindlist({ eid: lessionid.value })
|
||||||
|
if (!res.data || res.data.length < 1) {
|
||||||
|
ElMessage.warning('当前章节下未绑定知识点,暂不更新该试题知识点!');
|
||||||
|
curKnowledgePointList.value = [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
curKnowledgePointList.value = res.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
curKnowledgePointList.value = res.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//item.evalnodeid = '3772b,374112,374233';
|
//item.evalnodeid = '3772b,374112,374233';
|
||||||
|
@ -1666,10 +1699,56 @@ const myMessageShow=(title, msg, status)=>{
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
watch(() => props.bookobj.levelSecondId, (newVal, oldVal) => {
|
/**
|
||||||
|
* @desc: 遍历原知识点数据, 将title字段转为knowTitle以供knowledgePointProps进行tree的格式转换显示
|
||||||
|
* @return: {*}
|
||||||
|
* @param {*} list
|
||||||
|
*/
|
||||||
|
const updateKnowledgePoint = (list) => {
|
||||||
|
list.forEach(item => {
|
||||||
|
if (item.title && item.title != '') {
|
||||||
|
item.knowTitle = item.title;
|
||||||
|
}
|
||||||
|
if (item.children && Array.isArray(item.children)) {
|
||||||
|
updateKnowledgePoint(item.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(() => props.bookobj.levelSecondId, async (newVal, oldVal) => {
|
||||||
console.log(props.bookobj,'课程选择')
|
console.log(props.bookobj,'课程选择')
|
||||||
// props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId
|
// props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId
|
||||||
lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId;
|
lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化知识点: 分两种情况
|
||||||
|
* 1. 语文/英语: 获取学科下的所有知识点(该学科对应无章节与知识点绑定, 故只获取全知识点)
|
||||||
|
* 2. 其他: 获取当前章节下的所有知识点
|
||||||
|
*/
|
||||||
|
let id = props.bookobj.levelSecondId;
|
||||||
|
if( props.bookobj.node.edustage == '高中' && (props.bookobj.node.edusubject == '语文' || props.bookobj.node.edusubject == '英语') ){
|
||||||
|
id = props.bookobj.node.rootid;
|
||||||
|
const res = await listEvaluation({ edusubject: props.bookobj.node.edusubject, edustage: props.bookobj.node.edustage, itemkey: "subject", pageSize: 10 });
|
||||||
|
id = res.rows[0]?.id;
|
||||||
|
if (id) {
|
||||||
|
listKnowlegepointFormat({evalId: id, pageNum: 1, pageSize: 5000,}).then(res => {
|
||||||
|
//console.log('listKnowlegepointFormat->', res.rows);
|
||||||
|
curKnowledgePointList.value = updateKnowledgePoint(res.rows);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
getBindlist({ eid: id }).then(res => {
|
||||||
|
if (!res.data || res.data.length < 1) {
|
||||||
|
ElMessage.warning('当前章节下未绑定知识点,暂不更新该试题知识点!');
|
||||||
|
curKnowledgePointList.value = [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
curKnowledgePointList.value = res.data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -1680,7 +1759,20 @@ defineExpose({
|
||||||
cropperFormItemCallBack,
|
cropperFormItemCallBack,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.el-cascader-panel {
|
||||||
|
li[aria-haspopup="true"] {
|
||||||
|
.el-checkbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
||||||
.questForm-item-cover{
|
.questForm-item-cover{
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
@ -1698,6 +1790,4 @@ defineExpose({
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -20,6 +20,7 @@
|
||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
<el-form-item label="知识点" label-width="70">
|
<el-form-item label="知识点" label-width="70">
|
||||||
<el-cascader
|
<el-cascader
|
||||||
|
disabled
|
||||||
v-model="entpCourseWorkQueryParams.point"
|
v-model="entpCourseWorkQueryParams.point"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
@ -118,11 +119,18 @@ import { useGetHomework } from '@/hooks/useGetHomework'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import {throttle,debounce } from '@/utils/comm'
|
import {throttle,debounce } from '@/utils/comm'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import useClassTaskStore from '@/store/modules/classTask'
|
||||||
|
|
||||||
// 定义要发送的emit事件
|
// 定义要发送的emit事件
|
||||||
const emit = defineEmits(['addQuiz'])
|
const emit = defineEmits(['addQuiz'])
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const userStore = useUserStore().user
|
const userStore = useUserStore().user
|
||||||
|
const {
|
||||||
|
entpCourseWorkTypeList,
|
||||||
|
entpCourseWorkGroupList,
|
||||||
|
entpCourseWorkYearList
|
||||||
|
} = useClassTaskStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bookobj: {
|
bookobj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -130,41 +138,11 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const entpCourseWorkTypeList = ref([
|
|
||||||
{value: 0, label: "不限"},
|
|
||||||
{value: 1, label: "单选题"},
|
|
||||||
{value: 2, label: "填空题"},
|
|
||||||
{value: 3, label: "多选题"},
|
|
||||||
{value: 4, label: "判断题"},
|
|
||||||
{value: 5, label: "主观题"},
|
|
||||||
{value: 6, label: "复合题"},
|
|
||||||
]); // 习题查询条件 - 题型
|
|
||||||
|
|
||||||
const entpCourseWorkGroupList = ref([{
|
|
||||||
Key: -1,
|
|
||||||
Value: '不限',
|
|
||||||
}, {
|
|
||||||
Key: 1,
|
|
||||||
Value: '真题',
|
|
||||||
}, {
|
|
||||||
Key: 0,
|
|
||||||
Value: '非真题',
|
|
||||||
}]); // 习题查询条件 - 题源
|
|
||||||
|
|
||||||
const entpCourseWorkPointList = ref([
|
const entpCourseWorkPointList = ref([
|
||||||
{label: '不限', value: []},
|
{label: '不限', value: []},
|
||||||
]); // 习题查询条件 - 知识点
|
]); // 习题查询条件 - 知识点
|
||||||
const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
|
const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
|
||||||
//const knowledgePointProps = ref({value: 'thirdId', label: 'knowTitle'});
|
//const knowledgePointProps = ref({value: 'thirdId', label: 'knowTitle'});
|
||||||
const entpCourseWorkYearList =ref([
|
|
||||||
{label: '不限', value: '-1'},
|
|
||||||
{label: '2024', value: '2024'},
|
|
||||||
{label: '2023', value: '2023'},
|
|
||||||
{label: '2022', value: '2022'},
|
|
||||||
{label: '2021', value: '2021'},
|
|
||||||
{label: '2020', value: '2020'},
|
|
||||||
]); // 习题查询条件 - 年份
|
|
||||||
|
|
||||||
|
|
||||||
// 习题查询参数条件
|
// 习题查询参数条件
|
||||||
|
@ -342,7 +320,7 @@ const handleQueryFromEntpCourseWork= async (queryType) => {
|
||||||
} else if (clueres.rows[i].cluetag == 'mapview') {
|
} else if (clueres.rows[i].cluetag == 'mapview') {
|
||||||
clueres.rows[i].worktype = '学科定位';
|
clueres.rows[i].worktype = '学科定位';
|
||||||
}
|
}
|
||||||
console.log("clueres.rows[i].childlist",clueres.rows[i].childlist);
|
//console.log("clueres.rows[i].childlist",clueres.rows[i].childlist);
|
||||||
if (clueres.rows[i].childlist != '') {
|
if (clueres.rows[i].childlist != '') {
|
||||||
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
|
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
|
||||||
for (var j=0; j<clueres.rows[i].childArray.length; j++) {
|
for (var j=0; j<clueres.rows[i].childArray.length; j++) {
|
||||||
|
|
|
@ -58,6 +58,11 @@ import { useGetSubject } from '@/hooks/useGetSubject'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import useClassTaskStore from '@/store/modules/classTask'
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const classTaskStore = useClassTaskStore();
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
const chartDom = ref(null);
|
const chartDom = ref(null);
|
||||||
|
@ -206,11 +211,14 @@ ipcRenderer.on('minWinResize', debounce((e, data) =>{
|
||||||
}, 100))
|
}, 100))
|
||||||
|
|
||||||
onMounted(async ()=>{
|
onMounted(async ()=>{
|
||||||
|
|
||||||
await useGetSubject()
|
await useGetSubject()
|
||||||
// 确保DOM 渲染完成
|
// 确保DOM 渲染完成
|
||||||
await nextTick()
|
await nextTick()
|
||||||
chartInstance = echarts.init(chartDom.value)
|
chartInstance = echarts.init(chartDom.value)
|
||||||
|
|
||||||
|
// 更新第三方题型题类
|
||||||
|
await classTaskStore.initJYInfo(userStore.user);
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
|
|
@ -187,7 +187,7 @@ const getWorkType = async (data) => {
|
||||||
if (selName === curName) {
|
if (selName === curName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const jyCT = await JYApiListCT(proxy, selName);
|
const jyCT = await JYApiListCT(selName);
|
||||||
if (jyCT.length == 0) {
|
if (jyCT.length == 0) {
|
||||||
ElMessage.error('获取题型失败!');
|
ElMessage.error('获取题型失败!');
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -60,6 +60,7 @@ import { sessionStore } from '@/utils/store'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
import { dataSetJson } from '@/utils/comm.js'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
const userInfo = useUserStore().user
|
const userInfo = useUserStore().user
|
||||||
const textarea = ref('')
|
const textarea = ref('')
|
||||||
|
@ -119,7 +120,7 @@ const getCompletion = async (val) => {
|
||||||
type: 'robot',
|
type: 'robot',
|
||||||
msg: answer,
|
msg: answer,
|
||||||
})
|
})
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
loaded.value = false
|
loaded.value = false
|
||||||
}
|
}
|
||||||
|
@ -132,6 +133,8 @@ const saveAdjust = (item) =>{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const curFile = reactive({})
|
const curFile = reactive({})
|
||||||
const dataset_id = ref('')
|
const dataset_id = ref('')
|
||||||
const fileList = ref([])
|
const fileList = ref([])
|
||||||
|
@ -144,7 +147,9 @@ const getList = () =>{
|
||||||
Object.assign(curFile, fileList.value[0])
|
Object.assign(curFile, fileList.value[0])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
emitter.on('curFile', (item) =>{
|
||||||
|
changeFile(item)
|
||||||
|
})
|
||||||
const changeFile = (val) =>{
|
const changeFile = (val) =>{
|
||||||
|
|
||||||
Object.assign(curFile, val);
|
Object.assign(curFile, val);
|
||||||
|
@ -154,8 +159,8 @@ const changeFile = (val) =>{
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data);
|
||||||
let text = props.modeType == 1 ? '课标': props.modeType == 2 ? '教材' : '考试'
|
let text = props.modeType == 1 ||props.modeType == 2 ? '课标' : '考试'
|
||||||
|
|
||||||
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
params.dataset_id = dataSetJson[jsonKey]
|
||||||
dataset_id.value = dataSetJson[jsonKey]
|
dataset_id.value = dataSetJson[jsonKey]
|
||||||
|
@ -190,7 +195,7 @@ onMounted(() => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
}
|
}
|
||||||
.chart-item {
|
.chart-item {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
@ -300,4 +305,4 @@ onMounted(() => {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,16 +8,16 @@
|
||||||
</template>
|
</template>
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="flex dialog-top">
|
<div class="flex dialog-top">
|
||||||
<el-radio-group v-model="radio" @change="changeRadio">
|
<!-- <el-radio-group v-model="radio" @change="changeRadio">
|
||||||
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
|
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group> -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content-list">
|
<div class="content-list">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(item, index) in list" :class="activeIndex == index ? 'li-active' : ''" @click="clickItem(index)">
|
<li v-for="(item, index) in fileList" :class="activeIndex == index ? 'li-active' : ''" @click="clickItem(index, item)">
|
||||||
<el-image class="img" :src="item.url" />
|
<el-image class="img" :src="url" />
|
||||||
<span>{{ item.name }}</span>
|
<el-text truncated>{{ item.fileName }}</el-text>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,6 +47,7 @@ import { sessionStore } from '@/utils/store'
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
import { dataSetJson } from '@/utils/comm.js'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
const userInfo = useUserStore().user
|
const userInfo = useUserStore().user
|
||||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
|
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
|
||||||
|
@ -93,11 +94,7 @@ const changeRadio = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const activeIndex = ref(-1)
|
const activeIndex = ref(0)
|
||||||
|
|
||||||
const clickItem = (index) => {
|
|
||||||
activeIndex.value = index
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataset_id = ref('')
|
const dataset_id = ref('')
|
||||||
|
|
||||||
|
@ -109,7 +106,7 @@ const onSuccess = async (response) =>{
|
||||||
dataset_id: dataset_id.value
|
dataset_id: dataset_id.value
|
||||||
}
|
}
|
||||||
const res = await completion(data)
|
const res = await completion(data)
|
||||||
console.log(res)
|
|
||||||
if(res.data.code != 200) return
|
if(res.data.code != 200) return
|
||||||
let docData = {
|
let docData = {
|
||||||
fileUrl: response.url,
|
fileUrl: response.url,
|
||||||
|
@ -123,19 +120,30 @@ const onSuccess = async (response) =>{
|
||||||
}
|
}
|
||||||
const { msg } = await addDoc(docData)
|
const { msg } = await addDoc(docData)
|
||||||
ElMessage.success(msg)
|
ElMessage.success(msg)
|
||||||
|
getList()
|
||||||
|
|
||||||
}
|
}
|
||||||
const curNode = reactive({})
|
const curNode = reactive({})
|
||||||
|
|
||||||
|
const fileList = ref([])
|
||||||
|
const curFile = reactive({})
|
||||||
const getList = () =>{
|
const getList = () =>{
|
||||||
docList({
|
docList({
|
||||||
userId: userInfo.userId,
|
userId: userInfo.userId,
|
||||||
dataset_id: dataset_id.value
|
dataset_id: dataset_id.value
|
||||||
}).then( res =>{
|
}).then( res =>{
|
||||||
console.log(res)
|
fileList.value = [...res.rows]
|
||||||
|
Object.assign(curFile, fileList.value[0])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clickItem = (index, item) => {
|
||||||
|
activeIndex.value = index
|
||||||
|
Object.assign(curFile, item)
|
||||||
|
emitter.emit('curFile',item)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(() =>{
|
onMounted(() =>{
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
@ -174,6 +182,7 @@ onMounted(() =>{
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
width: 130px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
|
@ -143,7 +143,8 @@ const getCompletion = async () => {
|
||||||
for (let item of childTempList.value) {
|
for (let item of childTempList.value) {
|
||||||
try {
|
try {
|
||||||
item.loading = true
|
item.loading = true
|
||||||
params.prompt = `根据${curNode.edustage}${curNode.edusubject},提炼出${item.prompt}`
|
// params.prompt = `根据${curNode.edustage}${curNode.edusubject},提炼出${item.prompt}`
|
||||||
|
params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
|
||||||
const { data } = await completion(params)
|
const { data } = await completion(params)
|
||||||
let answer = data.answer
|
let answer = data.answer
|
||||||
item.oldAnswer = answer
|
item.oldAnswer = answer
|
||||||
|
@ -257,7 +258,7 @@ const removeItem = async (item) => {
|
||||||
isAdd.value = false
|
isAdd.value = false
|
||||||
Object.assign(keywordItem, item)
|
Object.assign(keywordItem, item)
|
||||||
isEditKeyWord.value = true
|
isEditKeyWord.value = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// const { msg } = await removeChildTemp(item.id)
|
// const { msg } = await removeChildTemp(item.id)
|
||||||
|
@ -269,7 +270,7 @@ const removeItem = async (item) => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data);
|
||||||
let text = props.modeType == 1 ? '课标' : props.modeType == 2 ? '教材' : '考试'
|
let text = props.modeType == 1 || props.modeType == 2? '课标' : '考试'
|
||||||
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
params.dataset_id = dataSetJson[jsonKey]
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,65 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="page-resource flex mb-4">
|
||||||
<div class="page-resource flex">
|
<!-- 左侧 教材 目录 -->
|
||||||
<!-- 左侧 教材 目录 -->
|
<!-- <ChooseTextbook @change-book="getData" @node-click="getData" /> -->
|
||||||
<ChooseTextbook @change-book="getData" @node-click="getData" />
|
|
||||||
|
|
||||||
<div class="page-right">
|
<div class="page-right">
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<el-button style="margin-left: 12px;" type="primary" @click="onchange('/model/curriculum')">课标研读</el-button>
|
<el-button type="primary" @click="onchange('/model/curriculum')">课标研读</el-button>
|
||||||
<!-- <el-button type="primary" @click="onchange('/model/management')">作业管理1</el-button> -->
|
<!-- <el-button type="primary" @click="onchange('/model/management')">作业管理1</el-button> -->
|
||||||
<el-button type="primary" @click="onchange('/model/newClassTaskAssign')">作业管理</el-button>
|
<el-button type="primary" @click="onchange('/model/newClassTaskAssign')">作业管理</el-button>
|
||||||
<el-button type="success" @click="onchange('/model/teaching')">教材研读</el-button>
|
<el-button type="success" @click="onchange('/model/teaching')">教材研读</el-button>
|
||||||
<el-button type="info" @click="onchange('/model/design')">教学框架设计</el-button>
|
<el-button type="info" @click="onchange('/model/design')">教学框架设计</el-button>
|
||||||
<el-button type="success" @click="openPPTist">打开PPTist</el-button>
|
<el-button type="success" @click="openPPTist">打开PPTist</el-button>
|
||||||
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
|
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
|
||||||
</div>
|
<el-button type="primary" v-menus="dt.menus">测试</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<el-row class="container">
|
||||||
|
<!-- 左侧 选择教材 目录 -->
|
||||||
|
<ChooseTextbook @change-book="changeBook" @node-click="changeBook" />
|
||||||
|
<!-- 中间 展示内容 -->
|
||||||
|
<el-col :span="10">
|
||||||
|
<div class="c-item mb-4 mx-4">
|
||||||
|
<div class="flex justify-between pb-2">
|
||||||
|
<h3>教师资源</h3>
|
||||||
|
<span class="c-btns">
|
||||||
|
<template v-for="item in resourBtns">
|
||||||
|
<el-button :size="item.size" text :icon="item.icon" @click="handleAll(item.prop)">{{ item.name }}</el-button>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<c-table ref="resourRef" v-bind="sourceOpt" t-class="rounded">
|
||||||
|
<template #title="{row,value}">
|
||||||
|
<el-link :underline="false" @click="handleAll('open', row)">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use :xlink:href="`#icon-${getIcon(row)}`"></use>
|
||||||
|
</svg>
|
||||||
|
<b class="ml-1">{{ value }}</b>
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</c-table>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, watch, reactive } from 'vue'
|
import { onMounted, ref, watch, reactive } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { createWindow } from '@/utils/tool' // 相关工具
|
import { Plus, Refresh, Upload, Files, UploadFilled } from '@element-plus/icons-vue'
|
||||||
|
import useUserStore from '@/store/modules/user' // 用户信息
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
import { createWindow, sessionStore } from '@/utils/tool' // 相关工具
|
||||||
|
import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
|
||||||
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
|
// 组件引入
|
||||||
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
import ChooseTextbook from '@/components/choose-textbook/index.vue'
|
||||||
|
import { menusEvent } from '@/plugins/vue3-menus' // 右键菜单
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore() // 用户信息
|
||||||
|
|
||||||
const courseObj = reactive({
|
const courseObj = reactive({
|
||||||
// 课程相关参数: 教材id,单元id,章节id,课程名称
|
// 课程相关参数: 教材id,单元id,章节id,课程名称
|
||||||
|
@ -34,10 +68,58 @@ const courseObj = reactive({
|
||||||
levelSecondId: '',
|
levelSecondId: '',
|
||||||
coursetitle: '',
|
coursetitle: '',
|
||||||
node: null, // 选择的课程节点
|
node: null, // 选择的课程节点
|
||||||
|
entp: null, // 当前课程
|
||||||
|
})
|
||||||
|
const dt = reactive({
|
||||||
|
curRow: null, // 当前行数据
|
||||||
|
menus: [ // 右键菜单
|
||||||
|
{ label: '打开', click: (_, args) => handleAll('open', args) },
|
||||||
|
{ label: '删除', click: (_, args) => handleAll('delete', args) },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
// ref定义
|
||||||
|
const resourRef = ref() // 资源ref
|
||||||
|
// 资源按钮配置
|
||||||
|
const resourBtns = [
|
||||||
|
{ name: '刷新', prop: 'refresh', size: 'small', icon: Refresh },
|
||||||
|
{ name: '资源库', prop:'resource', size:'small', icon: Files },
|
||||||
|
{ name: '上传', prop:'upload', size:'small', icon: UploadFilled },
|
||||||
|
{ name: '添加', prop:'add', size:'small', icon: Plus },
|
||||||
|
]
|
||||||
|
// 资源相关配置-cTable
|
||||||
|
const sourceOpt = reactive({
|
||||||
|
data: [], // 数据
|
||||||
|
option: [ // 列配置
|
||||||
|
{ label: '名称', prop: 'title', align: 'left' },
|
||||||
|
{ label: '类型', prop: 'filetype', width: 80, },
|
||||||
|
{ label: '时间', prop: 'timestamp', width: 160, sortable: true },
|
||||||
|
],
|
||||||
|
noPage: true, // 不显示分页
|
||||||
|
isMain: false, // 主界面
|
||||||
|
highlightCurrentRow: true, // 高亮当前行
|
||||||
|
rowClick: (r, c, e) => { // 行点击事件-处理高亮(再次点击取消选中)
|
||||||
|
if (dt.curRow == r) { // 重复点击-取消选中
|
||||||
|
resourRef.value.$refs.table.setCurrentRow()
|
||||||
|
dt.curRow = null
|
||||||
|
} else dt.curRow = r
|
||||||
|
},
|
||||||
|
rowContextmenu: (r, c, e) => { // 行—右键菜单事件
|
||||||
|
dt.menus.forEach(item => {
|
||||||
|
if(item.label == '打开') item.icon = getIcon(r, 'svg')
|
||||||
|
else if(item.label == '删除') item.icon = getIcon('icon-shanchu', 'class')
|
||||||
|
})
|
||||||
|
menusEvent(e, dt.menus, r)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 查询
|
// 页面加载
|
||||||
const getData = (data) => {
|
onMounted(() => {
|
||||||
|
})
|
||||||
|
|
||||||
|
// 相关方法-methods
|
||||||
|
// 教材选中
|
||||||
|
const changeBook = async(data) => {
|
||||||
|
// console.log(data)
|
||||||
const { textBook, node } = data
|
const { textBook, node } = data
|
||||||
let textbookId = textBook.curBookId
|
let textbookId = textBook.curBookId
|
||||||
let levelSecondId = node.id
|
let levelSecondId = node.id
|
||||||
|
@ -57,6 +139,13 @@ const getData = (data) => {
|
||||||
|
|
||||||
// 头部 教材分析打开外部链接需要当前章节ID
|
// 头部 教材分析打开外部链接需要当前章节ID
|
||||||
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId }))
|
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId }))
|
||||||
|
// 获取当前章节对应的课程信息
|
||||||
|
const params = { evalid: node.id, edituserid: userStore.id, pageSize: 1 }
|
||||||
|
const res = await HTTP_SERVER_API('getCourseList', params)
|
||||||
|
courseObj.entp = res?.rows?.[0] || null
|
||||||
|
sessionStore.set('curr.entp', courseObj.entp) // 缓存当前课程信息
|
||||||
|
// 更新-资源列表
|
||||||
|
getResourceList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const openPPTist = () => {
|
const openPPTist = () => {
|
||||||
|
@ -71,19 +160,170 @@ const onchange = (path) => {
|
||||||
router.push(path)
|
router.push(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 获取资源列表
|
||||||
|
const getResourceList = async () => {
|
||||||
|
const entpcourseidarray = courseObj?.entp?.id
|
||||||
|
if (!entpcourseidarray) return msgUtils.msgWarning('请选择章节?')
|
||||||
|
const params = {
|
||||||
|
pageSize: 100, parentid: 0, entpcourseidarray,
|
||||||
|
orderByColumn: 'timestamp', isAsc: 'desc',
|
||||||
|
}
|
||||||
|
const res = await HTTP_SERVER_API('getCourseFileList', params)
|
||||||
|
if (res?.code == 200) {
|
||||||
|
sourceOpt.data = res?.rows || []
|
||||||
|
} else {
|
||||||
|
msgUtils.msgWarning('获取资源列表, 请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 统一HTTP处理
|
||||||
|
const HTTP_SERVER_API = (type, params = {}) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'addEntpcourse': { // 添加课程
|
||||||
|
const node = courseObj.node || {}
|
||||||
|
if (!node) return msgUtils.msgWarning('请选择章节?')
|
||||||
|
const def = { // 默认参数
|
||||||
|
entpid: userStore.user.deptId, // 部门id
|
||||||
|
level: 1, // 层级
|
||||||
|
parentid: 0, // 父级id
|
||||||
|
dictid: 0, // 字典id
|
||||||
|
evalid: node.id, // 章节id
|
||||||
|
evalparentid: node.parentid, // 单元id(父级id)
|
||||||
|
edusubject: node.edusubject, // 学科
|
||||||
|
edudegree: node.edudegree, // 年级
|
||||||
|
edustage: node.edustage, // 阶段
|
||||||
|
coursetype: '课标学科', // 课程类型
|
||||||
|
coursetitle: node.itemtitle, // 课程名称
|
||||||
|
coursedesc: '', // 课程描述
|
||||||
|
status: '', // 状态
|
||||||
|
dflag: 0, // 状态
|
||||||
|
edituserid: userStore.id, // 编辑人id
|
||||||
|
createblankfile: 'no', // 创建空白文件
|
||||||
|
}
|
||||||
|
courseObj.entp = def
|
||||||
|
return API_entpcourse.addEntpcourse(def)
|
||||||
|
}
|
||||||
|
case 'addEntpcoursefile': { // 添加课程文件
|
||||||
|
const enpt = courseObj.entp
|
||||||
|
const def = {
|
||||||
|
parentid: 0,
|
||||||
|
entpid: userStore.user.deptId,
|
||||||
|
entpcourseid: enpt.id,
|
||||||
|
ppttype: 'file',
|
||||||
|
title: enpt.coursetitle,
|
||||||
|
fileurl: '',
|
||||||
|
filetype: 'aptist',
|
||||||
|
datacontent: '',
|
||||||
|
filekey: '',
|
||||||
|
filetag: '',
|
||||||
|
fileidx: 0,
|
||||||
|
dflag: 0,
|
||||||
|
status: '',
|
||||||
|
edituserid: userStore.id
|
||||||
|
}
|
||||||
|
// return Promise.resolve(1)
|
||||||
|
return API_entpcoursefile.addEntpcoursefileReturnId({...def,...params})
|
||||||
|
}
|
||||||
|
case 'getCourseList': { // 获取课程列表
|
||||||
|
return API_entpcourse.listEntpcourse(params)
|
||||||
|
}
|
||||||
|
case 'getCourseFileList':{ // 获取课程文件列表
|
||||||
|
return API_entpcoursefile.listEntpcoursefileNew(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 事件回调
|
||||||
|
const handleAll = async(type, row) =>{
|
||||||
|
// console.log(type)
|
||||||
|
switch (type) {
|
||||||
|
case 'refresh': // 刷新
|
||||||
|
getResourceList()
|
||||||
|
break;
|
||||||
|
case 'resource': // 资源库
|
||||||
|
break;
|
||||||
|
case 'upload': // 上传
|
||||||
|
break;
|
||||||
|
case 'add':{ // 添加PPT-list - 课程文件
|
||||||
|
const enpt = courseObj.entp // 获取课程信息
|
||||||
|
if (!enpt) { // 如果没有,就新增课程
|
||||||
|
const resid = await HTTP_SERVER_API('addEntpcourse')
|
||||||
|
courseObj.entp.id = resid
|
||||||
|
}
|
||||||
|
// 生成ppt课件-父级
|
||||||
|
const p_params = {parentContent: '{"width":1000,"ratio":0.5625}'}
|
||||||
|
const id = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
||||||
|
if (!!id??null) { // 生成第一个幻灯片
|
||||||
|
const params = {
|
||||||
|
parentid: id,
|
||||||
|
title: '第一页',
|
||||||
|
filetype: 'slide',
|
||||||
|
datacontent: '{"elements":[],"background":{"type":"solid","color":"#fff"}}' // json内容
|
||||||
|
}
|
||||||
|
// 生成ppt课件-子级(slide)
|
||||||
|
await HTTP_SERVER_API('addEntpcoursefile', params)
|
||||||
|
// 刷新资源列表
|
||||||
|
await getResourceList()
|
||||||
|
} else {
|
||||||
|
msgUtils.msgWarning('添加失败!')
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'open': { // 打开资源-pptist
|
||||||
|
if (row.filetype != 'aptist') return msgUtils.msgWarning('暂不支持该类型文件操作!')
|
||||||
|
sessionStore.set('curr.resource', row) // 缓存当前资源信息
|
||||||
|
createWindow('open-win', {
|
||||||
|
url: '/pptist', // 窗口关闭时,清除缓存
|
||||||
|
close: () => {
|
||||||
|
sessionStore.set('curr.resource', null) // 清除缓存
|
||||||
|
getResourceList() // 刷新资源列表
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'delete':{ // 删除资源
|
||||||
|
if (!(row && row.id)) return msgUtils.msgWarning('请选择要删除的资源!')
|
||||||
|
await msgUtils.confirm(`是否确认删除【${row.title}】课程课件?`)
|
||||||
|
await API_entpcoursefile.delEntpcoursefileNew(row.id)
|
||||||
|
msgUtils.msgSuccess('删除成功!')
|
||||||
|
// 刷新资源列表
|
||||||
|
await getResourceList()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// icons 处理 type 代表传递svg
|
||||||
|
const getIcon = (o, type) => {
|
||||||
|
let icon = typeof o == 'string' ? o : o?.filetype
|
||||||
|
if (['aptist'].includes(o?.filetype)) icon = 'pptx'
|
||||||
|
if (!!type) { // 其他格式icon
|
||||||
|
switch(type) {
|
||||||
|
case 'svg': // 返回svg格式
|
||||||
|
return `<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-${icon}"></use>
|
||||||
|
</svg>`
|
||||||
|
case 'class': // class
|
||||||
|
return `<span class="icon iconfont ${icon}"></span>`
|
||||||
|
case 'unicode': // unicode
|
||||||
|
return `<span class="icon iconfont">${icon}</span>`
|
||||||
|
default: // 返回icon-class
|
||||||
|
return `icon-${icon}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-resource {
|
.page-resource {
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
padding: 10px 15px 0;
|
// padding: 10px 15px 0;
|
||||||
|
|
||||||
.page-right {
|
.page-right {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-left: 20px;
|
// margin-left: 20px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
@ -94,7 +334,7 @@ const onchange = (path) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin: 1rem 0;
|
margin: 1rem;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
.el-button {
|
.el-button {
|
||||||
|
@ -105,4 +345,12 @@ const onchange = (path) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.container{
|
||||||
|
height: calc(100% - 32px - 3rem);
|
||||||
|
.c-item{
|
||||||
|
.c-btns{
|
||||||
|
.el-button{margin: 0;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,380 @@
|
||||||
|
<template>
|
||||||
|
<div class="ai-container">
|
||||||
|
<el-steps style="max-width:100% " :active="activeStep" align-center>
|
||||||
|
<el-step title="生成大纲" />
|
||||||
|
<el-step title="选择模板" />
|
||||||
|
<el-step title="制作PPT" />
|
||||||
|
</el-steps>
|
||||||
|
<div class="card-box">
|
||||||
|
<el-card class="card2" v-if="activeStep === 0">
|
||||||
|
<div class="paragraphs">
|
||||||
|
{{ outputText }}
|
||||||
|
</div>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="addMessage">从新生成</el-button>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 1">下一步</el-button>
|
||||||
|
</el-card>
|
||||||
|
<el-card v-if="activeStep === 1">
|
||||||
|
<div style="padding-bottom: 10px">ppt模板选择</div>
|
||||||
|
<div class="themes">
|
||||||
|
<div v-for="item in backGroundList" :key="item.key" :style="{
|
||||||
|
padding: '20px',
|
||||||
|
paddingRight: '30px',
|
||||||
|
paddingLeft: '30px',
|
||||||
|
margin: '10px',
|
||||||
|
backgroundColor: getBackgroundColor(item.key),
|
||||||
|
borderRadius: '10px',
|
||||||
|
borderBlock: '10px solid #e6e6e6'
|
||||||
|
}" @click="chooseBackground(item.key)" @mouseenter="changeCursor('pointer')" @mouseleave="changeCursor('default')">
|
||||||
|
{{ item.name }}
|
||||||
|
<br />
|
||||||
|
<img style="width: 150px; height: auto" :src="item.thumbnail" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row class="el-row">
|
||||||
|
<el-col :span="6" class="el-col">
|
||||||
|
<div class="grid-content-1">
|
||||||
|
<div>自动配图</div>
|
||||||
|
<el-switch v-model="outlineData.is_figure" />
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6" class="el-col">
|
||||||
|
<div class="grid-content-2">
|
||||||
|
<div>PPT作者名:</div>
|
||||||
|
<el-input v-model="outlineData.author" style="width: 50%" />
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 0">上一步</el-button>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="outlineCreatePPT()">生成PPT</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<el-card v-if="activeStep === 2">
|
||||||
|
<el-progress :percentage="percentage" type="circle" ></el-progress>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { creatAIPPT } from '@/utils/talkFile'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getBackGround,
|
||||||
|
createPPT,
|
||||||
|
getProgress,
|
||||||
|
} from "@/utils/ppt-request.js";
|
||||||
|
import CryptoJS from "crypto-js"
|
||||||
|
|
||||||
|
import { getSignature } from "@/utils/index.js";
|
||||||
|
|
||||||
|
let appId = "01ec9aa3";
|
||||||
|
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
|
||||||
|
let apikey = "39d05b269fa229f431a56c21794a8ea5"
|
||||||
|
let timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
let signature = getSignature(appId, secret, timestamp);
|
||||||
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
|
||||||
|
|
||||||
|
const outputText = ref(""); // 用于展示的大纲数据
|
||||||
|
const stagingData = ref([]); //储存的对话数据,用于多轮对话
|
||||||
|
const stagOutputText = ref(""); // 暂存大纲用于拆分
|
||||||
|
let extractedParts = ref([]) // 初步拆分
|
||||||
|
|
||||||
|
let firstArray = ref([]); //大纲的大纲等级数字部分
|
||||||
|
let secondArray = ref([]); //大纲的文字部分
|
||||||
|
|
||||||
|
|
||||||
|
const backGroundList = ref([]);
|
||||||
|
|
||||||
|
const inputTheme = ref("高中语文《沁园春雪》的授课课件"); // 输入的主题
|
||||||
|
const inputRequire = ref("") // 输入的需求
|
||||||
|
const activeStep = ref(0); // 上方进度条
|
||||||
|
const combined = ref('') // 修改完毕的大纲数据,准备传入ppt生成模型
|
||||||
|
|
||||||
|
const treeData = ref([]);
|
||||||
|
const status = ref("init");
|
||||||
|
|
||||||
|
const percentage = ref(0);
|
||||||
|
|
||||||
|
const getBackground = () => {
|
||||||
|
treeData.value = [];
|
||||||
|
getBackGround().then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
backGroundList.value = res;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBackgroundColor = (key) => {
|
||||||
|
return outlineData.value.theme === key ? '#83e2b6' : '#f5f5f5';
|
||||||
|
};
|
||||||
|
|
||||||
|
const outlineData = ref({
|
||||||
|
query: '', // 用户要求(最多8000字)
|
||||||
|
theme: 'auto', // ppt生成主题
|
||||||
|
author: 'AIX平台',
|
||||||
|
is_card_note: false, // 是否自动生成ppt演讲备注
|
||||||
|
is_cover_img: false, // 是否自动生成封面
|
||||||
|
is_figure: false, // 是否自动配图
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits(['addSuccess'])
|
||||||
|
const props = defineProps({
|
||||||
|
dataList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 将输入数据或返回数据存入记忆中
|
||||||
|
function updateStagingData(role, newData) {
|
||||||
|
stagingData.value.push({ role: role, content: newData });
|
||||||
|
}
|
||||||
|
//大纲直接生成ppt
|
||||||
|
const outlineCreatePPT = () => {
|
||||||
|
const newOutlineData = { ...outlineData.value, };
|
||||||
|
newOutlineData.query = outputText.value;
|
||||||
|
|
||||||
|
createPPT(newOutlineData).then((res) => {
|
||||||
|
console.log(res, "正在生成中");
|
||||||
|
activeStep.value = 2
|
||||||
|
|
||||||
|
const checkProgress = () => {
|
||||||
|
getProgress(res.sid).then((response) => {
|
||||||
|
percentage.value = response.process;
|
||||||
|
if (response && response.pptUrl && response.pptUrl.length > 4) {
|
||||||
|
// window.location.href = response.data.pptUrl;
|
||||||
|
//发消息到主进程,携带名称和URL,将URL下载下来后复制到文件列表并上传到服务
|
||||||
|
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFiledf28bf990a4c40ffb7477ed4b65392c27232357022409613439/%E3%80%8A%E9%9D%99%E5%A5%B3%E3%80%8B%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BB%E4%B8%8E%E7%A0%94%E7%A9%B6.pptx"
|
||||||
|
emit('addSuccess',res)
|
||||||
|
ElMessage.success("生成成功");
|
||||||
|
} else {
|
||||||
|
const sleepTime = 2000;
|
||||||
|
let remainingTime = sleepTime;
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
remainingTime -= 100;
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
checkProgress();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkProgress();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//初次对话
|
||||||
|
const addMessage = () => {
|
||||||
|
const themeValue = inputTheme.value;
|
||||||
|
const requireValue = inputRequire.value;
|
||||||
|
firstArray.value = []
|
||||||
|
secondArray.value = []
|
||||||
|
extractedParts.value = []
|
||||||
|
stagOutputText.value = ''
|
||||||
|
const combinedString = `请帮我生成一个ppt大纲,主题为:${themeValue}。具体内容要求为:${requireValue}。注意,用三个等级大纲展示,如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
|
||||||
|
updateStagingData("user", combinedString);
|
||||||
|
connectWebSocket(stagingData.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
let ttsWS
|
||||||
|
function connectWebSocket(data) {
|
||||||
|
outputText.value = ""; //清楚展示部分内容
|
||||||
|
status.value = "ttsing";
|
||||||
|
return getWebsocketUrl().then((url) => {
|
||||||
|
ttsWS = new WebSocket(url);
|
||||||
|
ttsWS.onopen = () => {
|
||||||
|
webSocketSend(ttsWS, data);
|
||||||
|
};
|
||||||
|
ttsWS.onmessage = (e) => {
|
||||||
|
result1(e.data);
|
||||||
|
};
|
||||||
|
ttsWS.onerror = (e) => {
|
||||||
|
status.value = "error";
|
||||||
|
console.log("WebSocket error:", e);
|
||||||
|
};
|
||||||
|
ttsWS.onclose = () => {
|
||||||
|
status.value = "init";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWebsocketUrl() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var apiKey = apikey;
|
||||||
|
var apiSecret = secret;
|
||||||
|
var url = "wss://spark-api.xf-yun.com/v4.0/chat";
|
||||||
|
|
||||||
|
var host = "spark-api.xf-yun.com";
|
||||||
|
var date = new Date().toGMTString();
|
||||||
|
var algorithm = "hmac-sha256";
|
||||||
|
var headers = "host date request-line";
|
||||||
|
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v4.0/chat HTTP/1.1`;
|
||||||
|
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
|
||||||
|
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
|
||||||
|
|
||||||
|
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
|
||||||
|
var authorization = CryptoJS.enc.Base64.stringify(
|
||||||
|
CryptoJS.enc.Utf8.parse(authorizationOrigin)
|
||||||
|
);
|
||||||
|
|
||||||
|
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
|
||||||
|
console.log(url);
|
||||||
|
resolve(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function webSocketSend(ws, data) {
|
||||||
|
const params = {
|
||||||
|
header: {
|
||||||
|
app_id: appId,
|
||||||
|
},
|
||||||
|
parameter: {
|
||||||
|
chat: {
|
||||||
|
domain: "4.0Ultra",
|
||||||
|
temperature: 0.5,
|
||||||
|
max_tokens: 1024,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
message: {
|
||||||
|
text: data,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ws.send(JSON.stringify(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
function result1(resultData) {
|
||||||
|
let jsonData = JSON.parse(resultData);
|
||||||
|
outputText.value += jsonData.payload.choices.text[0].content;
|
||||||
|
const div = document.querySelector('.paragraphs');
|
||||||
|
if (div) {
|
||||||
|
div.scrollTop = div.scrollHeight;
|
||||||
|
}
|
||||||
|
if (jsonData.payload && jsonData.payload.usage) {
|
||||||
|
startExtraction() // 返回完毕后开始拆分大纲
|
||||||
|
console.log(firstArray.value, secondArray.value)
|
||||||
|
activeStep.value = 2
|
||||||
|
updateStagingData("assistant", outputText.value) //返回数据存入记忆池
|
||||||
|
}
|
||||||
|
if (jsonData.header.code !== 0) {
|
||||||
|
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chooseBackground = (data) => {
|
||||||
|
outlineData.value.theme = data
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCursor = (cursorStyle) => {
|
||||||
|
document.documentElement.style.cursor = cursorStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx"
|
||||||
|
// creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => {
|
||||||
|
// emit('addSuccess',res)
|
||||||
|
// })
|
||||||
|
// connectWebSocket("");
|
||||||
|
let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFilea2e0342f406e4f89b7524bf421d3fef26331634651754729404/%E9%AB%98%E4%B8%AD%E8%AF%AD%E6%96%87%E3%80%8A%E6%B2%81%E5%9B%AD%E6%98%A5%C2%B7%E9%9B%AA%E3%80%8B%E6%8E%88%E8%AF%BE%E8%A7%A3%E6%9E%90.pptx";
|
||||||
|
emit('addSuccess',{url})
|
||||||
|
props.dataList.filter(item => {
|
||||||
|
inputRequire.value += item.answer
|
||||||
|
})
|
||||||
|
getBackground();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ai-container {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #f5f7f6;
|
||||||
|
padding: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-box {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card1 {
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paragraphs {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
text-align: left;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid #409EFF;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
.themes {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
border: 1px solid #409EFF;
|
||||||
|
padding: 10px;
|
||||||
|
outline-style: none;
|
||||||
|
/* margin: 5px */
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-row>.el-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-row>.el-col>div,
|
||||||
|
.outline-row>.el-col>div>.el-input {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-with-dash {
|
||||||
|
margin-left: 100px
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-with-dash::after {
|
||||||
|
content: "";
|
||||||
|
border-bottom: 1px dashed #000;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-content-1 {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #c2dbf3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-content-2 {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
padding: 20px
|
||||||
|
}
|
||||||
|
:deep(.el-card__body){
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,391 @@
|
||||||
|
<template>
|
||||||
|
<div class="ai-container">
|
||||||
|
<el-steps style="max-width:100% " :active="activeStep" align-center>
|
||||||
|
<el-step title="生成大纲" />
|
||||||
|
<el-step title="选择模板" />
|
||||||
|
<el-step title="制作PPT" />
|
||||||
|
</el-steps>
|
||||||
|
<div class="card-box">
|
||||||
|
<el-card class="card2" v-if="activeStep === 0">
|
||||||
|
<div class="paragraphs">
|
||||||
|
{{ outputText }}
|
||||||
|
</div>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="addMessage">从新生成</el-button>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 1">下一步</el-button>
|
||||||
|
</el-card>
|
||||||
|
<el-card v-if="activeStep === 1">
|
||||||
|
<div style="padding-bottom: 10px">ppt模板选择</div>
|
||||||
|
<div class="themes">
|
||||||
|
<div v-for="item in backGroundList" :key="item.templateIndexId" :style="{
|
||||||
|
padding: '5px',
|
||||||
|
paddingRight: '5px',
|
||||||
|
paddingLeft: '5px',
|
||||||
|
margin: '5px',
|
||||||
|
backgroundColor: getBackgroundColor(item.templateIndexId),
|
||||||
|
borderRadius: '10px',
|
||||||
|
borderBlock: '10px solid #e6e6e6'
|
||||||
|
}" @click="chooseBackground(item.templateIndexId)" @mouseenter="changeCursor('pointer')" @mouseleave="changeCursor('default')">
|
||||||
|
{{ item.name }}
|
||||||
|
<img style="width: 150px; height: auto" :src="getBackGroundImg(item.detailImage)" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-row class="el-row">
|
||||||
|
<!-- <el-col :span="6" class="el-col">
|
||||||
|
<div class="grid-content-1">
|
||||||
|
<div>演讲备注</div>
|
||||||
|
<el-switch v-model="outlineData.is_card_note" />
|
||||||
|
</div>
|
||||||
|
</el-col>-->
|
||||||
|
<!-- <el-col :span="6" class="el-col">
|
||||||
|
<div class="grid-content-2">
|
||||||
|
<div>生成封面</div>
|
||||||
|
<el-switch v-model="outlineData.is_cover_img" />
|
||||||
|
</div>
|
||||||
|
</el-col>-->
|
||||||
|
<el-col :span="6" class="el-col">
|
||||||
|
<div class="grid-content-1">
|
||||||
|
<div>自动配图</div>
|
||||||
|
<el-switch v-model="outlineData.isFigure" />
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6" class="el-col">
|
||||||
|
<div class="grid-content-2">
|
||||||
|
<div>PPT作者名:</div>
|
||||||
|
<el-input v-model="outlineData.author" style="width: 50%" />
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 0">上一步</el-button>
|
||||||
|
<el-button style="margin-bottom: 5px;" type="primary" @click="outlineCreatePPT()">生成PPT</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<el-card v-if="activeStep === 2">
|
||||||
|
<el-progress :percentage="30" type="circle" v-if="percentage === 30"></el-progress>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getBackGroundV2,
|
||||||
|
createPPTV2,
|
||||||
|
getProgressV2,
|
||||||
|
} from "@/utils/ppt-request.js";
|
||||||
|
import CryptoJS from "crypto-js"
|
||||||
|
|
||||||
|
import { getSignature } from "@/utils/index.js";
|
||||||
|
|
||||||
|
let appId = "01ec9aa3";
|
||||||
|
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
|
||||||
|
let apikey = "39d05b269fa229f431a56c21794a8ea5"
|
||||||
|
let timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
let signature = getSignature(appId, secret, timestamp);
|
||||||
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
|
||||||
|
|
||||||
|
const outputText = ref(""); // 用于展示的大纲数据
|
||||||
|
const stagingData = ref([]); //储存的对话数据,用于多轮对话
|
||||||
|
const stagOutputText = ref(""); // 暂存大纲用于拆分
|
||||||
|
let extractedParts = ref([]) // 初步拆分
|
||||||
|
|
||||||
|
let firstArray = ref([]); //大纲的大纲等级数字部分
|
||||||
|
let secondArray = ref([]); //大纲的文字部分
|
||||||
|
|
||||||
|
|
||||||
|
const backGroundList = ref([]);
|
||||||
|
|
||||||
|
const inputTheme = ref("高中语文《沁园春雪》的授课课件"); // 输入的主题
|
||||||
|
const inputRequire = ref("") // 输入的需求
|
||||||
|
const activeStep = ref(0); // 上方进度条
|
||||||
|
const combined = ref('') // 修改完毕的大纲数据,准备传入ppt生成模型
|
||||||
|
|
||||||
|
const treeData = ref([]);
|
||||||
|
const status = ref("init");
|
||||||
|
|
||||||
|
const percentage = ref(0);
|
||||||
|
|
||||||
|
const getBackgrounds = () => {
|
||||||
|
treeData.value = [];
|
||||||
|
getBackGroundV2().then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
backGroundList.value = res.records;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBackGroundImg = (imgUrlStr) => {
|
||||||
|
return JSON.parse(imgUrlStr).titleCoverImage
|
||||||
|
};
|
||||||
|
|
||||||
|
const outlineData = ref({
|
||||||
|
query: '', // 用户要求(最多8000字)
|
||||||
|
// templateId: 'auto', // ppt生成主题
|
||||||
|
author: 'AIX平台',
|
||||||
|
isFigure: false, // 是否自动配图
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits(['addSuccess'])
|
||||||
|
const props = defineProps({
|
||||||
|
dataList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 将输入数据或返回数据存入记忆中
|
||||||
|
function updateStagingData(role, newData) {
|
||||||
|
stagingData.value.push({ role: role, content: newData });
|
||||||
|
}
|
||||||
|
//大纲直接生成ppt
|
||||||
|
const outlineCreatePPT = () => {
|
||||||
|
const newOutlineData = { ...outlineData.value, };
|
||||||
|
newOutlineData.query = outputText.value;
|
||||||
|
|
||||||
|
createPPTV2(newOutlineData).then((res) => {
|
||||||
|
console.log(res, "正在生成中");
|
||||||
|
activeStep.value = 2
|
||||||
|
|
||||||
|
const checkProgress = () => {
|
||||||
|
getProgressV2(res.sid).then((response) => {
|
||||||
|
percentage.value = response.process;
|
||||||
|
if (response && response.pptUrl && response.pptUrl.length > 4) {
|
||||||
|
console.log('PPT',response)
|
||||||
|
// window.location.href = response.data.pptUrl;
|
||||||
|
//发消息到主进程,携带名称和URL,将URL下载下来后复制到文件列表并上传到服务
|
||||||
|
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFiledf28bf990a4c40ffb7477ed4b65392c27232357022409613439/%E3%80%8A%E9%9D%99%E5%A5%B3%E3%80%8B%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BB%E4%B8%8E%E7%A0%94%E7%A9%B6.pptx"
|
||||||
|
emit('addSuccess',res)
|
||||||
|
ElMessage.success("生成成功");
|
||||||
|
} else {
|
||||||
|
const sleepTime = 2000;
|
||||||
|
let remainingTime = sleepTime;
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
remainingTime -= 100;
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
checkProgress();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkProgress();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
//初次对话
|
||||||
|
const addMessage = () => {
|
||||||
|
const themeValue = inputTheme.value;
|
||||||
|
const requireValue = inputRequire.value;
|
||||||
|
firstArray.value = []
|
||||||
|
secondArray.value = []
|
||||||
|
extractedParts.value = []
|
||||||
|
stagOutputText.value = ''
|
||||||
|
const combinedString = `请帮我生成一个ppt大纲,主题为:${themeValue}。具体内容要求为:${requireValue}。注意,用三个等级大纲展示,如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
|
||||||
|
updateStagingData("user", combinedString);
|
||||||
|
connectWebSocket(stagingData.value);
|
||||||
|
// activeStep.value = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
let ttsWS
|
||||||
|
function connectWebSocket(data) {
|
||||||
|
outputText.value = ""; //清楚展示部分内容
|
||||||
|
status.value = "ttsing";
|
||||||
|
return getWebsocketUrl().then((url) => {
|
||||||
|
ttsWS = new WebSocket(url);
|
||||||
|
ttsWS.onopen = () => {
|
||||||
|
webSocketSend(ttsWS, data);
|
||||||
|
};
|
||||||
|
ttsWS.onmessage = (e) => {
|
||||||
|
result1(e.data);
|
||||||
|
};
|
||||||
|
ttsWS.onerror = (e) => {
|
||||||
|
status.value = "error";
|
||||||
|
console.log("WebSocket error:", e);
|
||||||
|
};
|
||||||
|
ttsWS.onclose = () => {
|
||||||
|
status.value = "init";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBackgroundColor = (key) => {
|
||||||
|
return outlineData.value.templateId === key ? '#83e2b6' : '#f5f5f5';
|
||||||
|
};
|
||||||
|
|
||||||
|
function getWebsocketUrl() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var apiKey = apikey;
|
||||||
|
var apiSecret = secret;
|
||||||
|
var url = "wss://spark-api.xf-yun.com/v4.0/chat";
|
||||||
|
|
||||||
|
var host = "spark-api.xf-yun.com";
|
||||||
|
var date = new Date().toGMTString();
|
||||||
|
var algorithm = "hmac-sha256";
|
||||||
|
var headers = "host date request-line";
|
||||||
|
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v4.0/chat HTTP/1.1`;
|
||||||
|
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
|
||||||
|
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
|
||||||
|
|
||||||
|
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
|
||||||
|
var authorization = CryptoJS.enc.Base64.stringify(
|
||||||
|
CryptoJS.enc.Utf8.parse(authorizationOrigin)
|
||||||
|
);
|
||||||
|
|
||||||
|
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
|
||||||
|
console.log(url);
|
||||||
|
resolve(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function webSocketSend(ws, data) {
|
||||||
|
const params = {
|
||||||
|
header: {
|
||||||
|
app_id: appId,
|
||||||
|
},
|
||||||
|
parameter: {
|
||||||
|
chat: {
|
||||||
|
domain: "4.0Ultra",
|
||||||
|
temperature: 0.5,
|
||||||
|
max_tokens: 1024,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
message: {
|
||||||
|
text: data,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ws.send(JSON.stringify(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
function result1(resultData) {
|
||||||
|
let jsonData = JSON.parse(resultData);
|
||||||
|
console.log(jsonData)
|
||||||
|
outputText.value += jsonData.payload.choices.text[0].content;
|
||||||
|
const div = document.querySelector('.paragraphs');
|
||||||
|
if (div) {
|
||||||
|
div.scrollTop = div.scrollHeight;
|
||||||
|
}
|
||||||
|
if (jsonData.payload && jsonData.payload.usage) {
|
||||||
|
updateStagingData("assistant", outputText.value) //返回数据存入记忆池
|
||||||
|
}
|
||||||
|
if (jsonData.header.code !== 0) {
|
||||||
|
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chooseBackground = (data) => {
|
||||||
|
outlineData.value.templateId = data
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCursor = (cursorStyle) => {
|
||||||
|
document.documentElement.style.cursor = cursorStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx"
|
||||||
|
// creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => {
|
||||||
|
// emit('addSuccess',res)
|
||||||
|
// })
|
||||||
|
// connectWebSocket("init");
|
||||||
|
props.dataList.filter(item => {
|
||||||
|
inputRequire.value += item.answer
|
||||||
|
})
|
||||||
|
getBackgrounds();
|
||||||
|
// addMessage()
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ai-container {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #f5f7f6;
|
||||||
|
padding: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-box {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card1 {
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paragraphs {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
text-align: left;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid #409EFF;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
.themes {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
border: 1px solid #409EFF;
|
||||||
|
padding: 10px;
|
||||||
|
outline-style: none;
|
||||||
|
/* margin: 5px */
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-row>.el-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-row>.el-col>div,
|
||||||
|
.outline-row>.el-col>div>.el-input {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-with-dash {
|
||||||
|
margin-left: 100px
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-with-dash::after {
|
||||||
|
content: "";
|
||||||
|
border-bottom: 1px dashed #000;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-content-1 {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #c2dbf3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-content-2 {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
padding: 20px
|
||||||
|
}
|
||||||
|
:deep(.el-card__body){
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog class="ppt-dialog" v-model="model" :show-close="false" width="800" destroy-on-close :top="'3vh'">
|
||||||
|
<template #header="{ close, titleId, titleClass }">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<h4 :id="titleId" :class="titleClass">生成PPT(试验版)</h4>
|
||||||
|
<i class="iconfont icon-guanbi" @click="close"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<AiPptist @add-success="addAiPPT" :dataList="dataList"/>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import AiPptist from './ai-pptist.vue';
|
||||||
|
const model = defineModel()
|
||||||
|
const emit = defineEmits(['addSuccess'])
|
||||||
|
const props = defineProps({
|
||||||
|
dataList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const addAiPPT = (data) => {
|
||||||
|
emit('addSuccess', data)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.ppt-dialog){
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
.dialog-header{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.icon-guanbi {
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -42,6 +42,7 @@ import useUserStore from '@/store/modules/user'
|
||||||
import {ArrowDown} from '@element-plus/icons-vue'
|
import {ArrowDown} from '@element-plus/icons-vue'
|
||||||
import { onMounted,ref } from 'vue';
|
import { onMounted,ref } from 'vue';
|
||||||
import { listEvaluation } from '@/api/subject/index'
|
import { listEvaluation } from '@/api/subject/index'
|
||||||
|
import {sessionStore} from '@/utils/store'
|
||||||
const userStore = useUserStore().user
|
const userStore = useUserStore().user
|
||||||
const subjectList = ref([])
|
const subjectList = ref([])
|
||||||
// 获取基础的学科
|
// 获取基础的学科
|
||||||
|
@ -58,22 +59,27 @@ const getSubject = () => {
|
||||||
// 选择学段
|
// 选择学段
|
||||||
const handleUserEduStage = (item) => {
|
const handleUserEduStage = (item) => {
|
||||||
userStore.edustage = item
|
userStore.edustage = item
|
||||||
|
sessionStore.set('edustageSelf',item)
|
||||||
if(item === '幼儿园'){
|
if(item === '幼儿园'){
|
||||||
// 默认语文
|
// 默认语文
|
||||||
userStore.edusubject = '语文'
|
userStore.edusubject = '语文'
|
||||||
|
sessionStore.set('edusubjectSelf','语文')
|
||||||
}
|
}
|
||||||
else if(item === '高中' && userStore.edusubject === "道德与法治"){
|
else if(item === '高中' && userStore.edusubject === "道德与法治"){
|
||||||
// 默认语文
|
// 默认语文
|
||||||
userStore.edusubject = '政治'
|
userStore.edusubject = '政治'
|
||||||
|
sessionStore.set('edusubjectSelf','政治')
|
||||||
}
|
}
|
||||||
else if(item != '高中' && userStore.edusubject === "政治"){
|
else if(item != '高中' && userStore.edusubject === "政治"){
|
||||||
// 默认语文
|
// 默认语文
|
||||||
userStore.edusubject = '道德与法治'
|
userStore.edusubject = '道德与法治'
|
||||||
|
sessionStore.set('edusubjectSelf','道德与法治')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 选择学科
|
// 选择学科
|
||||||
const handleUserEduSubject = (item) => {
|
const handleUserEduSubject = (item) => {
|
||||||
userStore.edusubject = item;
|
userStore.edusubject = item;
|
||||||
|
sessionStore.set('edusubjectSelf',item)
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSubject()
|
getSubject()
|
||||||
|
|
|
@ -40,6 +40,9 @@ import {listClassmain} from '@/api/classManage/index'
|
||||||
//班级列表
|
//班级列表
|
||||||
import ClassList from './components/classList.vue'
|
import ClassList from './components/classList.vue'
|
||||||
|
|
||||||
|
import useClassTaskStore from '@/store/modules/classTask'
|
||||||
|
const classTaskStore = useClassTaskStore();
|
||||||
|
|
||||||
//班级列表
|
//班级列表
|
||||||
const classList = ref([])
|
const classList = ref([])
|
||||||
// 未加入的班级
|
// 未加入的班级
|
||||||
|
@ -162,11 +165,12 @@ setTimeout(() => {
|
||||||
function submit() {
|
function submit() {
|
||||||
proxy.$refs.userRef.validate((valid) => {
|
proxy.$refs.userRef.validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
userStore.user.avatar = userStore.user.avatar
|
userStore.user.edusubject = sessionStore.get('edusubjectSelf') ? sessionStore.get('edusubjectSelf') : userStore.user.edusubject
|
||||||
|
userStore.user.edustage = sessionStore.get('edustageSelf') ? sessionStore.get('edustageSelf') : userStore.user.edustage
|
||||||
updateUserInfo(userStore.user).then((response) => {
|
updateUserInfo(userStore.user).then((response) => {
|
||||||
if(response.code == 200){
|
if(response.code == 200){
|
||||||
userStore.login({username:userStore.user.userName,password:userStore.user.plainpwd}).then(() => {
|
userStore.login({username:userStore.user.userName,password:userStore.user.plainpwd}).then(() => {
|
||||||
userStore.getInfo().then(res => {
|
userStore.getInfo().then(async res => {
|
||||||
if(res.code === 200){
|
if(res.code === 200){
|
||||||
let sessionSubject = {
|
let sessionSubject = {
|
||||||
bookList: null,
|
bookList: null,
|
||||||
|
@ -177,6 +181,10 @@ function submit() {
|
||||||
}
|
}
|
||||||
sessionStore.set( 'subject', sessionSubject)
|
sessionStore.set( 'subject', sessionSubject)
|
||||||
ElMessage.success('修改成功')
|
ElMessage.success('修改成功')
|
||||||
|
|
||||||
|
console.log('userStore更新后', res);
|
||||||
|
await classTaskStore.initJYInfo(userStore.user);
|
||||||
|
//console.log('classTaskStore->', classTaskStore);
|
||||||
}else{
|
}else{
|
||||||
ElMessage.error(response.msg)
|
ElMessage.error(response.msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,15 @@
|
||||||
</el-row> -->
|
</el-row> -->
|
||||||
<el-row class="resoure-btns">
|
<el-row class="resoure-btns">
|
||||||
<el-col :span="24" class="query-row flex">
|
<el-col :span="24" class="query-row flex">
|
||||||
<div class="flex row-left"> <el-select v-model="sourceStore.query.fileSuffix" @change="sourceStore.changeSuffix"
|
<div class="flex row-left">
|
||||||
|
<!-- 第三方资源筛选-->
|
||||||
|
<el-select v-if="isThird" v-model="sourceStore.thirdQuery.type" @change="sourceStore.thirdChangeType"
|
||||||
|
style="width: 110px">
|
||||||
|
<el-option v-for="item in coursewareTypeList" :key="item.value" :label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<el-select v-else v-model="sourceStore.query.fileSuffix" @change="sourceStore.changeSuffix"
|
||||||
style="width: 110px">
|
style="width: 110px">
|
||||||
<el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label"
|
<el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label"
|
||||||
:value="item.value" />
|
:value="item.value" />
|
||||||
|
@ -48,7 +56,6 @@
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,22 @@
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<el-button type="primary" link @click="onSelect(item)">选择模式</el-button>
|
<el-button type="primary" link @click="onSelect(item)">选择模式</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-scrollbar>
|
<div class="content-list">
|
||||||
<div class="item-list flex">
|
<div class="item-list flex">
|
||||||
<el-card class="item-card" shadow="never" v-for="el in item.child" :key="el.id">
|
<el-card class="item-card" shadow="never" v-for="el in item.child" :key="el.id">
|
||||||
<p class="card-name">{{ el.name }}</p>
|
<p class="card-name">
|
||||||
<div class="card-text">
|
<el-text line-clamp="1" :title="el.name">
|
||||||
<el-text line-clamp="2">
|
{{ el.name }}
|
||||||
{{ el.prompt }}
|
</el-text>
|
||||||
</el-text>
|
</p>
|
||||||
</div>
|
<div class="card-text">
|
||||||
</el-card>
|
<el-text line-clamp="4" :title="el.prompt">
|
||||||
</div>
|
{{ el.prompt }}
|
||||||
</el-scrollbar>
|
</el-text>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,30 +101,44 @@ onMounted(() => {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.con-item {
|
.con-item {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
.item-list{
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-card {
|
.item-card {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 10px;
|
||||||
:deep(.el-card__body) {
|
:deep(.el-card__body) {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-name {
|
.card-name {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-text {
|
.card-text {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 12px;
|
|
||||||
|
.el-text{
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.content-list{
|
||||||
|
overflow-x: auto
|
||||||
|
}
|
||||||
|
.content-list::-webkit-scrollbar {
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -11,7 +11,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<el-button type="primary">生成大纲</el-button>
|
<el-button type="primary">生成大纲</el-button>
|
||||||
<el-button type="danger">生成PPT</el-button>
|
<el-button type="danger" @click="pptDialog = true">生成PPT</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-con flex">
|
<div class="right-con flex">
|
||||||
|
@ -49,29 +49,112 @@
|
||||||
</div>
|
</div>
|
||||||
<EditDialog v-model="isEdit" :item="curItem" />
|
<EditDialog v-model="isEdit" :item="curItem" />
|
||||||
<AdjustDialog v-model="isAdjust" :item="curItem" />
|
<AdjustDialog v-model="isAdjust" :item="curItem" />
|
||||||
|
<PptDialog @add-success="addAiPPT" :dataList="resultList" v-model="pptDialog"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onUnmounted, reactive } from 'vue'
|
import { ref, onMounted, onUnmounted, reactive } from 'vue'
|
||||||
|
import { sessionStore } from '@/utils/store'
|
||||||
import emitter from '@/utils/mitt'
|
import emitter from '@/utils/mitt'
|
||||||
import EditDialog from './edit-dialog.vue'
|
import EditDialog from './edit-dialog.vue'
|
||||||
import AdjustDialog from './adjust-dialog.vue'
|
import AdjustDialog from './adjust-dialog.vue'
|
||||||
import { completion } from '@/api/mode/index.js'
|
import { completion, tempResult } from '@/api/mode/index.js'
|
||||||
|
// import { dataSetJson } from '@/utils/comm.js'
|
||||||
|
import * as commUtils from '@/utils/comm.js'
|
||||||
|
import PptDialog from '@/views/prepare/container/pptist-dialog.vue'
|
||||||
|
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import {PPTXFileToJson} from '@/AixPPTist/src/hooks/useImport' // ppt转json
|
||||||
|
import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
|
||||||
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
|
import * as Api_server from '@/api/apiService' // 相关api
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const pptDialog = ref(false)
|
||||||
const resultList = ref([])
|
const resultList = ref([])
|
||||||
emitter.on('changeMode', (item) => {
|
const courseObj = reactive({
|
||||||
resultList.value = item.child
|
node: null, // 选择的课程节点
|
||||||
conversation()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
emitter.on('changeMode', (item) => {
|
||||||
|
console.log(item, 'item')
|
||||||
|
resultList.value = item.child
|
||||||
|
// conversation()
|
||||||
|
getTempResult(item.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// 查询模板结果
|
||||||
|
const getTempResult = (id) => {
|
||||||
|
tempResult({ mainModelId: id }).then(res => {
|
||||||
|
console.log(res, 2000)
|
||||||
|
let rows = res.rows
|
||||||
|
if (rows.length > 0) {
|
||||||
|
resultList.value.forEach(item => {
|
||||||
|
rows.forEach(el => {
|
||||||
|
if (item.id == el.modelId) {
|
||||||
|
item.answer = el.content
|
||||||
|
item.reultId = el.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const params = reactive(
|
||||||
|
{
|
||||||
|
prompt: '',
|
||||||
|
dataset_id: ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const addAiPPT = async(res) => {
|
||||||
|
let node = courseObj.node
|
||||||
|
if (!node) return msgUtils.msgWarning('请选择章节?')
|
||||||
|
//TODO res中有PPT地址
|
||||||
|
const params = { evalid: node.id, edituserid: userStore.id, pageSize: 1 }
|
||||||
|
const resEnpt = await HTTP_SERVER_API('getCourseList', params)
|
||||||
|
if (!(resEnpt?.rows?.[0] || null)) { // 创建
|
||||||
|
const resid = await HTTP_SERVER_API('addEntpcourse')
|
||||||
|
courseObj.entp.id = resid
|
||||||
|
} else courseObj.entp = resEnpt?.rows?.[0] || null
|
||||||
|
// 下载PPT 并解析json转换到我们自己数据库
|
||||||
|
fetch(res.url)
|
||||||
|
.then(res => res.arrayBuffer())
|
||||||
|
.then(async buffer => {
|
||||||
|
const resPptJson = await PPTXFileToJson(buffer)
|
||||||
|
const { def, slides, ...content } = resPptJson
|
||||||
|
// 转换图片|音频|视频 为线上地址
|
||||||
|
for( let o of slides ) {
|
||||||
|
await toRousrceUrl(o)
|
||||||
|
}
|
||||||
|
// return
|
||||||
|
// 生成ppt课件-父级
|
||||||
|
const p_params = {parentContent: JSON.stringify(content)}
|
||||||
|
const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
||||||
|
if (!!parentid??null) { // 生成内容幻灯片
|
||||||
|
if (slides.length > 0) {
|
||||||
|
const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide))
|
||||||
|
const params = {parentid, filetype: 'slide', title: '', slides: resSlides }
|
||||||
|
const res_3 = await HTTP_SERVER_API('batchAddNew', params)
|
||||||
|
if (res_3 && res_3.code == 200) {
|
||||||
|
msgUtils.msgSuccess('生成PPT课件成功')
|
||||||
|
} else {
|
||||||
|
msgUtils.msgWarning('生成PPT课件失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
const conversation = async () => {
|
const conversation = async () => {
|
||||||
for (let item of resultList.value) {
|
for (let item of resultList.value) {
|
||||||
item.loading = true
|
item.loading = true
|
||||||
try {
|
try {
|
||||||
const { data } = await completion({
|
params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}课标对${curNode.itemtitle}进行教学分析`
|
||||||
dataset_id: 'cee3062a9fcf11efa6910242ac140006',
|
const { data } = await completion(params)
|
||||||
prompt: item.prompt
|
|
||||||
})
|
|
||||||
item.answer = data.answer
|
item.answer = data.answer
|
||||||
} finally {
|
} finally {
|
||||||
item.loading = false
|
item.loading = false
|
||||||
|
@ -89,10 +172,8 @@ const curItem = reactive({})
|
||||||
const againResult = async (index, item) => {
|
const againResult = async (index, item) => {
|
||||||
try {
|
try {
|
||||||
resultList.value[index].loading = true
|
resultList.value[index].loading = true
|
||||||
const { data } = await completion({
|
params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}课标对${curNode.itemtitle}进行教学分析`
|
||||||
dataset_id: 'cee3062a9fcf11efa6910242ac140006',
|
const { data } = await completion(params)
|
||||||
prompt: item.prompt
|
|
||||||
})
|
|
||||||
resultList.value[index].answer = data.answer
|
resultList.value[index].answer = data.answer
|
||||||
} finally {
|
} finally {
|
||||||
resultList.value[index].loading = false
|
resultList.value[index].loading = false
|
||||||
|
@ -101,12 +182,12 @@ const againResult = async (index, item) => {
|
||||||
|
|
||||||
// 对话调整
|
// 对话调整
|
||||||
const isAdjust = ref(false)
|
const isAdjust = ref(false)
|
||||||
const onAdjust = (index, item) => {
|
const onAdjust = (index, item) => {
|
||||||
curIndex.value = index
|
curIndex.value = index
|
||||||
Object.assign(curItem, item)
|
Object.assign(curItem, item)
|
||||||
isAdjust.value = true
|
isAdjust.value = true
|
||||||
}
|
}
|
||||||
emitter.on('changeAdjust', (item) =>{
|
emitter.on('changeAdjust', (item) => {
|
||||||
resultList.value[curIndex.value].answer = item
|
resultList.value[curIndex.value].answer = item
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -121,6 +202,109 @@ emitter.on('changeResult', (item) => {
|
||||||
resultList.value[curIndex.value].answer = item
|
resultList.value[curIndex.value].answer = item
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ======== zdg start ============
|
||||||
|
// 统一HTTP处理
|
||||||
|
const HTTP_SERVER_API = (type, params = {}) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'addEntpcourse': { // 添加课程
|
||||||
|
const node = courseObj.node || {}
|
||||||
|
if (!node) return msgUtils.msgWarning('请选择章节?')
|
||||||
|
const def = { // 默认参数
|
||||||
|
entpid: userStore.user.deptId, // 部门id
|
||||||
|
level: 1, // 层级
|
||||||
|
parentid: 0, // 父级id
|
||||||
|
dictid: 0, // 字典id
|
||||||
|
evalid: node.id, // 章节id
|
||||||
|
evalparentid: node.parentid, // 单元id(父级id)
|
||||||
|
edusubject: node.edusubject, // 学科
|
||||||
|
edudegree: node.edudegree, // 年级
|
||||||
|
edustage: node.edustage, // 阶段
|
||||||
|
coursetype: '课标学科', // 课程类型
|
||||||
|
coursetitle: node.itemtitle, // 课程名称
|
||||||
|
coursedesc: '', // 课程描述
|
||||||
|
status: '', // 状态
|
||||||
|
dflag: 0, // 状态
|
||||||
|
edituserid: userStore.id, // 编辑人id
|
||||||
|
createblankfile: 'no', // 创建空白文件
|
||||||
|
}
|
||||||
|
courseObj.entp = def
|
||||||
|
return API_entpcourse.addEntpcourse(def)
|
||||||
|
}
|
||||||
|
case 'addEntpcoursefile': { // 添加课程文件
|
||||||
|
params = getDefParams(params)
|
||||||
|
return API_entpcoursefile.addEntpcoursefileReturnId(params)
|
||||||
|
}
|
||||||
|
case 'batchAddNew': { // 批量添加课程文件
|
||||||
|
params = getDefParams(params)
|
||||||
|
return API_entpcoursefile.batchAddNew(params)
|
||||||
|
}
|
||||||
|
case 'getCourseList': { // 获取课程列表
|
||||||
|
return API_entpcourse.listEntpcourse(params)
|
||||||
|
}
|
||||||
|
case 'getCourseFileList':{ // 获取课程文件列表
|
||||||
|
return API_entpcoursefile.listEntpcoursefileNew(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取默认参数
|
||||||
|
const getDefParams = (params) => {
|
||||||
|
const enpt = courseObj.entp
|
||||||
|
const def = {
|
||||||
|
parentid: 0,
|
||||||
|
entpid: userStore.user.deptId,
|
||||||
|
entpcourseid: enpt.id,
|
||||||
|
ppttype: 'file',
|
||||||
|
title: enpt.coursetitle,
|
||||||
|
fileurl: '',
|
||||||
|
filetype: 'aptist',
|
||||||
|
datacontent: '',
|
||||||
|
filekey: '',
|
||||||
|
filetag: '',
|
||||||
|
fileidx: 0,
|
||||||
|
dflag: 0,
|
||||||
|
status: '',
|
||||||
|
edituserid: userStore.id
|
||||||
|
}
|
||||||
|
return Object.assign(def, params)
|
||||||
|
}
|
||||||
|
// 图片|音频|视频 转换为在线地址
|
||||||
|
const toRousrceUrl = async(o) => {
|
||||||
|
if (!!o.src) { // 如果有src就转换
|
||||||
|
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
|
||||||
|
const isBlobUrl = /^blob:/.test(o.src)
|
||||||
|
console.log('isBase64', o, isBase64)
|
||||||
|
if (isBase64) {
|
||||||
|
const bolb = commUtils.base64ToBlob(o.src)
|
||||||
|
const fileName = Date.now() + '.png'
|
||||||
|
const file = commUtils.blobToFile(bolb, fileName)
|
||||||
|
// o.src = fileName
|
||||||
|
// console.log('file', file)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const res = await Api_server.Other.uploadFile(formData)
|
||||||
|
if (res && res.code == 200){
|
||||||
|
const url = res?.url
|
||||||
|
url &&(o.src = url)
|
||||||
|
}
|
||||||
|
} else if (isBlobUrl) { // 视频和音频
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
||||||
|
if (o?.elements) o.elements.forEach(async o => {await toRousrceUrl(o)})
|
||||||
|
}
|
||||||
|
// ======== zdg end ============
|
||||||
|
|
||||||
|
const curNode = reactive({})
|
||||||
|
onMounted(() => {
|
||||||
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
Object.assign(curNode, data);
|
||||||
|
courseObj.node = data
|
||||||
|
|
||||||
|
let jsonKey = `课标-${data.edustage}-${data.edusubject}`
|
||||||
|
params.dataset_id = commUtils.dataSetJson[jsonKey]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
// 解绑
|
// 解绑
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -204,4 +388,4 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"../*": ["./src/*"]
|
"../*": ["./src/*"],
|
||||||
|
"@/*": [
|
||||||
|
"./src/renderer/src/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
{
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*":["src/renderer/src/*"],
|
||||||
|
"@root/*":["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
"files": [],
|
"files": [],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue