Compare commits

..

44 Commits

Author SHA1 Message Date
lyc 1bafc24025 Merge pull request 'lyc-dev' (#242) from lyc-dev into main
Reviewed-on: #242
2025-02-21 17:30:29 +08:00
lyc 2797313612 冲突 2025-02-21 17:29:40 +08:00
lyc e007c30235 修复一些问题 2025-02-21 17:27:39 +08:00
lyc 3675de66f5 处理冲突 2025-02-21 16:55:53 +08:00
朱浩 a679a8fc44 Merge remote-tracking branch 'origin/main' 2025-02-21 16:42:12 +08:00
朱浩 5d7dc6fc80 文件上传插件开发 2025-02-21 16:42:01 +08:00
lyc c812eaee5f 修复一些问题 2025-02-21 16:38:41 +08:00
yangws 084da1f68d Merge pull request 'fix:作业管理分值问题;' (#241) from yws_dev into main
Reviewed-on: #241
2025-02-21 16:01:01 +08:00
小杨 7c377e0eaa fix:作业管理分值问题; 2025-02-21 16:00:30 +08:00
lyc d954c88ec5 edit 2025-02-21 00:15:20 +08:00
lyc a708eb59b5 edit 2025-02-20 17:27:59 +08:00
lyc f02f6a8384 edit:模型模板 2025-02-19 17:28:59 +08:00
朱浩 95d2059b9b 版本升级 2025-02-19 16:59:34 +08:00
朱浩 f238e37fdd 版本升级 2025-02-19 16:59:16 +08:00
朱浩 03897d2aa5 Merge remote-tracking branch 'origin/main' 2025-02-19 16:55:38 +08:00
yangws a01d7f08da Merge pull request 'fix:空格问题;' (#240) from yws_dev into main
Reviewed-on: #240
2025-02-19 16:27:50 +08:00
小杨 055746144d fix:空格问题; 2025-02-19 16:27:25 +08:00
yangws 06a2697ec2 Merge pull request 'fix:修改rootid;' (#239) from yws_dev into main
Reviewed-on: #239
2025-02-19 16:25:48 +08:00
小杨 fd43dde733 fix:修改rootid; 2025-02-19 16:25:22 +08:00
lyc 870ffb167a edit bookld 2025-02-19 16:18:36 +08:00
朱浩 0960599c73 Merge remote-tracking branch 'origin/main' 2025-02-19 10:35:22 +08:00
朱浩 a5579d4091 版本升级 2025-02-19 10:35:11 +08:00
zouyf d5c17eb17f Merge pull request 'zouyf_dev' (#238) from zouyf_dev into main
Reviewed-on: #238
2025-02-19 10:31:40 +08:00
“zouyf” f5c9cdc9de Merge branch 'main' into zouyf_dev 2025-02-19 10:27:22 +08:00
“zouyf” b5f43f1a37 去掉无自主试题的提示 2025-02-19 10:27:04 +08:00
lyc e27f7e0018 edit 2025-02-18 17:32:10 +08:00
zhangxuelin df906c52f6 Merge pull request 'zxl' (#237) from zxl into main
Reviewed-on: #237
2025-02-18 17:04:57 +08:00
zhangxuelin 7a64625567 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zxl 2025-02-18 17:04:22 +08:00
zhangxuelin f033155cc1 修改音频上传 2025-02-18 17:04:01 +08:00
朱浩 b3b1dde262 Merge remote-tracking branch 'origin/main' 2025-02-17 09:37:19 +08:00
朱浩 cbb3971c9b 版本升级 2025-02-17 09:37:10 +08:00
lyc 327e7d78ad Merge branch 'main' into lyc-dev 2025-02-17 09:30:08 +08:00
lyc e97ff8e147 修复一些问题 2025-02-16 23:13:30 +08:00
zhengdegang 38227cc282 Merge pull request 'zdg_dev' (#236) from zdg_dev into main
Reviewed-on: #236
2025-02-14 16:36:32 +08:00
zdg 433161ef13 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zdg_dev 2025-02-14 16:35:37 +08:00
zdg 0b2ed540b8 修复
1.上课视频无法控制
2.ppt导入图片远程上传异常
2025-02-14 16:35:28 +08:00
zhangxuelin 0af53f82f0 Merge pull request '新增学校管理展示判断' (#235) from zxl into main
Reviewed-on: #235
2025-02-13 14:33:47 +08:00
zhangxuelin 38df362a86 新增学校管理展示判断 2025-02-13 14:32:35 +08:00
朱浩 0beae0b035 打字机效率变更 2025-02-13 14:25:18 +08:00
朱浩 159cb75ca9 Merge remote-tracking branch 'origin/main' 2025-02-11 16:41:09 +08:00
朱浩 c69bc91185 去除32位打包兼容 2025-02-11 16:41:02 +08:00
朱浩 66bc555445 思维导图插件,酉阳版本升级 2025-02-11 16:40:16 +08:00
朱浩 35d8bdf13f 思维导图插件 2025-02-10 17:14:25 +08:00
lyc ba1a4a3abe Merge branch 'lyc-dev' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into lyc-dev 2025-01-25 02:50:30 +08:00
37 changed files with 1322 additions and 602 deletions

View File

@ -6,11 +6,11 @@ directories:
win: win:
executableName: 文枢课堂 executableName: 文枢课堂
icon: resources/logo2.ico icon: resources/logo2.ico
target: # target:
- target: nsis # - target: nsis
arch: # arch:
- x64 # - x64
- ia32 # - ia32
files: files:
- '!**/.vscode/*' - '!**/.vscode/*'
- '!src/*' - '!src/*'

View File

@ -6,11 +6,11 @@ directories:
win: win:
executableName: 永川中小学AI教学系统 executableName: 永川中小学AI教学系统
icon: resources/yc-logo.png icon: resources/yc-logo.png
target: # target:
- target: nsis # - target: nsis
arch: # arch:
- x64 # - x64
- ia32 # - ia32
files: files:
- '!**/.vscode/*' - '!**/.vscode/*'
- '!src/*' - '!src/*'

View File

@ -6,11 +6,11 @@ directories:
win: win:
executableName: 实训教学 executableName: 实训教学
icon: resources/yc-logo.png icon: resources/yc-logo.png
target: # target:
- target: nsis # - target: nsis
arch: # arch:
- x64 # - x64
- ia32 # - ia32
files: files:
- '!**/.vscode/*' - '!**/.vscode/*'
- '!src/*' - '!src/*'

View File

@ -6,11 +6,11 @@ directories:
win: win:
executableName: 育人酉数平台 executableName: 育人酉数平台
icon: resources/yy-logo.png icon: resources/yy-logo.png
target: # target:
- target: nsis # - target: nsis
arch: # arch:
- x64 # - x64
- ia32 # - ia32
files: files:
- '!**/.vscode/*' - '!**/.vscode/*'
- '!src/*' - '!src/*'

View File

@ -13,11 +13,11 @@ asarUnpack:
win: win:
executableName: AIx executableName: AIx
icon: resources/logo2.ico icon: resources/logo2.ico
target: # target:
- target: nsis # - target: nsis
arch: # arch:
- x64 # - x64
- ia32 # - ia32
nsis: nsis:
oneClick: false oneClick: false
allowToChangeInstallationDirectory: true allowToChangeInstallationDirectory: true

View File

@ -1,6 +1,6 @@
{ {
"name": "aix-win-ws", "name": "aix-win-ws",
"version": "2.5.16", "version": "2.5.20",
"description": "", "description": "",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "上海交大重庆人工智能研究院", "author": "上海交大重庆人工智能研究院",
@ -44,7 +44,7 @@
"@vue-office/excel": "^1.7.11", "@vue-office/excel": "^1.7.11",
"@vue-office/pdf": "^2.0.2", "@vue-office/pdf": "^2.0.2",
"@vueuse/core": "^10.11.0", "@vueuse/core": "^10.11.0",
"aix-plugins-aitools": "^1.1.5", "aix-plugins-aitools": "^1.1.24",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"circular-json": "^0.5.9", "circular-json": "^0.5.9",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -94,7 +94,7 @@ const insertImageElementaudio = (files: FileList) => {
const imageFile = files[0] const imageFile = files[0]
if (!imageFile) return if (!imageFile) return
PPTApi.toRousrceUrl(imageFile).then(data=>{ PPTApi.toRousrceUrl(imageFile).then(data=>{
videoSrc.value=data audioSrc.value=data
insertAudio() insertAudio()
}) })

View File

@ -80,6 +80,7 @@ provide(injectKeySlideScale, scale)
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
} }
.slide-item { .slide-item {
position: absolute; position: absolute;

View File

@ -198,7 +198,7 @@ export default (isLoader?: boolean = true) => {
const touchInfo = ref<{ x: number; y: number; } | null>(null) const touchInfo = ref<{ x: number; y: number; } | null>(null)
const touchStartListener = (e: TouchEvent) => { const touchStartListener = (e: TouchEvent) => {
e.preventDefault() // 阻止默认事件 // e.preventDefault() // 阻止默认事件
touchInfo.value = { touchInfo.value = {
// x: e.changedTouches[0].pageX, // x: e.changedTouches[0].pageX,
// y: e.changedTouches[0].pageY, // y: e.changedTouches[0].pageY,
@ -208,6 +208,7 @@ export default (isLoader?: boolean = true) => {
} }
const touchEndListener = (e: TouchEvent) => { const touchEndListener = (e: TouchEvent) => {
if (!touchInfo.value) return if (!touchInfo.value) return
// window.scrollTo(0, 0) // 滚动到顶部
const offsetX = Math.abs(touchInfo.value.x - e.changedTouches[0].clientX) const offsetX = Math.abs(touchInfo.value.x - e.changedTouches[0].clientX)
const offsetY = e.changedTouches[0].clientY - touchInfo.value.y const offsetY = e.changedTouches[0].clientY - touchInfo.value.y
if ( Math.abs(offsetY) > offsetX && Math.abs(offsetY) > 50 ) { if ( Math.abs(offsetY) > offsetX && Math.abs(offsetY) > 50 ) {

6
src/renderer/src/api/file/index.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
declare module '@/api/file' {
// 定义 uploadFile 函数的类型
// export function uploadFile(file: File): Promise<any>;
export function sessionToken(): Promise<any>;
export function uploadSingleFileToEos(data: SingleUploadData): Promise<any>;
}

View File

@ -110,3 +110,11 @@ export const sessionToken = () => {
method: 'get' method: 'get'
}) })
} }
export const uploadSingleFileToEos = (params) => {
return request({
url: '/common/uploadSingleFileToEos',
method: 'post',
params
})
}

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4723712 */ font-family: "iconfont"; /* Project id 4723712 */
src: url('iconfont.woff2?t=1737434703828') format('woff2'), src: url('iconfont.woff2?t=1739948469020') format('woff2'),
url('iconfont.woff?t=1737434703828') format('woff'), url('iconfont.woff?t=1739948469020') format('woff'),
url('iconfont.ttf?t=1737434703828') format('truetype'); url('iconfont.ttf?t=1739948469020') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-zhongxinshiyang:before {
content: "\e67b";
}
.icon-siweidaotu:before { .icon-siweidaotu:before {
content: "\e606"; content: "\e606";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "4320365",
"name": "重新试样",
"font_class": "zhongxinshiyang",
"unicode": "e67b",
"unicode_decimal": 59003
},
{ {
"icon_id": "11685410", "icon_id": "11685410",
"name": "思维导图", "name": "思维导图",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,78 +1,12 @@
<template> <template>
<!-- <form @submit.prevent="submitForm" enctype="multipart/form-data">-->
<form action="https://wzyzoss.eos-chongqing-3.cmecloud.cn" method="post" enctype="multipart/form-data">
<!-- action 是具体要上传的地址 -->
<!--
上传后文件Object:
<input type="input" name="key" :value="uploadData.key" placeholder="文件名" style="width: 400px"/><br/><br/>
ACL
<input type="hidden" name="acl" :value="uploadData.acl" placeholder="文件 ACL" style="width: 400px"/><br/><br/>
Content-Type:
<input type="input" name="Content-Type" :value="uploadData['Content-Type']" placeholder="文件类型" style="width: 400px"/><br/><br/>
X-Amz-Credential
<input type="text" name="X-Amz-Credential" :value="uploadData['x-amz-credential']" placeholder="X-Amz-Credential从后端程序返回中获取" style="width: 400px"/><br/><br/>
X-Amz-Algorithm
<input type="text" name="X-Amz-Algorithm" :value="uploadData['x-amz-algorithm']" placeholder="X-Amz-Algorithm 从后端程序返回中获取" style="width: 400px"/><br/><br/>
X-Amz-Date
<input type="text" name="X-Amz-Date" :value="uploadData['x-amz-date']" placeholder="X-Amz-Date 从后端程序返回中获取" style="width: 400px"><br/><br/>
Policy
<input type="text" name="Policy" :value="uploadData.policy" placeholder="Policy 从后端程序返回中获取" style="width: 400px"/><br/><br/>
X-Amz-Signature
<input type="text" name="X-Amz-Signature" :value="uploadData['x-amz-signature']" placeholder="X-Amz-Signature 从后端程序返回中获取" style="width: 400px"/><br/><br/>
-->
选择文件Object
<input type="file" name="file" @change="handleFileChange" style="width: 400px"/> <br/><br/> <input type="file" name="file" @change="handleFileChange" style="width: 400px"/> <br/><br/>
<input type="submit" name="submit" value="上传到 EOS" style="width: 400px"/><br/><br/>
<el-button @click="uploadFile">上传</el-button> <el-button @click="uploadFile">上传</el-button>
</form>
</template> </template>
<script setup> <script setup>
import {ref, onMounted} from "vue" import {ref, onMounted} from "vue"
import {createSignature, sessionToken} from "@/api/file"; import {sessionToken} from "@/api/file";
import axios from "axios" import {uploadSingleToEos} from '@/utils/fileUpload'
const url = "https://wzyzoss.eos-chongqing-3.cmecloud.cn"
const uploadData = ref({
"bucket": "wzyzoss",
"x-amz-date": "20250113T061000Z",
"x-amz-signature": "2d6fba9f27544bfc7414d660e2e73aafdaf02fe3de45e68f59d580276239cd07",
"acl": "private",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"key": "wzyzossa",
"x-amz-credential": "07ICFAF4IWWZP6RH0WCG/20250113/us-east-1/s3/aws4_request",
"Content-Type": null,
"policy": "eyJleHBpcmF0aW9uIjoiMjAyNS0wMS0xM1QwNzoxMDowMC42NzVaIiwiY29uZGl0aW9ucyI6W3sieC1hbXotZGF0ZSI6IjIwMjUwMTEzVDA2MTAwMFoifSx7ImFjbCI6InByaXZhdGUifSx7ImJ1Y2tldCI6Ind6eXpvc3MifSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInd6eXpvc3NhIl0sWyJzdGFydHMtd2l0aCIsIiRDb250ZW50LVR5cGUiLCJudWxsIl0seyJ4LWFtei1hbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In0seyJ4LWFtei1jcmVkZW50aWFsIjoiMDdJQ0ZBRjRJV1daUDZSSDBXQ0cvMjAyNTAxMTMvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LFsiY29udGVudC1sZW5ndGgtcmFuZ2UiLDEsMTAwMDAwXV19"
})
const submitForm = ()=> {
let formData = new FormData();
for (const formDataKey in formData) {
formData.append(formDataKey, formData[formDataKey]);
}
axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
console.log('表单提交成功,服务器响应:', response.data);
})
.catch(error => {
console.log('表单提交失败:', error);
});
}
const S3Data = { const S3Data = {
apiVersion: "2006-03-01", apiVersion: "2006-03-01",
@ -92,32 +26,24 @@ const handleFileChange = (event)=> {
const uploadMessage = ref(null) const uploadMessage = ref(null)
const callUploadSuccess = (e)=> {
console.log(e)
}
const callProgress = (e,p)=> {
console.log(e,p)
}
const uploadFile = ()=>{ const uploadFile = ()=>{
if (selectedFile) { if (selectedFile) {
console.log(S3Data) let res = uploadSingleToEos(selectedFile, null,{callUploadSuccess,callProgress})
// AWS.S3 console.log(res)
const s3 = new AWS.S3(S3Data);
let params = {
Key: selectedFile.name,
Bucket: "wzyzoss",
ContentType: selectedFile.type,
Body: selectedFile
}
console.log(params)
s3.putObject(params, function (err, data) {
console.log(err,data)
});
} }
} }
onMounted(()=>{ onMounted(()=>{
console.log(AWS)
/*createSignature({objectName:"123.jpg",contentType:"image/png"}).then(res=>{
uploadData.value = res.body
})*/
sessionToken().then(res=>{ sessionToken().then(res=>{
uploadMessage.value = res.data uploadMessage.value = res.data
console.log(res.data)
S3Data.accessKeyId = res.data.accessKeyId S3Data.accessKeyId = res.data.accessKeyId
// S3Data.accessKeyId = "kzOm2cc7nT12ao907Tc" // S3Data.accessKeyId = "kzOm2cc7nT12ao907Tc"
S3Data.secretAccessKey = res.data.secretAccessKey S3Data.secretAccessKey = res.data.secretAccessKey

View File

@ -13,7 +13,8 @@
<template #default="{ node, data }"> <template #default="{ node, data }">
<div v-if="props.isClassTask && (data.bookId == '' || data.bookId == '0')" class="tree-label-wrap"> <div v-if="props.isClassTask && (data.bookId == '' || data.bookId == '0')" class="tree-label-wrap">
<el-tooltip effect="light" placement="right" > <el-tooltip effect="light" placement="right" >
<template #content> {{ node.label }}<br /><span style="color: red;">-该单元章节无自主试题-</span> </template> <!-- <template #content> {{ node.label }}<br /><span style="color: red;">-该单元章节无自主试题-</span> </template> -->
<template #content> {{ node.label }}</template>
<div class="tree-label" style="color: #A5B3CA" > <div class="tree-label" style="color: #A5B3CA" >
{{ node.label }} {{ node.label }}
</div> </div>
@ -195,6 +196,12 @@ const handleNodeClick = (data) => {
}, },
node: nodeData node: nodeData
} }
/**
*
* 章节数据里面的rootid 为bookId
* 因为部分数据源的rootid 有问题 这里需要重复赋值一下
*/
curData.node.rootid = curBook.data.id
// :electron-store // :electron-store
let defaultExpandedKeys = parentNode ? [parentNode.id] : [nodeData.id] let defaultExpandedKeys = parentNode ? [parentNode.id] : [nodeData.id]
sessionStore.set('subject.defaultExpandedKeys', defaultExpandedKeys) sessionStore.set('subject.defaultExpandedKeys', defaultExpandedKeys)

View File

@ -1,9 +1,9 @@
<template> <template>
<el-dialog v-model="isDialog" :show-close="false" width="800" append-to-body destroy-on-close> <el-dialog v-model="model" :show-close="false" width="800" append-to-body destroy-on-close>
<template #header> <template #header>
<div class="custom-header flex"> <div class="custom-header flex">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<i class="iconfont icon-guanbi" @click="isDialog = false"></i> <i class="iconfont icon-guanbi" @click="model = false"></i>
</div> </div>
</template> </template>
<div class="dialog-content"> <div class="dialog-content">
@ -15,7 +15,9 @@
</div> </div>
<div class="flex-start flex" v-else> <div class="flex-start flex" v-else>
<div class="flex" v-loading="!item.msg"> <div class="flex" v-loading="!item.msg">
<div class="chart-item robot">{{ item.msg }}</div> <div class="chart-item robot">
<v-md-editor v-model="item.msg" mode="preview" />
</div>
</div> </div>
<div class="flex flex-end replace-item"> <div class="flex flex-end replace-item">
<span @click="saveAdjust(item)"><i class="iconfont icon-tihuan"></i>替换分析结果</span> <span @click="saveAdjust(item)"><i class="iconfont icon-tihuan"></i>替换分析结果</span>
@ -54,7 +56,7 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue' import { ref, reactive, onMounted, watch, onUnmounted } from 'vue'
import { completion, docList } from '@/api/mode/index' import { completion, docList } from '@/api/mode/index'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import { dataSetJson } from '@/utils/comm.js' import { dataSetJson } from '@/utils/comm.js'
@ -66,7 +68,7 @@ const userInfo = useUserStore().user
const textarea = ref('') const textarea = ref('')
const isDialog = defineModel() const model = defineModel()
const props = defineProps({ const props = defineProps({
item: { item: {
@ -89,6 +91,12 @@ const props = defineProps({
} }
}) })
watch(model, (newVal) =>{
if(newVal){
msgList.value.length = 0
}
})
const emit = defineEmits(['saveEdit']) const emit = defineEmits(['saveEdit'])
const loaded = ref(false) const loaded = ref(false)
@ -148,7 +156,7 @@ const getCompletion = async (val) => {
} }
const saveAdjust = (item) =>{ const saveAdjust = (item) =>{
isDialog.value = false model.value = false
emitter.emit('onSaveAdjust', item.msg) emitter.emit('onSaveAdjust', item.msg)
} }
@ -158,12 +166,17 @@ const dataset_id = ref('')
const fileList = ref([]) const fileList = ref([])
const getList = () =>{ const getList = () =>{
docList({ docList({
userId: userInfo.userId, createUser: userInfo.userId,
dataset_id: dataset_id.value datasetId: dataset_id.value,
edustage: curNode.edustage,
edusubject: curNode.edusubject
}).then( res =>{ }).then( res =>{
if(res.rows.length){
fileList.value = res.rows fileList.value = res.rows
Object.assign(curFile, fileList.value[0]) Object.assign(curFile, fileList.value[0])
params.document_ids = fileList.value[0].docId params.document_ids = fileList.value[0].docId
}
}) })
} }
emitter.on('changeCurFile', (item) =>{ emitter.on('changeCurFile', (item) =>{
@ -239,6 +252,7 @@ onUnmounted(() => {
.robot { .robot {
background: #409EFF; background: #409EFF;
color: #FFF; color: #FFF;
width: 100%;
} }
.replace-item{ .replace-item{
font-size: 12px; font-size: 12px;
@ -332,4 +346,13 @@ onUnmounted(() => {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
} }
:deep(.v-md-editor){
background-color: #409EFF;
}
:deep(.github-markdown-body) {
font-size: 14px;
padding: 10px;
}
</style> </style>

View File

@ -13,7 +13,7 @@
<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" autosize :rows="2" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>

View File

@ -137,7 +137,9 @@ const curFile = reactive({})
const getList = () => { const getList = () => {
docList({ docList({
createUser: userInfo.userId, createUser: userInfo.userId,
datasetId: dataset_id.value datasetId: dataset_id.value,
edustage: curNode.edustage,
edusubject: curNode.edusubject
}).then((res) => { }).then((res) => {
fileList.value = [...res.rows] fileList.value = [...res.rows]
if(res.rows.length){ if(res.rows.length){

View File

@ -4,12 +4,12 @@
<div class="container-right-header flex"> <div class="container-right-header flex">
<el-dropdown @command="changeTemplate" :hide-on-click="false"> <el-dropdown @command="changeTemplate" :hide-on-click="false">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
{{ curTemplate.name }} {{ curTemplate.ex3 ? `${curTemplate.name}(平台)` : `${curTemplate.name}(个人)` }}
<i class="iconfont icon-xiangxia"></i> <i class="iconfont icon-xiangxia"></i>
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.name <el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.ex3 ? `${item.name}(平台)` : `${item.name}(个人)`
}}</el-dropdown-item> }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@ -18,20 +18,20 @@
<el-select v-model="curMode" placeholder="Select" class="mr-4 w-30"> <el-select v-model="curMode" placeholder="Select" class="mr-4 w-30">
<el-option v-for="item in modeOptions" :key="item.value" :disabled="item.disabled" :label="item.label" :value="item.value" /> <el-option v-for="item in modeOptions" :key="item.value" :disabled="item.disabled" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-button type="danger" link :disabled="!(templateList.length)" @click="removeItem(curTemplate, false)"> <el-button type="danger" link :disabled="!(templateList.length)" @click="removeItem(curTemplate, false)" v-if="curTemplate.ex3 != 1">
删除 删除
</el-button> </el-button>
<el-button type="primary" link :disabled="!(templateList.length)" @click="onAdd"> <el-button type="primary" link :disabled="!(templateList.length)" @click="onAdd">
<i class="iconfont icon-jiahao"></i> <i class="iconfont icon-jiahao"></i>
添加提示词 {{ curTemplate.ex3 == 1 ? '复制并创建个人模板' : '添加提示词' }}
</el-button> </el-button>
<el-button type="primary" :disabled="!(childTempList.length)" @click="getCompletion">一键研读</el-button> <el-button type="primary" :disabled="!(childTempList.length)" @click="getModelResult('')">一键研读</el-button>
</div> </div>
</div> </div>
<!--List--> <!--List-->
<div class="container-right-list" ref="listRef"> <div class="container-right-list" ref="outerContainer">
<template v-for="(item, index) in childTempList" > <template v-for="(item, index) in childTempList" >
<div class="template-item" v-loading="item.loading"> <div class="template-item" v-loading="item.loading" ref="messageElements">
<div class="item-header"> <div class="item-header">
<div> <div>
<span class="blue">#</span>{{ item.name }} <span class="blue">#</span>{{ item.name }}
@ -50,18 +50,26 @@
<div class="item-text"> <div class="item-text">
{{ item.prompt }} {{ item.prompt }}
</div> </div>
<div class="item-text text-answer" v-if="item.answer"> <div class="item-text text-answer">
<div class="item-icon"> <div class="item-icon">
<i class="iconfont icon-ai"></i> <i class="iconfont icon-ai"></i>
</div> </div>
<div class="item-answer"> <div class="item-answer">
<TypingEffect v-if="isStarted[index]" :text="item.answer" :delay="10" :aiShow="item.aiShow" <!-- <TypingEffect v-if="isStarted[index]" :text="item.answer" :delay="10" :aiShow="item.aiShow"
@complete="handleCompleteText($event, index)" @updateScroll="scrollToBottom($event, index)" /> @complete="handleCompleteText($event, index)" @updateScroll="scrollToBottom($event, index)" /> -->
<TypingEffect
:text="item.answer"
:showTypewriter="item.showTypewriter"
:speed="30"
:charsPerFrame="15"
@scroll="handleScroll(index)"
@done="handleDone(index)"
/>
</div> </div>
</div> </div>
<div class="ai-btn" v-if="item.answer"> <div class="ai-btn" v-if="item.answer">
<el-button type="primary" link @click="againResult(index, item)"> <el-button type="primary" link @click="getModelResult(index, item)">
<i class="iconfont icon-ai1"></i> <i class="iconfont icon-zhongxinshiyang"></i>
重新研读 重新研读
</el-button> </el-button>
<el-button type="primary" link @click="onAdjust(index, item)"> <el-button type="primary" link @click="onAdjust(index, item)">
@ -99,7 +107,6 @@ import TypingEffect from '@/components/typing-effect/index.vue'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import emitter from '@/utils/mitt'; import emitter from '@/utils/mitt';
import { dataSetJson } from '@/utils/comm.js' import { dataSetJson } from '@/utils/comm.js'
import { cloneDeep } from 'lodash'
const props = defineProps(['type']) const props = defineProps(['type'])
const { user } = useUserStore() const { user } = useUserStore()
@ -120,6 +127,10 @@ const modeOptions = ref([
label: '知识库模型', label: '知识库模型',
value: 2, value: 2,
disabled: false disabled: false
},
{
label: 'deepseek模型',
value: 3,
} }
]) ])
@ -172,60 +183,252 @@ const getTemplateList = () => {
} }
}) })
} }
const getChildTemplate = () => { const getChildTemplate = (val) => {
tempLoading.value = true tempLoading.value = true
modelList({ model: props.type, type: 2, parentId: curTemplate.id, ex1: curNode.edustage, ex2: curNode.edusubject }).then(res => { modelList({ model: props.type, type: 2, parentId: curTemplate.id, ex1: curNode.edustage, ex2: curNode.edusubject }).then(res => {
childTempList.value = res.rows childTempList.value = res.rows
if (childTempList.value.length) { if (childTempList.value.length) {
childTempList.value.forEach(item => item.answer = '') childTempList.value.forEach(item => {
item.answer = '';
item.showTypewriter = false
})
} }
getTempResult()
//
getTempResult(val)
}).finally(() => { }).finally(() => {
tempLoading.value = false tempLoading.value = false
}) })
} }
const isStarted = ref([]);
const listRef = ref()
// //
const getTempResult = () => { const getTempResult = (val) => {
tempResult({ mainModelId: curTemplate.id, pageNum: 1, pageSize: 10000, ex1: curNode.id }).then(res => { tempResult({ mainModelId: curTemplate.id, pageNum: 1, pageSize: 10000, ex1: curNode.id }).then(async res => {
let rows = res.rows let rows = res.rows
childTempList.value.forEach(item => { childTempList.value.forEach(item => {
rows.forEach(el => { rows.forEach(el => {
if (item.id == el.modelId) { if (item.id == el.modelId) {
item.answer = getResult(el.content) item.answer = el.content
item.resultId = el.id item.resultId = el.id
} }
}) })
}) })
if (rows.length > 0) { // val : true
isStarted.value = new Array(rows.length).fill(true) if(val){
} // dom
await nextTick()
//
let lastIndex = childTempList.value.length - 1
getModelResult(lastIndex, childTempList.value[lastIndex], true)
}
}) })
} }
const scrollToBottom = (height, index) => { const prompt = ref('')
if (listRef.value) { //
let sum = 0 const isAgain = ref(false)
let listDom = listRef.value.children
//
const getModelResult = async (index, item, isLast) => {
/**
*
* index: 当前项索引
* item: 当前项
* isLast: 是否最后一项 用于添加子模板后研读
*/
let str = prompt.value
//
if(index !== ''){
isAgain.value = true
//
childTempList.value[index].answer = ''
//
childTempList.value[index].showTypewriter = true
//
if (messageElements.value[index]) {
messageElements.value[index].scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}
// prompt
str = str.replace('{模板标题}',item.name)
str = str.replace('{模板内容}',item.prompt)
params.prompt = str
params.template = item.prompt
//
try {
childTempList.value[index].loading = true
const data = await requestModelData()
childTempList.value[index].answer = data.answer;
if(isLast){
await onSaveTemp(childTempList.value[index])
}else{
//
onEditResult(data.answer, index)
}
} finally {
childTempList.value[index].loading = false
}
}
//
else{
//
childTempList.value.forEach(item => {
if (item.answer) {
item.answer = ''
}
})
//
messageElements.value[0].scrollIntoView({
behavior: 'smooth',
block: 'start',
})
processNext(0)
}
}
//
const processNext = async (index) =>{
let str = prompt.value
//
const item = childTempList.value[index]
item.loading = true
str = str.replace('{模板标题}',item.name)
str = str.replace('{模板内容}',item.prompt)
params.prompt = str
params.template = item.prompt
//
try {
const data = await requestModelData()
item.answer = data.answer
item.showTypewriter = true
//
await onSaveTemp(item)
} finally {
item.loading = false
}
}
//
const handleDone = (index) =>{
if(isAgain.value){
isAgain.value = false
return
}
else{
if(index == childTempList.value.length - 1) return
outerContainer.value.scrollTo({
top: outerContainer.value.scrollHeight,
behavior: 'smooth',
});
processNext(index + 1)
}
}
// ms:
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// or
const requestModelData = async () =>{
//
let data = null
if (curMode.value == 1) {
const res = await sendChart({
content: params.prompt,
conversationId: conversation_id.value,
stream: false
})
// 1s
if(!isAdjust.value){
await delay(1000); // 1
}
data = res.data
}
//
else {
if (curMode.value == 3) {
params.llm = 'deepseek-r1:8b'
}
const res = await completion(params)
data = res.data
}
return data
}
const messageElements = ref([]); //
//
// const animationId = ref(null)
const animationIDs = ref([])
const outerContainer = ref()
let lastScrollTime = 0;
const scrollInterval = 200
const handleScroll = (index) => {
const now = Date.now();
if (now - lastScrollTime >= scrollInterval) {
const container = document.querySelector('.container-right-list');
if(isAgain.value){
scrollToTmp(index)
}
else{
if (container) {
if(index == 0){ if(index == 0){
// 220 const item = messageElements.value[index]
let screenHeight = window.innerHeight - 220 if(item.clientHeight > outerContainer.value.clientHeight ){
if (height > screenHeight) { item.scrollIntoView({ behavior: 'smooth', block: 'end' })
listRef.value.scrollTop = (height - screenHeight + 50)
} }
} }
else{ else{
for (let i = 0; i < index; i++) { //
sum += listDom[i].clientHeight const id = requestAnimationFrame(() => {
} container.scrollTo({
listRef.value.scrollTop = sum + height top: container.scrollHeight,
behavior: 'smooth',
});
});
animationIDs.value.push(id)
} }
} }
} }
lastScrollTime = now;
}
}
//
const scrollToTmp = (index) =>{
//
if(index == childTempList.value.length - 1){
const id = requestAnimationFrame(() => {
outerContainer.value.scrollTo({
top: outerContainer.value.scrollHeight,
behavior: 'smooth',
});
});
animationIDs.value.push(id)
}
else{
const item = messageElements.value[index]
if(item.clientHeight > outerContainer.value.clientHeight ){
item.scrollIntoView({ behavior: 'smooth', block: 'end' })
}
}
}
// //
const changeTemplate = (val) => { const changeTemplate = (val) => {
@ -294,116 +497,10 @@ const onEdit = (index, item) => {
isEdit.value = true isEdit.value = true
} }
// //
const onEditResult = async (answer, index) => {
const prompt = ref('')
//
const isAgain = ref(false)
const againResult = async (index, item) => {
isAgain.value = true
isStarted.value[index] = false
childTempList.value[index].answer = ''
if (index == 0) {
listRef.value.scrollTop = 0
} else {
scrollToBottom(50, index)
}
try {
await nextTick()
childTempList.value[index].loading = true
item.aiShow = true
let str = cloneDeep(prompt.value)
str = str.replace('{模板标题}',item.name)
str = str.replace('{模板内容}',item.prompt)
params.prompt = str
params.template = item.prompt
let data = null;
//
if (curMode.value == 1) {
const res = await sendChart({
content: params.prompt,
conversationId: conversation_id.value,
stream: false
})
data = res.data
} else {
//
const res = await completion(params)
data = res.data
}
childTempList.value[index].answer = getResult(data.answer);
isStarted.value[index] = true
} finally {
childTempList.value[index].loading = false
}
}
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
//
const getCompletion = async () => {
isStarted.value = new Array(childTempList.length).fill(false)
isStarted.value[0] = true
childTempList.value.forEach(item => {
if (item.answer) {
item.answer = ''
}
})
for (let item of childTempList.value) {
try {
item.loading = true
item.aiShow = true
let str = cloneDeep(prompt.value)
str = str.replace('{模板标题}',item.name)
str = str.replace('{模板内容}',item.prompt)
params.prompt = str
params.template = item.prompt
//
let data = null
if (curMode.value == 1) {
const res = await sendChart({
content: params.prompt,
conversationId: conversation_id.value,
stream: false
})
await delay(1000); // 1
data = res.data
}
//
else {
const res = await completion(params)
data = res.data
}
item.answer = getResult(data.answer)
onSaveTemp(item)
} finally {
item.loading = false
}
}
}
const handleCompleteText = async (answer, index) => {
if (index < childTempList.value.length - 1) {
isStarted.value[index + 1] = true; //
}
if (isAgain.value) {
try {
await editTempResult({ id: childTempList.value[index].resultId, content: answer }) await editTempResult({ id: childTempList.value[index].resultId, content: answer })
} finally {
isAgain.value = false
}
}
} }
// //
@ -436,18 +533,13 @@ const onSaveTemp = async (item) => {
} }
} }
// ### **
let getResult = (str) => {
let newStr = str.replace(/#+|(\*\*)/g, '');
return newStr
}
// //
emitter.on('onGetChild', () => { emitter.on('onGetChild', () => {
getChildTemplate() getChildTemplate(true)
}) })
// //
emitter.on('onGetMain', () => { emitter.on('onGetMain====》', () => {
getTemplateList() getTemplateList()
}) })
@ -513,7 +605,11 @@ onUnmounted(() => {
emitter.off('onGetMain'); emitter.off('onGetMain');
emitter.off('onGetChild'); emitter.off('onGetChild');
emitter.off('onSaveAdjust'); emitter.off('onSaveAdjust');
//
animationIDs.value.forEach(id => cancelAnimationFrame(id))
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -605,14 +701,15 @@ onUnmounted(() => {
.iconfont { .iconfont {
margin-right: 3px; margin-right: 3px;
font-size: 18px;
} }
:deep(.el-button) { :deep(.el-button) {
font-size: 13px; font-size: 14px;
} }
.icon-ai1 { .icon-zhongxinshiyang {
font-size: 18px; font-size: 20px;
} }
} }
} }

View File

@ -1,72 +1,109 @@
<template> <template>
<div class="typing-effect" ref="typingEffectRef"> <div class="typing-effect" ref="typingEffectRef">
<!-- <span v-html="displayedText"></span> --> <!-- <span v-html="displayedText"></span> -->
<el-input <!-- <el-input
v-model="displayedText" v-model="currentText"
:autosize="{ minRows: 2 }" :autosize="{ minRows: 2 }"
type="textarea" type="textarea"
readonly readonly
resize="none" resize="none"
style="width: 100%;" style="width: 100%;"
input-style="border:none;outline: none;box-shadow:none;color:000;fontSize:14px" input-style="border:none;outline: none;box-shadow:none;color:000;fontSize:14px"
/> /> -->
<v-md-editor v-model="currentText" mode="preview" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch, nextTick } from 'vue'; import { ref, onMounted, watch, onUnmounted } from 'vue';
const props = defineProps({ const props = defineProps({
text: { text: {
type: [String, Object], type: String,
required: true required: true,
}, },
delay: { speed: {
type: Number, type: Number,
default: 100 // default: 50, //
},
charsPerFrame: {
type: Number,
default: 1, //
},
showTypewriter: {
type: Boolean,
default: true, //
}, },
aiShow: {
type: [Boolean] // true
}
}); });
const typingEffectRef = ref(null);
const emit = defineEmits(['complete', 'updateScroll']);
const displayedText = ref('');
const index = ref(0);
const type = async () => { // emits
await nextTick() const emit = defineEmits(['scroll','done']);
if(!props.aiShow) {
displayedText.value = props.text //
return const currentText = ref(''); //
} const currentCharIndex = ref(0); //
if (index.value <= props.text.length) { const isTyping = ref(false); //
displayedText.value += props.text.charAt(index.value); const animationFrameId = ref(null); // requestAnimationFrame ID
index.value++;
//
const typeText = () => {
if (currentCharIndex.value < props.text.length) {
// charsPerFrame
currentText.value += props.text.slice(
currentCharIndex.value,
currentCharIndex.value + props.charsPerFrame
);
currentCharIndex.value += props.charsPerFrame;
emit('scroll'); //
// 使 requestAnimationFrame
animationFrameId.value = requestAnimationFrame(() => {
setTimeout(() => { setTimeout(() => {
type(); typeText();
emit('updateScroll', typingEffectRef.value.clientHeight); // }, props.speed);
}, props.delay); });
} else { } else {
// complete isTyping.value = false;
emit('complete',displayedText.value); emit('done')
} }
}; };
onMounted(() => { //
resetAndType(); const showFullText = () =>{
}); currentText.value = props.text
const resetAndType = () =>{
displayedText.value = '';
index.value = 0;
type();
} }
// props 便 text delay //
watch([() => props.text, () => props.delay], resetAndType); const startTypeWriter = () => {
isTyping.value = true;
currentText.value = '';
currentCharIndex.value = 0;
typeText();
};
// text
watch(
() => props.text,
() => {
if(props.showTypewriter && props.text){
startTypeWriter();
} else {
showFullText();
}
}
);
//
onMounted(() => {
// startTypeWriter();
});
//
onUnmounted(() => {
if (animationFrameId.value) {
cancelAnimationFrame(animationFrameId.value);
}
});
</script> </script>
@ -74,4 +111,12 @@ watch([() => props.text, () => props.delay], resetAndType);
:deep(.el-textarea__inner:hover){ :deep(.el-textarea__inner:hover){
box-shadow: none; box-shadow: none;
} }
.typing-effect{
:deep(.github-markdown-body){
padding: 0;
font-size: 14px;
}
}
</style> </style>

View File

@ -23,7 +23,7 @@
</li> </li>
<li v-if="computedregistertype!=4" :class="computedregistertype==1 || computedregistertype==2 ? '':'pointer-events'" @click="onUserTo('/joinSchool')">加入学校</li> <li v-if="computedregistertype!=4" :class="computedregistertype==1 || computedregistertype==2 ? '':'pointer-events'" @click="onUserTo('/joinSchool')">加入学校</li>
<li @click="onUserTo('/profile')">个人中心</li> <li @click="onUserTo('/profile')">个人中心</li>
<li v-if="isStadium() !== true" @click="onUserTo('/schoolManagement')">学校管理</li> <li v-if="isStadium() !== true && checkRole(['admin','headmaster'])" @click="onUserTo('/schoolManagement')">学校管理</li>
<li v-if="isStadium() !== true" @click="onUserTo('/class')">班级中心</li> <li v-if="isStadium() !== true" @click="onUserTo('/class')">班级中心</li>
<li @click="logout">退出登录</li> <li @click="logout">退出登录</li>
</ul> </ul>
@ -65,7 +65,7 @@ import pkc from "../../../../../package.json"
import defaultUserImg from '@/assets/images/img-avatar.png' import defaultUserImg from '@/assets/images/img-avatar.png'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import {toLinkLeftWeb} from "@/utils/tool" import {toLinkLeftWeb} from "@/utils/tool"
import { checkRole } from '@/utils/permission'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
const dev_api = ref(import.meta.env.VITE_APP_BASE_API) const dev_api = ref(import.meta.env.VITE_APP_BASE_API)
@ -86,6 +86,7 @@ const defaultImg = ['/img/avatar-default.jpg','/images/img-avatar.png','/src/ass
// //
const isStadium = () => { const isStadium = () => {
// console.log('isStadium',userStore.roles )
let user = userStore.user let user = userStore.user
let roles = user.roles let roles = user.roles
return roles.some(item => item.roleKey === 'stadium') return roles.some(item => item.roleKey === 'stadium')

View File

@ -25,7 +25,7 @@ import log from 'electron-log/renderer' // 渲染进程日志-文件记录
import customComponent from '@/components/common' // 自定义组件 import customComponent from '@/components/common' // 自定义组件
import plugins from './plugins' // plugins插件 import plugins from './plugins' // plugins插件
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import aiAudio from 'aix-plugins-aitools' // 文字转语音插件 import aiTools from 'aix-plugins-aitools' // 文字转语音插件
import '../../../node_modules/aix-plugins-aitools/aitools.css' import '../../../node_modules/aix-plugins-aitools/aitools.css'
@ -91,7 +91,7 @@ app.use(router)
.use(plugins) .use(plugins)
.use(Icon) .use(Icon)
.use(Directive) .use(Directive)
.use(aiAudio) .use(aiTools)
.use(VMdEditor) .use(VMdEditor)
.mount('#app') .mount('#app')

View File

@ -0,0 +1,160 @@
import {sessionToken, uploadSingleFileToEos} from "@/api/file";
import CryptoJS from 'crypto-js'
import {ElMessageBox} from "element-plus";
/**
* @description
* @param {Function} callGetToken - token回调
* @param {Function} callUpload - EOS回调
* @param {Function} callProgress -
* @param {Function} callUploadSuccess -
*/
type CallBack = {
callGetToken?: (e:any)=>any;
callUploadFile?: (e:any)=>any;
callProgress?: (e:any,p:number)=>any;
callUploadSuccess?: (e:any)=>any;
}
/**
* @description
* @param {string} _bucket -
* @param {string} id - id
* @param {string} filePath -
* @param {string} fileMd5 - MD5
* @param {string} fileNewName -
* @param {string} fileName -
* @param {string} fileSize -
* @param {string} fileSuffix -
* @param {string} fileType -
*/
type SingleUploadData = {
_bucket?: string,
id?: string,
filePath?: string,
fileMd5?: string,
fileNewName?: string,
fileName?: string,
fileSize?: number,
fileSuffix?: string,
fileType?: string,
}
const S3Data = {
apiVersion: "2006-03-01",
accessKeyId: "", // 服务端获取到的 access key ID
secretAccessKey: "", // 服务端获取到的 secret access key
endpoint: "",
sessionToken: '',
signatureVersion: "v2",
sslEnabled: true // 是否启用 HTTPS 连接
}
/**
* @description
* @param file
* @param {SingleUploadData} uploadData
* @param {CallBack} callBack -
*/
const uploadSingleToEos = async (file:File, uploadData: SingleUploadData, callBack?: CallBack) => {
console.log(file)
//去服务端拿授权
let res = await sessionToken().catch((err:any)=>{
ElMessageBox.alert(err)
});
S3Data.accessKeyId = res.data.accessKeyId
S3Data.secretAccessKey = res.data.secretAccessKey
S3Data.endpoint = res.data.endPoint
S3Data.sessionToken = res.data.sessionToken
uploadData = uploadData?uploadData:{}
uploadData._bucket = res.data.bucket
//如果文件名不存在,则使用时间戳为名字
uploadData.id = crypto.randomUUID();
uploadData.fileMd5 = await getFileMD5(file);
uploadData.fileName = file.name?file.name:(new Date().getTime()+"");
if (uploadData.fileName.lastIndexOf(".") === -1) {
uploadData.fileSuffix = ""
uploadData.fileNewName = uploadData.id
}else {
uploadData.fileSuffix = uploadData.fileName.substring(uploadData.fileName.lastIndexOf(".")+1)
uploadData.fileNewName = uploadData.id + "." + uploadData.fileSuffix
}
uploadData.filePath = getEosFileKey(uploadData.id, uploadData.fileSuffix);
uploadData.fileSize = file.size
uploadData.fileType = file.type
console.log(uploadData)
//回调获取token
callBack?.callGetToken && callBack.callGetToken(res.data)
//将文件上传到EOS
let uploadRes = await uploadFile(file,uploadData,callBack).catch((err)=>{
ElMessageBox.alert(err)
});
if (!uploadRes) return
//回调成功上传EOS
callBack?.callGetToken && callBack.callGetToken(uploadRes)
//去服务端确认是否已经完成上传,并保存文件上传信息
let saveUploadRes = await uploadSingleFileToEos(uploadData)
//回调整个文件上传完成
callBack?.callUploadSuccess && callBack.callUploadSuccess(saveUploadRes)
return saveUploadRes
}
/**
* @description
* @param file
* @param uploadData
* @param callBack
*/
const uploadFile = (file:File, uploadData: SingleUploadData,callBack?:CallBack) =>{
return new Promise((resolve, reject) => {
const s3 = new AWS.S3(S3Data);
let params = {
Key: uploadData.filePath,
Bucket: uploadData._bucket,
ContentType: file.type,
Body: file
}
s3.putObject(params, function (err:any, data:any) {
if (err) {
reject(data)
}else {
resolve(data)
}
}).on('httpUploadProgress', function(evt:any) {
//回调上传进度
callBack?.callProgress && callBack.callProgress(evt,Math.round((evt.loaded / evt.total) * 100));
})
})
}
/**
* @description EOS文件key
* @param uuid id
* @param suffix
*/
const getEosFileKey = (uuid:string, suffix:string) => {
let date = new Date()
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
return year + "/" + month + "/" + day + "/" + uuid + "." + suffix;
}
/**
* @description MD5
* @param file
*/
const getFileMD5 = (file:File):Promise<string> => {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {
let buffer = reader.result as ArrayBuffer;
let md5 = CryptoJS.MD5(buffer).toString();
resolve(md5)
}
})
}
export {uploadSingleToEos}

View File

@ -0,0 +1,51 @@
import useUserStore from '@/store/modules/user'
/**
* 字符权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkPermi(value) {
if (value && value instanceof Array && value.length > 0) {
const permissions = useUserStore().permissions
const permissionDatas = value
const all_permission = "*:*:*";
const hasPermission = permissions.some(permission => {
return all_permission === permission || permissionDatas.includes(permission)
})
if (!hasPermission) {
return false
}
return true
} else {
console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
return false
}
}
/**
* 角色权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkRole(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = useUserStore().roles
const permissionRoles = value
const super_admin = "admin";
const hasRole = roles.some(role => {
return super_admin === role || permissionRoles.includes(role)
})
if (!hasRole) {
return false
}
return true
} else {
console.error(`need roles! Like checkRole="['admin','editor']"`)
return false
}
}

View File

@ -4,7 +4,7 @@
<el-tab-pane :label="item.label" style="text-align:left" stretch="true"> <el-tab-pane :label="item.label" style="text-align:left" stretch="true">
<template v-if="item.stuList.length > 0"> <template v-if="item.stuList.length > 0">
<template v-for="(stuItem,stuIndex) in item.stuList" :key="stuIndex"> <template v-for="(stuItem,stuIndex) in item.stuList" :key="stuIndex">
<el-tag style="margin:5px 10px 0 0" type="primary">{{ stuItem.studentname }}:{{ stuItem.getScore }}</el-tag> <el-tag style="margin:5px 10px 0 0" type="primary">{{ stuItem.studentname }}:{{ stuItem.point }}</el-tag>
</template> </template>
</template> </template>
<template v-else> <template v-else>
@ -93,6 +93,8 @@ watch(() => useOverview.tableList, () => {
}else{ }else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item); hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
} }
console.log(hasStudents.value,'hasStudents.value');
showStudents(0) showStudents(0)
},{deep: true}) },{deep: true})
</script> </script>

View File

@ -376,12 +376,12 @@ const getClassWorkStudentList = (rowId) => {
} }
} }
} }
const allScore = evalarray.reduce((acc, cur) => acc + cur.score, 0) const allScore = evalarray.reduce((acc, cur) => acc + cur.teacherRating, 0)
//console.log(evalarray, 'evalarray------------------------------------') //console.log(evalarray, 'evalarray------------------------------------')
if (feedcount > 0) { if (feedcount > 0) {
// : /*100 // : /*100
response.rows[i].scoingRate = ((score / allScore) * 100).toFixed(0) + '%' response.rows[i].scoingRate = ((score / allScore) * 100).toFixed(0) + '%'
response.rows[i].getScore = score response.rows[i].getScore = allScore
} else { } else {
response.rows[i].scoingRate = '0%' response.rows[i].scoingRate = '0%'
response.rows[i].getScore = 0 response.rows[i].getScore = 0

View File

@ -1,189 +1,30 @@
<template> <template>
<div class="page-mindmap flex flex-col h-full"> <aiMindMap
<div class="mindmap-header flex items-center justify-between pl-5"> @exportImg="exportImg"
<div class="flex w-1/2 items-center justify-between"> @createMindMap="createMindMap"
<div> @reloadMindMap="reloadMindMap"
<b>AI思维导图</b> <span class="ml-5">{{ curNode.itemtitle }}</span> ref="mindMapRef"
</div>
<div>
<template v-if="isEdit">
<el-button class="mr-3" @click="isEdit = false">取消</el-button>
<el-button type="success" class="mr-3" @click="isEdit = false">保存</el-button>
</template>
<!-- <el-button v-else type="primary" class="mr-3" @click="isEdit = true">编辑</el-button>-->
<!-- <el-select v-model="curMode" class="mr-3 w-30">
<el-option
v-for="item in modeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/> />
</el-select>-->
</div>
</div>
<div>
导出清晰度<el-input-number v-model="scale" :min="1" :max="10" />数字越大越清晰但图片大小会越大
<el-button @click="outputImg" type="primary">导出图片</el-button>
</div>
</div>
<div class="mindmap-main flex flex-1" style="overflow: auto;">
<div class="w-1/3 h-full p-3 main-left flex flex-col" v-loading="loadingAnswer" style="overflow: auto;">
<div class="flex flex-1" style="overflow: auto;">
<v-md-editor v-model="resMarkdown" :mode="isEdit ? 'edit' : 'preview'" />
</div>
<div class="main-left-ipt">
<el-input
v-model="textVal"
style="max-width: 600px"
placeholder="请输入信息对内容进一步调整"
class="input-with-select"
>
<template #append>
<el-icon size="20" style="cursor: pointer;" @click="sendMessage"><Position /></el-icon>
</template>
</el-input>
<!-- <el-input style="float: left;" v-model="textVal" size="large" placeholder="请输入信息对内容进一步调整" /><el-button>发送</el-button>-->
</div>
</div>
<div class="w-2/3 h-full main-right">
<mindmap v-if="rootData" :timetravel="true" :drag="true" :zoom="true" :edit="true"
:center-btn="true" :fit-btn="true" :ctm="true" :add-node-btn="true"
v-model="rootData"></mindmap>
</div>
</div>
</div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, computed, watch } from 'vue' import {onMounted, ref} from "vue";
import { Position } from "@element-plus/icons-vue" // import aiMindMap from './mindMapAITools.vue'
import { sessionStore } from '@/utils/store' import {sessionStore} from "@/utils/store";
import { modelList, completion, modelChat } from '@/api/mode/index.js' import {editTempResult, modelList, tempResult, tempSave} from "@/api/mode";
import { createChart, sendChart } from '@/api/ai/index'
import { Transformer } from 'markmap-lib'
import mindmap from 'vue3-mindmap'
import html2canvas from 'html2canvas'
import 'vue3-mindmap/dist/style.css'
import {exportImg64File} from "@/utils/talkFile"; import {exportImg64File} from "@/utils/talkFile";
import axios from 'axios' import useUserStore from '@/store/modules/user'
const { user } = useUserStore()
const transformer = new Transformer() const showMap = ref(false)
//
const curNode = reactive({})
const textVal = ref(``)
const loadingAnswer = ref(false)
const scale = ref(5)
// const textVal = computed(() => {
// return `${curNode.itemtitle}markdownmarkdown`
// })
const curMode = ref(2)
const modeOptions = ref([
{
label: '教学大模型',
value: 1
},
{
label: '知识库模型',
value: 2,
}
])
const isEdit = ref(false)
// AI
const resMarkdown = ref(``)
const params = reactive({
prompt: '',
dataset_id: '',
template: ''
})
const messages = ref([
{
role: 'user',
content: ''
}
])
const aiConversation = async () => {
console.log(prompt.value)
// return
// params.prompt = prompt.value.replace(/{}/g, '')
//
// let str = '-PPT'
// messages.value[0].content = str
params.prompt = prompt.value
//
loadingAnswer.value = true
if (curMode.value == 1) {
const res = await sendChart({
content: params.prompt,
conversationId: conversation_id.value,
stream: false
})
resMarkdown.value = res.data.answer.replaceAll('```markdown','').replaceAll('```', '')
}
//
else {
let data = {
"messages": [
{
"role": "user",
"content": params.prompt
}
]
}
const res = await text2mindmap(data).finally(() => {
loadingAnswer.value = false
})
console.log(res)
resMarkdown.value = res.data.answer.content.replaceAll('```markdown','').replaceAll('```', '')
}
}
const sendMessage = async () => {
loadingAnswer.value = true
rootData.value = null
let data = {
"messages": [
{
"role":"assiatant",
"content": resMarkdown.value
},
{
"role": "user",
"content": `${textVal.value}${prompt.value}`
}
]
}
let res = await text2mindmap(data).finally(() => {
loadingAnswer.value = false
})
resMarkdown.value = res.data.answer.content.replaceAll('```markdown','').replaceAll('```', '')
}
const text2mindmap =(data)=> {
return axios({
url: "https://ai.ysaix.com:7865/chat",
method: "post",
headers: {
"Content-Type": "application/json",
Accept: "*/*",
},
data:data
});
}
// prompt
const prompt = ref('') const prompt = ref('')
const getPrompt = async () => { const url = ref("https://ai.ysaix.com:7865/chat")
const { rows } = await modelList({ model: 5 })
let str = rows.find((item) => item.name.indexOf('思维导图') != -1).prompt
let curNode = sessionStore.get('subject.curNode')
const title = ref(curNode.itemtitle)
let curModel = ref({})
const getPrompt = async () => {
let str = curModel.value.prompt
str = str.replace('{学段}', curNode.edustage) str = str.replace('{学段}', curNode.edustage)
str = str.replace('{学科}', curNode.edusubject) str = str.replace('{学科}', curNode.edusubject)
let bookV = curNode.roottitle + '版' let bookV = curNode.roottitle + '版'
@ -192,76 +33,59 @@ const getPrompt = async () => {
prompt.value = str prompt.value = str
} }
// const exportImg = (data) => {
const conversation_id = ref('') exportImg64File(data, `${curNode.itemtitle}思维导图`)
const getChartId = async () => {
const res = await createChart()
conversation_id.value = res.data.conversation_id
// ai
aiConversation()
} }
const rootData = ref(null) const createMindMap = async (data) => {
if (tempData.value) {
tempData.value.content = data
await editTempResult(tempData.value)
}else {
const item = {
mainModelId: 5,
modelId: curModel.value.id,
content: data,
ex1: curNode.id
}
await tempSave(item)
await getTempData()
}
}
const degure = (item) => { const reloadMindMap = async (data) => {
item.name = item.content.replace(/<[^>]*>/g, '');; tempData.value.content = data
if (item.children && item.children.length) { await editTempResult(tempData.value)
item.children.forEach(item2 => { }
degure(item2)
const getTempData = async () => {
const { rows } = await tempResult({
mainModelId: 5,
modelId: curModel.value.id,
ex1: curNode.id,
createUser: user.userId
}) })
} tempData.value = rows?rows[0]:null
} }
const outputImg = () => { const reloadData = ref('')
let svg = window.document.getElementById('Mindmap_svg-wrapper_fgvb6') const tempData = ref(null)
// let das = svg.querySelectorAll('.Mindmap_root_fgvb6')[0] const mindMapRef = ref(null)
html2canvas(svg,{ onMounted(async () => {
scale: scale.value const { rows } = await modelList({ model: 5 })
}).then((canvas) => { curModel.value = rows.find((item) => item.name.indexOf('思维导图') != -1)
const dataUrl = canvas.toDataURL() await getTempData()
exportImg64File(dataUrl, `${curNode.itemtitle}思维导图`) reloadData.value = tempData.value?tempData.value.content:'';
await getPrompt()
mindMapRef.value.initData({
reloadData: reloadData.value,
title: title.value,
prompt: prompt.value,
url: url.value
}) })
// mm.value.fit() // showMap.value = true
}
//
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data)
//
getPrompt()
// ID
getChartId()
})
// initValue
watch(() => resMarkdown.value, () => {
const { root } = transformer.transform(resMarkdown.value)
degure(root)
rootData.value = [root]
console.log(rootData.value)
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.mindmap-header {
background: #fff;
height: 45px;
border-radius: 5px;
margin-bottom: 20px;
}
.main-left {
background: #fff;
border-radius: 5px;
.main-left-ipt {
height: 40px;
}
}
.main-right{
border: 1px solid #ccc;
svg {
width: 100%;
height: 100%;
}
}
</style> </style>

View File

@ -0,0 +1,267 @@
<template>
<div class="page-mindmap flex flex-col h-full">
<div class="mindmap-header flex items-center justify-between pl-5">
<div class="flex w-1/2 items-center justify-between">
<div>
<b>AI思维导图</b> <span class="ml-5">{{ curNode.itemtitle }}</span>
</div>
<div>
<template v-if="isEdit">
<el-button class="mr-3" @click="isEdit = false">取消</el-button>
<el-button type="success" class="mr-3" @click="isEdit = false">保存</el-button>
</template>
<!-- <el-button v-else type="primary" class="mr-3" @click="isEdit = true">编辑</el-button>-->
<!-- <el-select v-model="curMode" class="mr-3 w-30">
<el-option
v-for="item in modeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>-->
</div>
</div>
<div>
导出清晰度<el-input-number v-model="scale" :min="1" :max="10" />数字越大越清晰但图片大小会越大
<el-button @click="outputImg" type="primary">导出图片</el-button>
</div>
</div>
<div class="mindmap-main flex flex-1" style="overflow: auto;">
<div class="w-1/3 h-full p-3 main-left flex flex-col" v-loading="loadingAnswer" style="overflow: auto;">
<div class="flex flex-1" style="overflow: auto;">
<v-md-editor v-model="resMarkdown" :mode="isEdit ? 'edit' : 'preview'" />
</div>
<div class="main-left-ipt">
<el-input
v-model="textVal"
style="max-width: 600px"
placeholder="请输入信息对内容进一步调整"
class="input-with-select"
>
<template #append>
<el-icon size="20" style="cursor: pointer;" @click="sendMessage"><Position /></el-icon>
</template>
</el-input>
<!-- <el-input style="float: left;" v-model="textVal" size="large" placeholder="请输入信息对内容进一步调整" /><el-button>发送</el-button>-->
</div>
</div>
<div class="w-2/3 h-full main-right">
<mindmap v-if="rootData" :timetravel="true" :drag="true" :zoom="true" :edit="true"
:center-btn="true" :fit-btn="true" :ctm="true" :add-node-btn="true"
v-model="rootData"></mindmap>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed, watch } from 'vue'
import { Position } from "@element-plus/icons-vue"
import { sessionStore } from '@/utils/store'
import { modelList, completion, modelChat } from '@/api/mode/index.js'
import { createChart, sendChart } from '@/api/ai/index'
import { Transformer } from 'markmap-lib'
import mindmap from 'vue3-mindmap'
import html2canvas from 'html2canvas'
import 'vue3-mindmap/dist/style.css'
import {exportImg64File} from "@/utils/talkFile";
import axios from 'axios'
const transformer = new Transformer()
//
const curNode = reactive({})
const textVal = ref(``)
const loadingAnswer = ref(false)
const scale = ref(5)
// const textVal = computed(() => {
// return `${curNode.itemtitle}markdownmarkdown`
// })
const curMode = ref(2)
const modeOptions = ref([
{
label: '教学大模型',
value: 1
},
{
label: '知识库模型',
value: 2,
}
])
const isEdit = ref(false)
// AI
const resMarkdown = ref(``)
const params = reactive({
prompt: '',
dataset_id: '',
template: ''
})
const messages = ref([
{
role: 'user',
content: ''
}
])
const aiConversation = async () => {
console.log(prompt.value)
// return
// params.prompt = prompt.value.replace(/{}/g, '')
//
// let str = '-PPT'
// messages.value[0].content = str
params.prompt = prompt.value
//
loadingAnswer.value = true
if (curMode.value == 1) {
const res = await sendChart({
content: params.prompt,
conversationId: conversation_id.value,
stream: false
})
resMarkdown.value = res.data.answer.replaceAll('```markdown','').replaceAll('```', '')
}
//
else {
let data = {
"messages": [
{
"role": "user",
"content": params.prompt
}
]
}
const res = await text2mindmap(data).finally(() => {
loadingAnswer.value = false
})
console.log(res)
resMarkdown.value = res.data.answer.content.replaceAll('```markdown','').replaceAll('```', '')
}
}
const sendMessage = async () => {
loadingAnswer.value = true
rootData.value = null
let data = {
"messages": [
{
"role":"assiatant",
"content": resMarkdown.value
},
{
"role": "user",
"content": `${textVal.value}${prompt.value}`
}
]
}
let res = await text2mindmap(data).finally(() => {
loadingAnswer.value = false
})
resMarkdown.value = res.data.answer.content.replaceAll('```markdown','').replaceAll('```', '')
}
const text2mindmap =(data)=> {
return axios({
url: "https://ai.ysaix.com:7865/chat",
method: "post",
headers: {
"Content-Type": "application/json",
Accept: "*/*",
},
data:data
});
}
// prompt
const prompt = ref('')
const getPrompt = async () => {
const { rows } = await modelList({ model: 5 })
let str = rows.find((item) => item.name.indexOf('思维导图') != -1).prompt
str = str.replace('{学段}', curNode.edustage)
str = str.replace('{学科}', curNode.edusubject)
let bookV = curNode.roottitle + '版'
str = str.replace('{教材版本}', bookV)
str = str.replace('{课程名称}', `${curNode.itemtitle}`)
prompt.value = str
}
//
const conversation_id = ref('')
const getChartId = async () => {
const res = await createChart()
conversation_id.value = res.data.conversation_id
// ai
aiConversation()
}
const rootData = ref(null)
const degure = (item) => {
item.name = item.content.replace(/<[^>]*>/g, '');;
if (item.children && item.children.length) {
item.children.forEach(item2 => {
degure(item2)
})
}
}
const outputImg = () => {
let svg = window.document.getElementById('Mindmap_svg-wrapper_fgvb6')
// let das = svg.querySelectorAll('.Mindmap_root_fgvb6')[0]
html2canvas(svg,{
scale: scale.value
}).then((canvas) => {
const dataUrl = canvas.toDataURL()
exportImg64File(dataUrl, `${curNode.itemtitle}思维导图`)
})
// mm.value.fit()
}
//
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data)
//
getPrompt()
// ID
getChartId()
})
// initValue
watch(() => resMarkdown.value, () => {
const { root } = transformer.transform(resMarkdown.value)
degure(root)
rootData.value = [root]
console.log(rootData.value)
})
</script>
<style lang="scss" scoped>
.mindmap-header {
background: #fff;
height: 45px;
border-radius: 5px;
margin-bottom: 20px;
}
.main-left {
background: #fff;
border-radius: 5px;
.main-left-ipt {
height: 40px;
}
}
.main-right{
border: 1px solid #ccc;
svg {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,276 @@
<!--
initData参数
{
prompt: 思维导图prompt数据,
title: 标题
url: 请求地址
reloadData: 初始化数据如果为空则默认去获取
}
回调函数
emits: {
exportImg: 导出图片按钮回调
createMindMap: 创建思维导图回调,初始化生成
reloadMindMap: 重新加载思维导图回调对话重新生成
}
-->
<template>
<div class="page-mindmap">
<div class="mindmap-header">
<div class="mindmap-title">
<div>
<b>AI思维导图</b> <span class="">{{ props.title }}</span>
</div>
<div>
<template v-if="isEdit">
<el-button class="" @click="isEdit = false">取消</el-button>
<el-button type="success" class="" @click="isEdit = false">保存</el-button>
</template>
<!-- <el-button v-else type="primary" class="mr-3" @click="isEdit = true">编辑</el-button>-->
</div>
</div>
<div>
导出清晰度<el-input-number v-model="scale" :min="1" :max="10" />数字越大越清晰但图片大小会越大
<el-button @click="outputImg" type="primary">导出图片</el-button>
</div>
</div>
<div class="mindmap-main" style="overflow: auto;">
<div class="mindmap-main-left" v-loading="loadingAnswer" style="overflow: auto;">
<div class="main-left-content" style="overflow: auto;">
<v-md-editor v-model="resMarkdown" :mode="isEdit ? 'edit' : 'preview'" />
</div>
<div class="main-left-ipt">
<el-input
v-model="textVal"
style="max-width: 600px"
placeholder="请输入信息对内容进一步调整"
class="input-with-select"
>
<template #append>
<el-icon size="20" style="cursor: pointer;" @click="reConversation"><Position /></el-icon>
</template>
</el-input>
<!-- <el-input style="float: left;" v-model="textVal" size="large" placeholder="请输入信息对内容进一步调整" /><el-button>发送</el-button>-->
</div>
</div>
<div class="mindmap-main-right">
<mindmap v-if="rootData" :timetravel="true" :drag="true" :zoom="true" :edit="true"
:center-btn="true" :fit-btn="true" :ctm="true" :add-node-btn="true"
v-model="rootData"></mindmap>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'aiMindMap'
}
</script>
<script setup>
import { ref, reactive, onMounted, watch, defineExpose } from 'vue'
import { Position } from "@element-plus/icons-vue"
import { Transformer } from 'markmap-lib'
import mindmap from 'vue3-mindmap'
import html2canvas from 'html2canvas'
import 'vue3-mindmap/dist/style.css'
import axios from 'axios'
const transformer = new Transformer()
const emits = defineEmits(['exportImg', 'createMindMap', 'reloadMindMap'])
const props = reactive({
prompt: '',
url: '',
title: '',
reloadData: ''
})
const textVal = ref(``)
const loadingAnswer = ref(false)
const scale = ref(5)
const modeOptions = ref([
{
label: '教学大模型',
value: 1
},
{
label: '知识库模型',
value: 2,
}
])
const isEdit = ref(false)
// AI
const resMarkdown = ref(``)
const params = reactive({
prompt: '',
dataset_id: '',
template: ''
})
const messages = ref([
{
role: 'user',
content: ''
}
])
const aiConversation = async () => {
params.prompt = props.prompt
loadingAnswer.value = true
let data = {
"messages": [
{
"role": "user",
"content": params.prompt
}
]
}
const res = await text2mindmap(data).finally(() => {
loadingAnswer.value = false
})
resMarkdown.value = res.data.answer.content.replaceAll('```markdown','').replaceAll('```', '')
emits('createMindMap', resMarkdown.value, res)
}
const reConversation = async () => {
loadingAnswer.value = true
rootData.value = null
let data = {
"messages": [
{
"role":"assiatant",
"content": resMarkdown.value
},
{
"role": "user",
"content": `${textVal.value}${props.prompt}`
}
]
}
let res = await text2mindmap(data).finally(() => {
loadingAnswer.value = false
})
resMarkdown.value = res.data.answer.content.replaceAll('```markdown','').replaceAll('```', '')
emits('reloadMindMap', resMarkdown.value, res)
}
const text2mindmap =(data)=> {
return axios({
url: props.url,
method: "post",
headers: {
"Content-Type": "application/json",
Accept: "*/*",
},
data:data
});
}
const rootData = ref(null)
const degure = (item) => {
item.name = item.content.replace(/<[^>]*>/g, '');;
if (item.children && item.children.length) {
item.children.forEach(item2 => {
degure(item2)
})
}
}
const outputImg = () => {
let svg = window.document.getElementById('Mindmap_svg-wrapper_fgvb6')
html2canvas(svg,{
scale: scale.value
}).then((canvas) => {
const dataUrl = canvas.toDataURL()
emits('exportImg', dataUrl)
})
}
const initData = (datas) => {
Object.assign(props, datas)
//
if (props.reloadData) {
resMarkdown.value = props.reloadData
}else {
aiConversation()
}
}
defineExpose({
initData,
outputImg,
aiConversation,
reConversation
})
//
onMounted(() => {
// initData()
})
// initValue
watch(() => resMarkdown.value, () => {
const { root } = transformer.transform(resMarkdown.value)
degure(root)
rootData.value = [root]
})
</script>
<style lang="scss" scoped>
.page-mindmap{
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
width: 100%;
.mindmap-header {
background: #fff;
height: 45px;
border-radius: 5px;
margin-bottom: 20px;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.mindmap-main{
display: flex;
flex: 1;
flex-direction: row;
.mindmap-main-left {
background: #fff;
flex-basis: 34%;
border-radius: 5px;
display: flex;
flex-direction: column;
.main-left-content{
flex: 1;
}
.main-left-ipt {
height: 50px;
display: flex;
justify-content: center;
align-items: center;
}
}
.mindmap-main-right{
border: 1px solid #ccc;
flex-basis: 66%;
svg {
width: 100%;
height: 100%;
}
}
}
}
</style>

View File

@ -438,17 +438,23 @@ const toRousrceUrl = async (o) => {
if (!!o.src) { // src if (!!o.src) { // src
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src) const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
const isBlobUrl = /^blob:/.test(o.src) const isBlobUrl = /^blob:/.test(o.src)
let onLineUrl = '' // 线 let onLineUrl = '', typeExt = '' // 线|
if (!!o.zipPath) onLineUrl = pptMedia[o.zipPath] || '' // if (!!o.zipPath) {
onLineUrl = pptMedia[o.zipPath] || '' //
typeExt = o.zipPath.split('.').pop()
}
if (onLineUrl) o.src = onLineUrl // 线 if (onLineUrl) o.src = onLineUrl // 线
else { // else { //
if (isBase64) { // if (isBase64) { //
const url = await getOnlineFileUrl(o.src) if(!typeExt) typeExt = 'png'
const url = await getOnlineFileUrl(o.src, typeExt)
url &&(o.src = url)
url && o.zipPath && (pptMedia[o.zipPath] = url) // url && o.zipPath && (pptMedia[o.zipPath] = url) //
} else if (isBlobUrl) { // } else if (isBlobUrl) { //
if(!typeExt) typeExt = o.type=='video'?'mp4':'mp3'
const res = await fetch(o.src) const res = await fetch(o.src)
const blob = await res.blob() const blob = await res.blob()
const url = await getOnlineFileUrl(blob, o.type=='video'?'mp4':'mp3') const url = await getOnlineFileUrl(blob, typeExt)
URL.revokeObjectURL(o.src) // URL.revokeObjectURL(o.src) //
url &&(o.src = url) url &&(o.src = url)
url && o.zipPath && (pptMedia[o.zipPath] = url) // url && o.zipPath && (pptMedia[o.zipPath] = url) //

View File

@ -653,17 +653,23 @@ export default {
if (!!o.src) { // src if (!!o.src) { // src
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src) const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
const isBlobUrl = /^blob:/.test(o.src) const isBlobUrl = /^blob:/.test(o.src)
let onLineUrl = '' // 线 let onLineUrl = '', typeExt = '' // 线|
if (!!o.zipPath) onLineUrl = this.pptMedia[o.zipPath] || '' // if (!!o.zipPath) {
onLineUrl = this.pptMedia[o.zipPath] || '' //
typeExt = o.zipPath.split('.').pop()
}
if (onLineUrl) o.src = onLineUrl // 线 if (onLineUrl) o.src = onLineUrl // 线
else { // else { //
if (isBase64) { // if (isBase64) { //
const url = await this.getOnlineFileUrl(o.src) if(!typeExt) typeExt = 'png'
const url = await this.getOnlineFileUrl(o.src, typeExt)
url &&(o.src = url)
url && o.zipPath && (this.pptMedia[o.zipPath] = url) // url && o.zipPath && (this.pptMedia[o.zipPath] = url) //
} else if (isBlobUrl) { // } else if (isBlobUrl) { //
if(!typeExt) typeExt = o.type=='video'?'mp4':'mp3'
const res = await fetch(o.src) const res = await fetch(o.src)
const blob = await res.blob() const blob = await res.blob()
const url = await this.getOnlineFileUrl(blob, o.type=='video'?'mp4':'mp3') const url = await this.getOnlineFileUrl(blob, typeExt)
URL.revokeObjectURL(o.src) // URL.revokeObjectURL(o.src) //
url &&(o.src = url) url &&(o.src = url)
url && o.zipPath && (this.pptMedia[o.zipPath] = url) // url && o.zipPath && (this.pptMedia[o.zipPath] = url) //