Merge branch 'main' into zouyf_dev
This commit is contained in:
commit
cdf8fd2733
|
@ -1,6 +1,8 @@
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VITE_APP_TITLE = 文枢课堂
|
VITE_APP_TITLE = 文枢课堂
|
||||||
|
|
||||||
|
VITE_APP_ID = 'aix-win-ws'
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
VITE_APP_ENV = 'production'
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
|
|
4
.env.yc
4
.env.yc
|
@ -1,5 +1,7 @@
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VITE_APP_TITLE = 文枢课堂
|
VITE_APP_TITLE = 永川中小学AI教学系统
|
||||||
|
|
||||||
|
VITE_APP_ID = 'aix-win-ws-yc'
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
VITE_APP_ENV = 'production'
|
VITE_APP_ENV = 'production'
|
||||||
|
|
2
.env.yc2
2
.env.yc2
|
@ -1,6 +1,8 @@
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VITE_APP_TITLE = 实训教学
|
VITE_APP_TITLE = 实训教学
|
||||||
|
|
||||||
|
VITE_APP_ID = 'aix-win-ws-yc2'
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
VITE_APP_ENV = 'production'
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ asarUnpack:
|
||||||
nsis:
|
nsis:
|
||||||
oneClick: false
|
oneClick: false
|
||||||
allowToChangeInstallationDirectory: true
|
allowToChangeInstallationDirectory: true
|
||||||
artifactName: ${name}-yc-${version}-setup.${ext}
|
artifactName: ${name}-${version}-setup.${ext}
|
||||||
shortcutName: ${productName}
|
shortcutName: ${productName}
|
||||||
uninstallDisplayName: ${productName}
|
uninstallDisplayName: ${productName}
|
||||||
createDesktopShortcut: always
|
createDesktopShortcut: always
|
||||||
|
|
|
@ -17,7 +17,7 @@ asarUnpack:
|
||||||
nsis:
|
nsis:
|
||||||
oneClick: false
|
oneClick: false
|
||||||
allowToChangeInstallationDirectory: true
|
allowToChangeInstallationDirectory: true
|
||||||
artifactName: ${name}-ycsx-${version}-setup.${ext}
|
artifactName: ${name}-${version}-setup.${ext}
|
||||||
shortcutName: ${productName}
|
shortcutName: ${productName}
|
||||||
uninstallDisplayName: ${productName}
|
uninstallDisplayName: ${productName}
|
||||||
createDesktopShortcut: always
|
createDesktopShortcut: always
|
||||||
|
|
|
@ -10,14 +10,13 @@
|
||||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||||
"start": "electron-vite preview",
|
"start": "electron-vite preview",
|
||||||
"dev": "electron-vite dev",
|
"dev": "electron-vite dev",
|
||||||
"build": "electron-vite build",
|
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"build:unpack": "npm run build && electron-builder --dir",
|
"build:unpack": "npm run build && electron-builder --dir",
|
||||||
"build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml",
|
"build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml",
|
||||||
"build:test": "electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml",
|
"build:test": "node updatePackageJsonName.js && electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml",
|
||||||
"build:prod": "electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml",
|
"build": "node updatePackageJsonName.js && electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml",
|
||||||
"build:yc": "electron-vite build --mode yc && electron-builder --win --config ./electron-builder-yc.yml",
|
"build:yc": "node updatePackageJsonName.js && electron-vite build --mode yc && electron-builder --win --config ./electron-builder-yc.yml",
|
||||||
"build:yc2": "electron-vite build --mode yc2 && electron-builder --win --config ./electron-builder-yc2.yml",
|
"build:yc2": "node updatePackageJsonName.js && electron-vite build --mode yc2 && electron-builder --win --config ./electron-builder-yc2.yml",
|
||||||
"build:lt": "electron-vite build --mode lt && electron-builder --win --config ./electron-builder-lt.yml",
|
"build:lt": "electron-vite build --mode lt && electron-builder --win --config ./electron-builder-lt.yml",
|
||||||
"build:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml",
|
"build:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml",
|
||||||
"build:linux": "npm run build && electron-builder --linux"
|
"build:linux": "npm run build && electron-builder --linux"
|
||||||
|
|
|
@ -136,14 +136,6 @@ export function syllabusList(params) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 大纲详情
|
|
||||||
export function syllabuss(id) {
|
|
||||||
return request({
|
|
||||||
url: '/education/generate/' + id,
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除大纲
|
// 删除大纲
|
||||||
export function removeSyllabus(id) {
|
export function removeSyllabus(id) {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
@ -14,17 +14,17 @@
|
||||||
<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>
|
||||||
<span class="tree-label" style="color: #A5B3CA" >
|
<div class="tree-label" style="color: #A5B3CA" >
|
||||||
{{ node.label }}
|
{{ node.label }}
|
||||||
</span>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="tree-label-wrap">
|
<div v-else class="tree-label-wrap">
|
||||||
<el-tooltip effect="light" placement="right" >
|
<el-tooltip effect="light" placement="right" >
|
||||||
<template #content> {{ node.label }}</template>
|
<template #content> {{ node.label }}</template>
|
||||||
<span class="tree-label">
|
<div class="tree-label">
|
||||||
{{ node.label }}
|
{{ node.label }}
|
||||||
</span>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -44,8 +44,9 @@
|
||||||
|
|
||||||
<div class="textbook-container">
|
<div class="textbook-container">
|
||||||
<el-scrollbar height="450px">
|
<el-scrollbar height="450px">
|
||||||
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
|
<div
|
||||||
:key="item.id" @click="changeBook(item)">
|
v-for="item in subjectList" :key="item.id" class="textbook-item flex"
|
||||||
|
:class="curBook.data.id == item.id ? 'active-item' : ''" @click="changeBook(item)">
|
||||||
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
|
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
|
||||||
<div v-else class="textbook-img">
|
<div v-else class="textbook-img">
|
||||||
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
|
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
|
||||||
|
@ -341,6 +342,7 @@ onMounted( async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-label-wrap, .tree-label {
|
.tree-label-wrap, .tree-label {
|
||||||
|
max-width: 100%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
|
@ -407,11 +407,52 @@ export const dataSetJson = {
|
||||||
"教材-高中-数学": "e03aa4fe9fd011ef91270242ac140006",
|
"教材-高中-数学": "e03aa4fe9fd011ef91270242ac140006",
|
||||||
"教材-高中-地理": "270516829fd111efb13c0242ac140006",
|
"教材-高中-地理": "270516829fd111efb13c0242ac140006",
|
||||||
"教材-高中-政治": "a2f0b247b85d11ef84290242ac140005",
|
"教材-高中-政治": "a2f0b247b85d11ef84290242ac140005",
|
||||||
|
|
||||||
|
"考试-小学-语文": "570f7ed2cc9d11ef9e070242ac140002",
|
||||||
|
"考试-小学-数学": "983270b8cc9d11efbbd80242ac140002",
|
||||||
|
"考试-小学-英语": "d5f80e4ccc9d11ef96fa0242ac140002",
|
||||||
"课标-小学-科学": "935cfec8bf6a11ef98950242ac140006",
|
"课标-小学-科学": "935cfec8bf6a11ef98950242ac140006",
|
||||||
"课标-小学-数学": "3c4e298fbf7911ef8e8b0242ac140002",
|
"课标-小学-数学": "3c4e298fbf7911ef8e8b0242ac140002",
|
||||||
"课标-小学-语文": "f76f1aa5bf7111ef90c80242ac140002",
|
"课标-小学-语文": "f76f1aa5bf7111ef90c80242ac140002",
|
||||||
|
"课标-小学-道德": "8da87869cbd711ef92280242ac140002",
|
||||||
|
"课标-小学-英语": "dc963316cbd811ef8d820242ac140002",
|
||||||
|
"课标-小学-劳动": "fc047d81cbdc11efa1740242ac140002",
|
||||||
"教材-小学-科学": "935cfec8bf6a11ef98950242ac140006",
|
"教材-小学-科学": "935cfec8bf6a11ef98950242ac140006",
|
||||||
"教材-小学-数学": "3c4e298fbf7911ef8e8b0242ac140002",
|
"教材-小学-数学": "3c4e298fbf7911ef8e8b0242ac140002",
|
||||||
"教材-小学-语文": "f76f1aa5bf7111ef90c80242ac140002",
|
"教材-小学-语文": "f76f1aa5bf7111ef90c80242ac140002",
|
||||||
|
"教材-小学-道德": "8da87869cbd711ef92280242ac140002",
|
||||||
|
"教材-小学-英语": "dc963316cbd811ef8d820242ac140002",
|
||||||
|
"教材-小学-劳动": "fc047d81cbdc11efa1740242ac140002",
|
||||||
|
|
||||||
|
"教材-初中-道德与法治": "df9f3ccccbdd11ef9e550242ac140002",
|
||||||
|
"教材-初中-语文": "3770ad18cbde11efadaa0242ac140002",
|
||||||
|
"教材-初中-数学": "8cc0a799cbde11ef8b440242ac140002",
|
||||||
|
"教材-初中-英语": "07b58ca2cbdf11efaa180242ac140002",
|
||||||
|
"教材-初中-物理": "86f2c018cbf211ef9d6a0242ac140002",
|
||||||
|
"教材-初中-化学": "c7b34790cbf211ef92350242ac140002",
|
||||||
|
"教材-初中-生物": "083ac3edcbf311efaad30242ac140002",
|
||||||
|
"教材-初中-地理": "7ee584e1cbf311efbd270242ac140002",
|
||||||
|
"教材-初中-历史": "8ae07971cbf411ef81e70242ac140002",
|
||||||
|
"教材-初中-信息技术": "ca476233cbf411efa9860242ac140002",
|
||||||
|
"课标-初中-道德与法治": "df9f3ccccbdd11ef9e550242ac140002",
|
||||||
|
"课标-初中-语文": "3770ad18cbde11efadaa0242ac140002",
|
||||||
|
"课标-初中-数学": "8cc0a799cbde11ef8b440242ac140002",
|
||||||
|
"课标-初中-英语": "07b58ca2cbdf11efaa180242ac140002",
|
||||||
|
"课标-初中-物理": "86f2c018cbf211ef9d6a0242ac140002",
|
||||||
|
"课标-初中-化学": "c7b34790cbf211ef92350242ac140002",
|
||||||
|
"课标-初中-生物": "083ac3edcbf311efaad30242ac140002",
|
||||||
|
"课标-初中-地理": "7ee584e1cbf311efbd270242ac140002",
|
||||||
|
"课标-初中-历史": "8ae07971cbf411ef81e70242ac140002",
|
||||||
|
"课标-初中-信息技术": "ca476233cbf411efa9860242ac140002",
|
||||||
|
|
||||||
|
"考试-初中-语文": "6be6d201cc0111ef89100242ac140002",
|
||||||
|
"考试-初中-数学": "d764b539cc0111ef8f1b0242ac140002",
|
||||||
|
"考试-初中-英语": "3477cff7cc9911efbfa50242ac140002",
|
||||||
|
"考试-初中-政治": "7ac981d8cc9a11efa5dc0242ac140002",
|
||||||
|
"考试-初中-历史": "c058a33acc9a11efb7f00242ac140002",
|
||||||
|
"考试-初中-地理": "5548224ecc9b11efa76d0242ac140002",
|
||||||
|
"考试-初中-生物": "206c5fd3cc9c11ef990f0242ac140002",
|
||||||
|
"考试-初中-物理": "93039442cc9c11ef89b10242ac140002",
|
||||||
|
"考试-初中-化学": "f8d78002cc9c11efbbf60242ac140002",
|
||||||
"鉴权": "ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm"
|
"鉴权": "ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm"
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,12 +107,12 @@ const getBackGroundV2 = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createOutlineV2 = async (params) => {
|
const createOutlineV2 = async (data) => {
|
||||||
try {
|
try {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
url:"/api/aipptV2/createOutlineV2",
|
url:"/api/aipptV2/createOutlineV2",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
params
|
data
|
||||||
});
|
});
|
||||||
console.log("createOutline response:", response);
|
console.log("createOutline response:", response);
|
||||||
|
|
||||||
|
@ -133,6 +133,18 @@ const createPPTV2 = async (data) => {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createPptByOutline = async (data) => {
|
||||||
|
try {
|
||||||
|
const response = await req("/api/aipptV2/createPptByOutline", "POST", data);
|
||||||
|
console.log("createOutline response:", response);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("请求失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
const getProgressV2 = async (id) => {
|
const getProgressV2 = async (id) => {
|
||||||
try {
|
try {
|
||||||
const response = await req(`/api/aipptV2/progressV2?sid=${id}`, "GET");
|
const response = await req(`/api/aipptV2/progressV2?sid=${id}`, "GET");
|
||||||
|
@ -143,4 +155,4 @@ const getProgressV2 = async (id) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { createOutline, getBackGround, createPPT, getProgress, getBackGroundV2, createOutlineV2, createPPTV2, getProgressV2, createByOutline };
|
export { createOutline, getBackGround, createPPT, getProgress, getBackGroundV2, createOutlineV2, createPPTV2, getProgressV2, createByOutline, createPptByOutline };
|
||||||
|
|
|
@ -34,8 +34,10 @@
|
||||||
import AiPptist from './ai-pptistV2.vue';
|
import AiPptist from './ai-pptistV2.vue';
|
||||||
import {Select} from "@element-plus/icons-vue";
|
import {Select} from "@element-plus/icons-vue";
|
||||||
import {ref, defineEmits, onMounted} from "vue";
|
import {ref, defineEmits, onMounted} from "vue";
|
||||||
import {createPPTV2, getBackGroundV2, getProgressV2} from "@/utils/ppt-request";
|
import {createPPTV2, getBackGroundV2, getProgressV2, createPptByOutline} from "@/utils/ppt-request";
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
const userStore = useUserStore()
|
||||||
const model = defineModel()
|
const model = defineModel()
|
||||||
const emit = defineEmits(['addSuccess', 'close-dialogs'])
|
const emit = defineEmits(['addSuccess', 'close-dialogs'])
|
||||||
const backGroundList = ref([]);
|
const backGroundList = ref([]);
|
||||||
|
@ -60,7 +62,7 @@
|
||||||
const outlineData = ref({
|
const outlineData = ref({
|
||||||
query: '', // 用户要求(最多8000字)
|
query: '', // 用户要求(最多8000字)
|
||||||
templateId: '', // ppt生成主题
|
templateId: '', // ppt生成主题
|
||||||
author: 'AIX平台',
|
author: userStore.user.nickName,
|
||||||
isFigure: false, // 是否自动配图
|
isFigure: false, // 是否自动配图
|
||||||
search: true,
|
search: true,
|
||||||
language: "cn"
|
language: "cn"
|
||||||
|
@ -68,9 +70,10 @@
|
||||||
const percentage = ref(0);
|
const percentage = ref(0);
|
||||||
const outlineCreatePPT = () => {
|
const outlineCreatePPT = () => {
|
||||||
const newOutlineData = { ...outlineData.value, };
|
const newOutlineData = { ...outlineData.value, };
|
||||||
newOutlineData.query = props.dataList.outline;
|
newOutlineData.outline = props.dataList.outline;
|
||||||
|
newOutlineData.query = "通过传入大纲帮我生成相应的PPT课件"
|
||||||
createPPTLoading.value = true;
|
createPPTLoading.value = true;
|
||||||
createPPTV2(newOutlineData).then((res) => {
|
createPptByOutline(newOutlineData).then((res) => {
|
||||||
console.log(res, "正在生成中");
|
console.log(res, "正在生成中");
|
||||||
createPPTLoading.value = false;
|
createPPTLoading.value = false;
|
||||||
activeStep.value = 2
|
activeStep.value = 2
|
||||||
|
|
|
@ -1,90 +1,155 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="display: flex;">
|
<div>
|
||||||
<div style="margin-left: 15px">
|
<div style="display: flex; align-items: center;">
|
||||||
<el-dropdown @command="handleUserEduStage">
|
<div style="margin-left: 15px">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<el-button class="custom-button" type="default" round >{{ userStore.edustage }}
|
<el-button class="custom-button" type="default" round>{{ eduStage || userStore.edustage }}</el-button>
|
||||||
<el-icon><ArrowDown /></el-icon>
|
</span>
|
||||||
</el-button>
|
</div>
|
||||||
</span>
|
<div style="margin-left: 15px">
|
||||||
<template #dropdown>
|
<span class="el-dropdown-link">
|
||||||
<el-dropdown-menu>
|
<el-button class="custom-button" type="default" round>{{ eduSubject || userStore.edusubject }}</el-button>
|
||||||
<el-dropdown-item command="幼儿园">幼儿园</el-dropdown-item>
|
</span>
|
||||||
<el-dropdown-item command="小学">小学</el-dropdown-item>
|
</div>
|
||||||
<el-dropdown-item command="初中">初中</el-dropdown-item>
|
<el-text
|
||||||
<el-dropdown-item command="高中">高中</el-dropdown-item>
|
type="primary"
|
||||||
</el-dropdown-menu>
|
style="margin-left: 10px; cursor: pointer;"
|
||||||
</template>
|
@click="handleUserEduSubject"
|
||||||
</el-dropdown>
|
v-popover="popoverRef"
|
||||||
|
ref="buttonRef"
|
||||||
|
>修改</el-text>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-left: 15px">
|
<!-- 级联选择框 -->
|
||||||
<el-dropdown @command="handleUserEduSubject">
|
<el-popover
|
||||||
<span class="el-dropdown-link">
|
ref="popoverRef"
|
||||||
<el-button class="custom-button" type="default" round>{{ userStore.edusubject }}
|
:virtual-ref="buttonRef"
|
||||||
<el-icon><ArrowDown /></el-icon>
|
trigger="click"
|
||||||
</el-button>
|
title="修改学科学段"
|
||||||
</span>
|
virtual-triggering
|
||||||
<template #dropdown>
|
placement="right"
|
||||||
<el-dropdown-menu>
|
width="300px"
|
||||||
<template v-for="(item, index) in subjectList">
|
>
|
||||||
<el-dropdown-item v-if="item.edustage == userStore.edustage" :command="item.itemtitle">{{
|
<div class="sidebar">
|
||||||
item.itemtitle }}</el-dropdown-item>
|
<div class="nav-level-one">
|
||||||
|
<template v-for="(item,index) in options" :key="index">
|
||||||
|
<div :class="[currentIndex===index?'active':'','nav-item']" @click="handleUserEduStage(item.label,index)">
|
||||||
|
<span class="nav-text">{{ item.label }}</span>
|
||||||
|
<el-icon class="icon"><ArrowRight /></el-icon>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown-menu>
|
</div>
|
||||||
</template>
|
<div class="nav-level-two">
|
||||||
</el-dropdown>
|
<el-scrollbar height="180">
|
||||||
|
<template v-for="(item,index) in cascadeOptions" :key="index">
|
||||||
|
<div class="nav-item" @click="handleChange(item)">
|
||||||
|
<span class="nav-text">{{ item.label }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import {ArrowDown} from '@element-plus/icons-vue'
|
import { onMounted, ref, unref } from 'vue';
|
||||||
import { onMounted,ref } from 'vue';
|
|
||||||
import { listEvaluation } from '@/api/subject/index'
|
import { listEvaluation } from '@/api/subject/index'
|
||||||
import {sessionStore} from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
|
import { ArrowRight } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
const userStore = useUserStore().user
|
const userStore = useUserStore().user
|
||||||
const subjectList = ref([])
|
const subjectList = ref([])
|
||||||
|
const buttonRef = ref()
|
||||||
|
const popoverRef = ref()
|
||||||
|
// 获取当前选择的学段
|
||||||
|
const currentIndex = ref(0)
|
||||||
|
// 当前的学科和学段
|
||||||
|
const eduStage = ref('')
|
||||||
|
const eduSubject = ref('')
|
||||||
|
|
||||||
|
// 学段固定
|
||||||
|
const options = ref([
|
||||||
|
{
|
||||||
|
value: '幼儿园',
|
||||||
|
label: '幼儿园',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '小学',
|
||||||
|
label: '小学',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '初中',
|
||||||
|
label: '初中',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '高中',
|
||||||
|
label: '高中',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
// 选择学段时后筛选学科
|
||||||
|
const cascadeOptions = ref([]);
|
||||||
// 获取基础的学科
|
// 获取基础的学科
|
||||||
const getSubject = () => {
|
const getSubject = () => {
|
||||||
//没有学科则不进行下面的步骤
|
if (!userStore.subject) return;
|
||||||
if(!userStore.subject) return
|
|
||||||
listEvaluation({ itemkey: 'subject', pageSize: 500 }).then((res) => {
|
listEvaluation({ itemkey: 'subject', pageSize: 500 }).then((res) => {
|
||||||
const arr = userStore.subject.split(',')
|
const arr = userStore.subject.split(',');
|
||||||
subjectList.value = res.rows.filter(item => arr.includes(String(item.id))).map(items => items)
|
subjectList.value = res.rows.filter(item => arr.includes(String(item.id)));
|
||||||
console.log(subjectList,'subjectList');
|
if (subjectList.value.length === 0) return;
|
||||||
|
options.value.forEach(option => {
|
||||||
|
option.children = subjectList.value
|
||||||
|
.filter(items => items.edustage === option.label)
|
||||||
|
.map(item => ({ value: item.id, label: item.edusubject }));
|
||||||
|
});
|
||||||
|
// 首次获取学段和学科列表
|
||||||
|
const cIndex = options.value.findIndex(item => item.label === userStore.edustage);
|
||||||
|
cascadeOptions.value = [...options.value[cIndex].children]
|
||||||
|
currentIndex.value = cIndex
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 选择学段
|
// 选择学段
|
||||||
const handleUserEduStage = (item) => {
|
const handleUserEduStage = (stage,index) => {
|
||||||
userStore.edustage = item
|
const currentData = options.value.find(item => item.label === stage);
|
||||||
sessionStore.set('edustageSelf',item)
|
cascadeOptions.value = [...currentData.children]
|
||||||
if(item === '幼儿园'){
|
currentIndex.value = index
|
||||||
// 默认语文
|
|
||||||
userStore.edusubject = '语文'
|
|
||||||
sessionStore.set('edusubjectSelf','语文')
|
|
||||||
}
|
|
||||||
else if(item === '高中' && userStore.edusubject === "道德与法治"){
|
|
||||||
// 默认语文
|
|
||||||
userStore.edusubject = '政治'
|
|
||||||
sessionStore.set('edusubjectSelf','政治')
|
|
||||||
}
|
|
||||||
else if(item != '高中' && userStore.edusubject === "政治"){
|
|
||||||
// 默认语文
|
|
||||||
userStore.edusubject = '道德与法治'
|
|
||||||
sessionStore.set('edusubjectSelf','道德与法治')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择学科
|
// 选择学科
|
||||||
const handleUserEduSubject = (item) => {
|
const handleUserEduSubject = () => {
|
||||||
userStore.edusubject = item;
|
unref(popoverRef).popperRef?.delayHide?.();
|
||||||
sessionStore.set('edusubjectSelf',item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleChange = (item) => {
|
||||||
|
const id = item.value
|
||||||
|
const current = subjectList.value.find(item => item.id === id);
|
||||||
|
if (current) {
|
||||||
|
eduSubject.value = item.label;
|
||||||
|
eduStage.value = current.edustage;
|
||||||
|
sessionStore.set('edustageSelf', eduStage.value);
|
||||||
|
|
||||||
|
if (eduStage.value === '高中' && eduSubject.value === "道德与法治") {
|
||||||
|
eduSubject.value = '政治';
|
||||||
|
sessionStore.set('edusubjectSelf', '政治');
|
||||||
|
} else if (eduStage.value !== '高中' && eduSubject.value === "政治") {
|
||||||
|
eduSubject.value = '道德与法治';
|
||||||
|
sessionStore.set('edusubjectSelf', '道德与法治');
|
||||||
|
} else {
|
||||||
|
sessionStore.set('edusubjectSelf', eduSubject.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSubject()
|
getSubject();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.custom-button {
|
.custom-button {
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -93,10 +158,58 @@ onMounted(() => {
|
||||||
outline-offset: none;
|
outline-offset: none;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-button i {
|
.custom-button i {
|
||||||
margin-left: 8px; /* 调整图标与文字之间的间距 */
|
margin-left: 8px; /* 调整图标与文字之间的间距 */
|
||||||
}
|
}
|
||||||
|
.sidebar {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-level-one,.nav-level-two {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
.nav-level-two .nav-item{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover {
|
||||||
|
background-color: rgb(245, 247, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-arrow {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-level-one.nav-item:first-child.nav-text {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
.active .nav-text{
|
||||||
|
font-weight: bold;
|
||||||
|
color:rgb(64, 158, 255)
|
||||||
|
}
|
||||||
|
.active .icon{
|
||||||
|
font-weight: bold;
|
||||||
|
color:rgb(64, 158, 255)
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="info-name">{{ state.user.nickName }}</div>
|
<div class="info-name">{{ state.user.nickName }}</div>
|
||||||
<div class="infomation" v-if="isStadium() !== true" >
|
<div class="infomation" v-if="isStadium() !== true" >
|
||||||
<selectClass v-if="!isSubject"/>
|
<SelectClass v-if="!isSubject"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,7 +34,7 @@ import resetPwd from './resetPwd.vue'
|
||||||
import { getUserProfile } from '@/api/system/user'
|
import { getUserProfile } from '@/api/system/user'
|
||||||
import pkc from "../../../../../package.json"
|
import pkc from "../../../../../package.json"
|
||||||
//选择学校和班级
|
//选择学校和班级
|
||||||
import selectClass from './components/selectClass.vue'
|
import SelectClass from './components/selectClass.vue'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
|
||||||
const version = ref(pkc.version)
|
const version = ref(pkc.version)
|
||||||
|
|
|
@ -1,282 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog v-model="isDialog" :show-close="false" width="800" destroy-on-close>
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="isDialog = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="dialog-content">
|
|
||||||
<el-scrollbar height="400px">
|
|
||||||
<div class="chart-con flex">
|
|
||||||
<template v-for="item in msgList">
|
|
||||||
<div class="flex-end flex" v-if="item.type == 'user'">
|
|
||||||
<div class="chart-item user">{{ item.msg }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex-start flex" v-else>
|
|
||||||
<div class="flex" v-loading="!item.msg">
|
|
||||||
<div class="chart-item robot">{{ item.msg }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-end replace-item">
|
|
||||||
<span @click="saveAdjust(item)"><i class="iconfont icon-tihuan"></i>替换分析结果</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="loaded" class="chart-loading">
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
<div class="input-box flex">
|
|
||||||
<el-input v-model="textarea" @keyup.enter="send" :disabled="loaded" />
|
|
||||||
<div class="ipt-icon" @click="send">
|
|
||||||
<i class="iconfont icon-fasong"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
|
||||||
import { completion } from '@/api/mode/index'
|
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import { sendChart } from '@/api/ai/index'
|
|
||||||
|
|
||||||
const textarea = ref('')
|
|
||||||
|
|
||||||
const isDialog = defineModel()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return { name: '11' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
curMode:{
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
conversation_id: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['saveEdit'])
|
|
||||||
|
|
||||||
const loaded = ref(false)
|
|
||||||
|
|
||||||
const msgList = ref([])
|
|
||||||
|
|
||||||
const send = () => {
|
|
||||||
if (loaded.value) return
|
|
||||||
msgList.value.push({
|
|
||||||
type: 'user',
|
|
||||||
msg: textarea.value
|
|
||||||
})
|
|
||||||
loaded.value = true
|
|
||||||
getConversation(textarea.value)
|
|
||||||
textarea.value = ''
|
|
||||||
}
|
|
||||||
const curNode = reactive({})
|
|
||||||
|
|
||||||
const params = reactive(
|
|
||||||
{
|
|
||||||
prompt: '',
|
|
||||||
dataset_id: '',
|
|
||||||
template: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 大模型对话
|
|
||||||
const getConversation = async (val) => {
|
|
||||||
try {
|
|
||||||
params.prompt = `按照${val}的要求,针对${curNode.edustage}${curNode.edusubject}课标,对${curNode.itemtitle}进行教学分析`
|
|
||||||
params.template = props.item.prompt
|
|
||||||
|
|
||||||
let data = null;
|
|
||||||
// 教学大模型
|
|
||||||
if(props.curMode == 1){
|
|
||||||
const res = await sendChart({
|
|
||||||
content: params.prompt,
|
|
||||||
conversationId: props.conversation_id,
|
|
||||||
stream: false
|
|
||||||
})
|
|
||||||
data = res.data
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// 知识库模型
|
|
||||||
const res = await completion(params)
|
|
||||||
data = res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
msgList.value.push({
|
|
||||||
type: 'robot',
|
|
||||||
msg: data.answer,
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
loaded.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveAdjust = (item) => {
|
|
||||||
isDialog.value = false
|
|
||||||
emitter.emit('onSaveAdjust', item.msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
// 框架设计 用课标的dataset_id
|
|
||||||
let jsonKey = `课标-${data.edustage}-${data.edusubject}`
|
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.custom-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
|
|
||||||
.chart-con {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
.flex-end {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-start {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-start;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-item {
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
background: #F2F2F2;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.robot {
|
|
||||||
background: #409EFF;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.replace-item {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #409EFF;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-box {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.ipt-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 5px;
|
|
||||||
|
|
||||||
.icon-fasong {
|
|
||||||
font-size: 26px;
|
|
||||||
color: #409EFF;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading,
|
|
||||||
.chart-loading>div {
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading {
|
|
||||||
display: block;
|
|
||||||
font-size: 0;
|
|
||||||
color: #66b1ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading.la-dark {
|
|
||||||
color: #66b1ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div {
|
|
||||||
display: inline-block;
|
|
||||||
float: none;
|
|
||||||
background-color: currentColor;
|
|
||||||
border: 0 solid currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading {
|
|
||||||
width: 54px;
|
|
||||||
height: 18px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div:nth-child(1) {
|
|
||||||
animation-delay: -200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div:nth-child(2) {
|
|
||||||
animation-delay: -100ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div:nth-child(3) {
|
|
||||||
animation-delay: 0ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 100%;
|
|
||||||
margin-right: 4px;
|
|
||||||
animation: ball-pulse 1s ease infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ball-pulse {
|
|
||||||
|
|
||||||
0%,
|
|
||||||
60%,
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
30% {
|
|
||||||
opacity: 0.1;
|
|
||||||
transform: scale(0.01);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -13,30 +13,34 @@
|
||||||
<i class="iconfont icon-shanchu"></i>
|
<i class="iconfont icon-shanchu"></i>
|
||||||
删除大纲
|
删除大纲
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button type="primary" @click="isEdit = true">
|
|
||||||
<i class="iconfont icon-bianji"></i>编辑大纲
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center-con" v-loading="loading">
|
<div class="center-con" v-loading="loading">
|
||||||
<!-- <TypingEffect v-if="answer" :text="answer" :delay="10" :aiShow="aiShow"/> -->
|
<template v-if="answer.title">
|
||||||
<div style="font-size: 18px;color: #409eff;">封面页</div>
|
<div class="flex justify-between">
|
||||||
<div class="con-item mb-5">
|
<span style="font-size: 18px;color: #409eff;">封面页</span>
|
||||||
<div class="item-name">标题:{{ answer.title }}</div>
|
<el-button type="primary" link @click="onEdit(item, -1)">编辑</el-button>
|
||||||
<div class="item-name">副标题:{{answer.subTitle }}</div>
|
|
||||||
</div>
|
|
||||||
<div style="font-size: 18px;color: #409eff;">目录页</div>
|
|
||||||
<div class="con-item" v-for="(item,index) in answer.chapters">
|
|
||||||
<div class="item-name">{{index + 1}}:{{ item.chapterTitle }}</div>
|
|
||||||
<div class="item-text">
|
|
||||||
<p v-for="(el,i) in item.chapterContents">{{ index + 1 }} - {{ i + 1}} : {{ el.chapterTitle }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="con-item mb-5">
|
||||||
<el-empty v-if="!answer.title" description="请选择符合您需要的教学模式,生成教学大纲" />
|
<div class="item-name">标题:{{ answer.title }}</div>
|
||||||
|
<div class="item-name">副标题:{{ answer.subTitle }}</div>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 18px;color: #409eff;">目录页</div>
|
||||||
|
<div class="con-item" v-for="(item, index) in answer.chapters">
|
||||||
|
<div class="item-name">
|
||||||
|
<span>{{ index + 1 }}:{{ item.chapterTitle }}</span>
|
||||||
|
<el-button type="primary" link @click="onEdit(item, index)">编辑</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="item-text">
|
||||||
|
<p v-for="(el, i) in item.chapterContents">{{ index + 1 }} - {{ i + 1 }} : {{ el.chapterTitle }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-empty v-else description="请选择符合您需要的教学模式,生成教学大纲" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<EditDialog v-model="isEdit" :item="curItem" />
|
<EditDialog v-model="isEdit" :item="editItem" :index="editIndex" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -47,9 +51,10 @@ import EditDialog from './edit-dialog.vue'
|
||||||
import emitter from '@/utils/mitt'
|
import emitter from '@/utils/mitt'
|
||||||
import * as commUtils from '@/utils/comm.js'
|
import * as commUtils from '@/utils/comm.js'
|
||||||
import { createChart, sendChart } from '@/api/ai/index'
|
import { createChart, sendChart } from '@/api/ai/index'
|
||||||
import { completion, addSyllabus, syllabuss, removeSyllabus } from '@/api/mode/index.js'
|
import { completion, addSyllabus, removeSyllabus, editSyllabus } from '@/api/mode/index.js'
|
||||||
import { createOutlineV2 } from '@/utils/ppt-request.js'
|
import { createOutlineV2 } from '@/utils/ppt-request.js'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
const curMode = ref(2)
|
const curMode = ref(2)
|
||||||
const isEdit = ref(false)
|
const isEdit = ref(false)
|
||||||
|
@ -70,26 +75,20 @@ const modeOptions = ref([
|
||||||
|
|
||||||
// 选中的环节
|
// 选中的环节
|
||||||
const selectedData = ref([])
|
const selectedData = ref([])
|
||||||
emitter.on('selected', (data)=>{
|
emitter.on('selected', (data) => {
|
||||||
selectedData.value = data
|
selectedData.value = data
|
||||||
})
|
})
|
||||||
// 回显大纲
|
// 回显大纲
|
||||||
const curItem = reactive({})
|
const curItem = reactive({})
|
||||||
emitter.on('onShow', (data)=>{
|
emitter.on('onShow', (data) => {
|
||||||
console.log(data)
|
|
||||||
aiShow.value = false
|
aiShow.value = false
|
||||||
Object.assign(answer, JSON.parse(data.outline))
|
Object.assign(answer, JSON.parse(data.outline))
|
||||||
|
|
||||||
Object.assign(curItem, data)
|
Object.assign(curItem, data)
|
||||||
curItem.answer = curItem.outline
|
curItem.outline = JSON.parse(curItem.outline)
|
||||||
getDetails(data.id)
|
emitter.emit('onResult',curItem)
|
||||||
})
|
})
|
||||||
|
|
||||||
const getDetails = (id) =>{
|
|
||||||
syllabuss(id).then( res =>{
|
|
||||||
Object.assign(curItem, res.data)
|
|
||||||
emitter.emit('onResult', res.data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = reactive(
|
const params = reactive(
|
||||||
{
|
{
|
||||||
|
@ -103,8 +102,8 @@ const params = reactive(
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const answer = reactive({})
|
const answer = reactive({})
|
||||||
|
|
||||||
const createAi = async ()=>{
|
const createAi = async () => {
|
||||||
if(selectedData.value.length == 0){
|
if (selectedData.value.length == 0) {
|
||||||
ElMessage.warning('请先选择教学环节后再生成教学大纲')
|
ElMessage.warning('请先选择教学环节后再生成教学大纲')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -134,25 +133,66 @@ const createAi = async ()=>{
|
||||||
|
|
||||||
data = res.data
|
data = res.data
|
||||||
}
|
}
|
||||||
const res = await createOutlineV2({query: data.answer})
|
const res = await createOutlineV2({ query: data.answer })
|
||||||
console.log(res)
|
|
||||||
emitter.emit('onResult', res)
|
|
||||||
Object.assign(answer, res.outline)
|
Object.assign(answer, res.outline)
|
||||||
|
curItem.outline = res.outline
|
||||||
|
emitter.emit('onResult', curItem)
|
||||||
onSaveTemp(JSON.stringify(res.outline))
|
onSaveTemp(JSON.stringify(res.outline))
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 编辑大纲
|
||||||
|
const editItem = reactive({})
|
||||||
|
const editIndex = ref(0)
|
||||||
|
const onEdit = (item, index)=>{
|
||||||
|
|
||||||
|
let obj = null
|
||||||
|
if(index == -1){
|
||||||
|
obj = {
|
||||||
|
title: answer.title,
|
||||||
|
subTitle: answer.subTitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
obj = cloneDeep(item)
|
||||||
|
}
|
||||||
|
editIndex.value = index
|
||||||
|
isEdit.value = true
|
||||||
|
Object.assign(editItem, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.on('editItem', (item) =>{
|
||||||
|
if(editIndex.value == -1){
|
||||||
|
answer.title = item.title
|
||||||
|
answer.subTitle = item.subTitle
|
||||||
|
}else{
|
||||||
|
answer.chapters[editIndex.value] = item
|
||||||
|
}
|
||||||
|
let data = cloneDeep(curItem)
|
||||||
|
|
||||||
|
data.outline = JSON.stringify(cloneDeep(answer))
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
editSyllabus(data).then( res =>{
|
||||||
|
curItem.outline = answer
|
||||||
|
emitter.emit('onResult', curItem)
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
}).finally( ()=>{
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// 保存模板
|
// 保存模板
|
||||||
const onSaveTemp = async (answer) => {
|
const onSaveTemp = async (answer) => {
|
||||||
if (answer == '') return
|
if (answer == '') return
|
||||||
let modelIds = selectedData.value.map( item => item.id).join(',')
|
let modelIds = selectedData.value.map(item => item.id).join(',')
|
||||||
const data = {
|
const data = {
|
||||||
eduId: curNode.id,
|
eduId: curNode.id,
|
||||||
outline: answer,
|
outline: answer,
|
||||||
outlineType: curMode.value == 1 ? 0 : 1,
|
outlineType: curMode.value == 1 ? 0 : 1,
|
||||||
modelIds,
|
modelIds,
|
||||||
sourceType: 1,
|
sourceType: 1,
|
||||||
createUserId: user.userId,
|
createUserId: user.userId,
|
||||||
|
@ -162,7 +202,7 @@ const onSaveTemp = async (answer) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除大纲
|
// 删除大纲
|
||||||
const delAnswer = () =>{
|
const delAnswer = () => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
'确定要删除大纲吗?',
|
'确定要删除大纲吗?',
|
||||||
'温馨提示',
|
'温馨提示',
|
||||||
|
@ -192,9 +232,11 @@ const getChartId = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(()=>{
|
onUnmounted(() => {
|
||||||
emitter.off('selected')
|
emitter.off('selected')
|
||||||
emitter.off('onShow')
|
emitter.off('onShow')
|
||||||
|
emitter.off('editItem')
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -216,18 +258,21 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container-center{
|
.container-center {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.center-header{
|
|
||||||
|
.center-header {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
.icon-jiahao{
|
|
||||||
|
.icon-jiahao {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.center-con{
|
|
||||||
|
.center-con {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
@ -235,11 +280,16 @@ onMounted(() => {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
.con-item{
|
|
||||||
|
.con-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
.item-text{
|
.item-name{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.item-text {
|
||||||
background: #F2F2F2;
|
background: #F2F2F2;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
|
@ -7,11 +7,28 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="dialog-content" v-loading="loading">
|
<div class="dialog-content" v-loading="loading">
|
||||||
<el-row>
|
<template v-if="props.index == -1">
|
||||||
<el-col :span="24">
|
<div class="flex mb-5">
|
||||||
<el-input v-model="textarea" :autosize="{ minRows: 5, maxRows: 15 }" type="textarea" />
|
<span class="name">标题:</span>
|
||||||
</el-col>
|
<el-input v-model="editItem.title" />
|
||||||
</el-row>
|
</div>
|
||||||
|
<div class="flex mb-5">
|
||||||
|
<span class="name">副标题</span>
|
||||||
|
<el-input v-model="editItem.subTitle" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="flex mb-5">
|
||||||
|
<span class="name">标题:</span>
|
||||||
|
<el-input v-model="editItem.chapterTitle" />
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<span class="name">内容:</span>
|
||||||
|
<div class="flex edit-con">
|
||||||
|
<el-input class="mb-3" v-model="item.chapterTitle" v-for="item in editItem.chapterContents" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
@ -26,13 +43,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch} from 'vue'
|
import { reactive, ref, watch} from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { editSyllabus } from '@/api/mode/index.js'
|
import { editSyllabus } from '@/api/mode/index.js'
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import emitter from '@/utils/mitt';
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
const textarea = ref('')
|
|
||||||
|
|
||||||
const isDialog = defineModel()
|
const isDialog = defineModel()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
@ -40,20 +57,28 @@ const loading = ref(false)
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
|
||||||
return { name: '11' }
|
},
|
||||||
}
|
index: {
|
||||||
|
type: [Number, String]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => props.item.answer, (newVal) => {
|
const editItem = reactive({})
|
||||||
if (newVal) {
|
|
||||||
textarea.value = newVal
|
watch(() => isDialog.value, (newVal) => {
|
||||||
|
if(newVal){
|
||||||
|
let data = cloneDeep(props.item)
|
||||||
|
Object.assign(editItem, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
},{ deep: true })
|
},{ deep: true })
|
||||||
|
|
||||||
const emit = defineEmits(['saveEdit'])
|
const emit = defineEmits(['saveEdit'])
|
||||||
const onSave = () =>{
|
const onSave = () =>{
|
||||||
|
emitter.emit('editItem', editItem)
|
||||||
|
isDialog.value = false
|
||||||
|
return
|
||||||
loading.value = true
|
loading.value = true
|
||||||
let data = cloneDeep(props.item)
|
let data = cloneDeep(props.item)
|
||||||
data.outline = textarea.value
|
data.outline = textarea.value
|
||||||
|
@ -83,6 +108,12 @@ const onSave = () =>{
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
.name{
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.edit-con{
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,159 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog v-model="mode" :show-close="false" width="600" append-to-body destroy-on-close>
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : item.isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="mode = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="dialog-content" v-loading="loading">
|
|
||||||
<p class="small-tip" v-if="item && item.ex3 == '1'">*当前模板为系统预设,不支持直接操作。需要复制一份为自己的然后再操作</p>
|
|
||||||
<el-form :model="form" label-width="auto">
|
|
||||||
<el-form-item label="名称">
|
|
||||||
<el-input v-model="form.name" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="提示词" v-if="item.ex3 == '1' ? false : true">
|
|
||||||
<el-input v-model="form.prompt" type="textarea" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button @click="mode = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click="saveAdd">
|
|
||||||
确定
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, watch } from 'vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
import { addKeyWords, addChildTemp, editChildTemp } from '@/api/mode/index'
|
|
||||||
|
|
||||||
const mode = defineModel()
|
|
||||||
const props = defineProps({
|
|
||||||
modeType: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
item: { // 当前操作的模板
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return { ex3: '' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
const form = reactive({
|
|
||||||
name: '',
|
|
||||||
prompt: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
watch(() => mode.value, (newVal) => {
|
|
||||||
if(newVal){
|
|
||||||
if (props.item.isAdd) {
|
|
||||||
form.name = ''
|
|
||||||
form.prompt = ''
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
form.name = props.item?.name
|
|
||||||
form.prompt = props.item?.prompt
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},{ deep: true})
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const saveAdd = async () => {
|
|
||||||
loading.value = true
|
|
||||||
|
|
||||||
if (props.item.ex3 == '1') {
|
|
||||||
let id; // id 为主模板id
|
|
||||||
if (props.item.isAdd) {
|
|
||||||
id = props.item.id
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// 编辑状态下 item 为子模板 主模板则是item.parentId
|
|
||||||
id = props.item.parentId
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// 系统预设模板 copy一份
|
|
||||||
const { msg } = await addKeyWords({ name: form.name, id })
|
|
||||||
emitter.emit('onGetMain', props.item)
|
|
||||||
ElMessage.success(msg)
|
|
||||||
mode.value = false
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (props.item.isAdd) {
|
|
||||||
onAddChildTemp(props.item.id)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
let data = JSON.parse(JSON.stringify(props.item))
|
|
||||||
data.name = form.name;
|
|
||||||
data.prompt = form.prompt
|
|
||||||
const { msg } = await editChildTemp(data)
|
|
||||||
emitter.emit('onGetChild', data )
|
|
||||||
ElMessage.success(msg)
|
|
||||||
mode.value = false
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加子模板
|
|
||||||
const onAddChildTemp = async (parentId) => {
|
|
||||||
// 添加子模板
|
|
||||||
let obj = {
|
|
||||||
name: form.name,
|
|
||||||
type: 2, // 子模板 固定值为2
|
|
||||||
sortNum: 1,
|
|
||||||
parentId,
|
|
||||||
lmType: 1,
|
|
||||||
model: props.modeType,
|
|
||||||
prompt: form.prompt,
|
|
||||||
ex1: props.item.ex1, //学段
|
|
||||||
ex2: props.item.ex2, // 学科
|
|
||||||
ex3: '', //是否系统预设 这里默认空
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var { msg } = await addChildTemp(obj)
|
|
||||||
emitter.emit('onGetChild')
|
|
||||||
ElMessage.success(msg)
|
|
||||||
mode.value = false
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.custom-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.small-tip {
|
|
||||||
text-align: left;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
color: #F56C6C;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,193 +1,394 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container-left flex">
|
<div class="container-left flex" v-loading="loading">
|
||||||
<div class="left-header flex">教学模式</div>
|
<div class="left-header flex">
|
||||||
<div class="left-con" v-loading="loading">
|
<span>教学模式</span>
|
||||||
<el-empty v-if="!(tempList.length)" description="暂无数据" />
|
<div>
|
||||||
<div class="con-item" v-for="item in tempList" :key="item.id" :class=" actId == item.id ? 'item-act' : ''">
|
<el-button type="primary" link @click="resetSelect">重置</el-button>
|
||||||
<div class="item-header flex">
|
<el-button type="primary" link @click="
|
||||||
<span>{{ item.name }}</span>
|
addVisible = true,
|
||||||
<el-button type="primary" link @click="onSelect(item)">选择模式</el-button>
|
addChild = false,
|
||||||
</div>
|
isEdit = false
|
||||||
<div class="content-list">
|
"><i class="iconfont icon-jiahao"></i>新增</el-button>
|
||||||
<div class="item-list flex" >
|
</div>
|
||||||
<el-card class="item-card" shadow="never" v-for="el in item.child" :key="el.id">
|
</div>
|
||||||
<p class="card-name">
|
<div class="left-list">
|
||||||
<el-text line-clamp="1" :title="el.name">
|
<div class="item" v-for="item in templateList" :key="item.id">
|
||||||
{{ el.name }}
|
<div class="item-name flex" @mouseenter="item.isAdd = true" @mouseleave="item.isAdd = false"
|
||||||
</el-text>
|
@click="toggleParent(item)">
|
||||||
</p>
|
<div class="flex">
|
||||||
<div class="card-text">
|
<span>{{ item.name }}</span>
|
||||||
<el-text line-clamp="4" :title="el.prompt">
|
<!--个人教学模式才会有添加、编辑-->
|
||||||
{{ el.prompt }}
|
<div v-if="!item.ex3" class="ml-3">
|
||||||
</el-text>
|
<el-button type="primary" link @click.stop="addModeChild(item)">添加</el-button>
|
||||||
</div>
|
<el-button type="primary" link @click.stop="editMode(item)">编辑</el-button>
|
||||||
</el-card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--加号 数鼠标悬浮 显示-->
|
||||||
|
<i v-show="item.isAdd && !item.selected" class="iconfont icon-jiahao"></i>
|
||||||
|
<!--减号 选中之后显示-->
|
||||||
|
<i v-show="item.selected" class="iconfont icon-zuixiaohua"></i>
|
||||||
|
</div>
|
||||||
|
<div class="item-child flex" :class="child.selected ? 'act-child' : ''" v-for="child in item.children"
|
||||||
|
:key="child.id" @mouseenter="child.isAdd = true" @mouseleave="child.isAdd = false"
|
||||||
|
@click="toggleChild(item, child)">
|
||||||
|
<div>
|
||||||
|
<span>{{ child.name }}</span>
|
||||||
|
<!--个人教学模式才会有编辑-->
|
||||||
|
<el-button v-if="!child.ex3" class="ml-3" type="primary" link @click.stop="editMode(child)">编辑</el-button>
|
||||||
|
</div>
|
||||||
|
<i v-show="child.isAdd && !child.selected" class="iconfont icon-jiahao"></i>
|
||||||
|
<i v-show="child.selected" class="iconfont icon-zuixiaohua"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--弹窗-->
|
||||||
|
<el-dialog v-model="addVisible" append-to-body :show-close="false" width="550" :before-close="handleBeforeClose"
|
||||||
|
style="border-radius: 10px; padding: 10px 15px">
|
||||||
|
<template #header>
|
||||||
|
<div class="mode-dialog-header flex">
|
||||||
|
<span>{{ addChild ? '教学环节' : '教学模式' }}</span>
|
||||||
|
<i class="iconfont icon-guanbi" @click="addVisible = false"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"
|
||||||
|
class="demo-ruleForm">
|
||||||
|
<el-form-item :label="`教学${addChild ? '环节' : '模式'}名称`" prop="name">
|
||||||
|
<div class="flex" style="width: 100%">
|
||||||
|
<el-input v-model="ruleForm.name" />
|
||||||
|
<el-button v-if="isEdit" link type="danger" class="ml-5 mr-3" @click="onDel">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="说明" v-if="addChild" prop="prompt">
|
||||||
|
<el-input v-model="ruleForm.prompt" type="textarea" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="mt-10 form-btn">
|
||||||
|
<el-button @click="closeDialog">关闭</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(ruleFormRef)">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
import { modelList } from '@/api/mode/index'
|
import { modelList } from '@/api/mode/index'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
|
import emitter from '@/utils/mitt'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { addChildTemp, editChildTemp, removeChildTemp, syllabusList } from '@/api/mode'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
const { user } = useUserStore()
|
const { user } = useUserStore()
|
||||||
|
|
||||||
// 获取模板
|
// 获取模板
|
||||||
let list = ref([])
|
|
||||||
const getTemplate = async (id) => {
|
|
||||||
const { rows } = await modelList({ createUser: user.userId, model: 4, type: 1, pageNum: 1, pageSize: 10000})
|
|
||||||
list.value = rows
|
|
||||||
if(list.value.length){
|
|
||||||
getChildTemp(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取子模板
|
|
||||||
const actId = ref('')
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tempList = ref([])
|
const templateList = ref([])
|
||||||
const getChildTemp = async (parentId) => {
|
const getTemplate = async () => {
|
||||||
tempList.value.length = 0
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
for (let item of list.value) {
|
const { rows } = await modelList({
|
||||||
try {
|
createUser: user.userId,
|
||||||
const { rows } = await modelList({ model: 4, type: 2, parentId: item.id })
|
model: 4,
|
||||||
tempList.value.push({
|
pageNum: 1,
|
||||||
id: item.id,
|
pageSize: 10000
|
||||||
name: item.name,
|
|
||||||
child: rows
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
actId.value = tempList.value[0].id
|
|
||||||
}
|
|
||||||
if(parentId){
|
|
||||||
const item = tempList.value.find(item => item.id == parentId)
|
|
||||||
emitter.emit('changeMode', item)
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
emitter.emit('changeMode', tempList.value[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 操作之后获取子模板
|
|
||||||
emitter.on('onGetChild', async (data) => {
|
|
||||||
|
|
||||||
await getTemplate(data.parentId)
|
|
||||||
|
|
||||||
})
|
|
||||||
// 操作之后获取主模板
|
|
||||||
emitter.on('onGetMain', (item) => {
|
|
||||||
getTemplate(item.parentId)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 选择模板
|
|
||||||
const emit = defineEmits([''])
|
|
||||||
const onSelect = (item) =>{
|
|
||||||
actId.value = item.id
|
|
||||||
item.child.forEach(el =>{
|
|
||||||
el.aiShow = false
|
|
||||||
})
|
})
|
||||||
emitter.emit('changeMode', item)
|
loading.value = false
|
||||||
|
|
||||||
|
// 过滤出 type 为 1 的项
|
||||||
|
let ary1 = rows.filter((item) => item.type === 1)
|
||||||
|
templateList.value = ary1.map((parent) => {
|
||||||
|
parent.children = rows.filter((child) => child.type === 2 && child.parentId === parent.id)
|
||||||
|
return parent
|
||||||
|
})
|
||||||
|
getSyllabus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询生成大纲记录
|
||||||
|
const getSyllabus = async () => {
|
||||||
|
const { rows } = await syllabusList({
|
||||||
|
createUserId: user.userId,
|
||||||
|
eduId: curNode.id,
|
||||||
|
sourceType: 1,
|
||||||
|
pageSize: 1,
|
||||||
|
orderByColumn: 'createTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
})
|
||||||
|
if (rows && rows.length) {
|
||||||
|
const idsAry = rows
|
||||||
|
.at(-1)
|
||||||
|
.modelIds.split(',')
|
||||||
|
.map((item) => Number(item))
|
||||||
|
// 设置选中
|
||||||
|
let ary = []
|
||||||
|
templateList.value.forEach((parent) => {
|
||||||
|
parent.children.forEach((child) => {
|
||||||
|
child.selected = idsAry.includes(child.id)
|
||||||
|
if (child.selected) {
|
||||||
|
ary.push(child)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 更新父项的 selected 状态
|
||||||
|
parent.selected = parent.children.every((child) => child.selected)
|
||||||
|
})
|
||||||
|
// 回显大纲数据
|
||||||
|
emitter.emit('onShow', rows.at(-1))
|
||||||
|
emitter.emit('selected', ary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置选中
|
||||||
|
const resetSelect = () => {
|
||||||
|
templateList.value.forEach((item) => {
|
||||||
|
item.selected = false
|
||||||
|
item.children.forEach((el) => {
|
||||||
|
el.selected = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
emitter.emit('selected', [])
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.on('resetSelect', () => {
|
||||||
|
resetSelect()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 点击教学模式
|
||||||
|
const toggleParent = (parent) => {
|
||||||
|
parent.selected = !parent.selected
|
||||||
|
parent.children.forEach((child) => {
|
||||||
|
child.selected = parent.selected
|
||||||
|
})
|
||||||
|
const selectedData = templateList.value
|
||||||
|
.map((item) => item.children.filter((child) => child.selected))
|
||||||
|
.flat()
|
||||||
|
// .flat()将二维数组扁平化为一维数组
|
||||||
|
emitter.emit('selected', selectedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击教学环节
|
||||||
|
const toggleChild = (parent, child) => {
|
||||||
|
child.selected = !child.selected
|
||||||
|
updateParentSelection(parent)
|
||||||
|
const selectedData = templateList.value
|
||||||
|
.map((item) => item.children.filter((child) => child.selected))
|
||||||
|
.flat()
|
||||||
|
emitter.emit('selected', selectedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查父项(教学模式)的所有子项(教学环节)是否都被选中,如果是,则设置父项为选中状态,否则取消选中
|
||||||
|
const updateParentSelection = (parent) => {
|
||||||
|
parent.selected = parent.children.every((child) => child.selected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗
|
||||||
|
const addVisible = ref(false)
|
||||||
|
|
||||||
|
const ruleFormRef = ref()
|
||||||
|
const rules = reactive({
|
||||||
|
name: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
const ruleForm = reactive({
|
||||||
|
name: '',
|
||||||
|
prompt: ''
|
||||||
|
})
|
||||||
|
// 提交
|
||||||
|
const onSubmit = async (formEl) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(async (valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
// 修改
|
||||||
|
if (isEdit.value) {
|
||||||
|
let data = cloneDeep(curEditItem)
|
||||||
|
data.name = ruleForm.name
|
||||||
|
await editChildTemp(data)
|
||||||
|
}
|
||||||
|
// 新增
|
||||||
|
else {
|
||||||
|
let obj = {
|
||||||
|
ex1: curNode.edustage,
|
||||||
|
ex2: curNode.edusubject,
|
||||||
|
model: 4,
|
||||||
|
name: ruleForm.name,
|
||||||
|
sortNum: 1,
|
||||||
|
type: 1
|
||||||
|
}
|
||||||
|
// 新增教学环节
|
||||||
|
if (addChild.value) {
|
||||||
|
obj.parentId = curEditItem.id
|
||||||
|
obj.prompt = ruleForm.prompt
|
||||||
|
obj.type = 2
|
||||||
|
}
|
||||||
|
await addChildTemp(obj)
|
||||||
|
}
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
resetForm()
|
||||||
|
addVisible.value = false
|
||||||
|
getTemplate()
|
||||||
|
} else {
|
||||||
|
console.log('error submit!', fields)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否编辑
|
||||||
|
const isEdit = ref(false)
|
||||||
|
// 区分是新增教学模式false 还是现在教学环节true
|
||||||
|
const addChild = ref(false)
|
||||||
|
|
||||||
|
const curEditItem = reactive({})
|
||||||
|
const addModeChild = (item) => {
|
||||||
|
isEdit.value = false
|
||||||
|
Object.assign(curEditItem, item)
|
||||||
|
addChild.value = true
|
||||||
|
addVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
const editMode = (item) => {
|
||||||
|
isEdit.value = true
|
||||||
|
addChild.value = false
|
||||||
|
Object.assign(curEditItem, item)
|
||||||
|
ruleForm.name = item.name
|
||||||
|
addVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 教学模式 教学环节
|
||||||
|
const onDel = () => {
|
||||||
|
ElMessageBox.confirm('确定要删除模板吗?', '温馨提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
await removeChildTemp(curEditItem.id)
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
resetForm()
|
||||||
|
addVisible.value = false
|
||||||
|
getTemplate()
|
||||||
|
})
|
||||||
|
.catch(() => { })
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
handleBeforeClose(() => (addVisible.value = false))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBeforeClose = (done) => {
|
||||||
|
resetForm()
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
const resetForm = () => {
|
||||||
|
if (ruleFormRef.value) {
|
||||||
|
ruleFormRef.value.resetFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const curNode = reactive({})
|
const curNode = reactive({})
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data)
|
||||||
getTemplate()
|
getTemplate()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 解绑
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
emitter.off('resetSelect')
|
||||||
emitter.off('onGetMain');
|
|
||||||
emitter.off('onGetChild');
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container-left {
|
.container-left {
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
font-size: 15px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.left-header {
|
.left-header {
|
||||||
height: 45px;
|
height: 32px;
|
||||||
background: #F6F6F6;
|
justify-content: space-between;
|
||||||
border-radius: 5px 0 0 0;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
.icon-jiahao {
|
||||||
text-indent: 2em;
|
font-size: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-con {
|
.left-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #fff;
|
margin-top: 5px;
|
||||||
border-radius: 0 0 0 5px;
|
background-color: #fff;
|
||||||
padding: 15px;
|
border-radius: 5px;
|
||||||
box-sizing: border-box;
|
text-align: left;
|
||||||
font-size: 14px;
|
padding: 0 10px;
|
||||||
overflow: auto;
|
line-height: 30px;
|
||||||
.item-header {
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 5px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
.con-item {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.item-list{
|
|
||||||
|
|
||||||
padding: 10px;
|
.item-child {
|
||||||
|
padding-left: 3em;
|
||||||
|
justify-content: space-between;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #666;
|
||||||
|
position: absolute;
|
||||||
|
left: 2em;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.item-act{
|
|
||||||
border: solid 1px #409eff;
|
.iconfont {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiahao {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuixiaohua {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.act-child {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
background: #eaf3ff;
|
||||||
.item-card {
|
|
||||||
width: 130px;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 10px;
|
|
||||||
margin-right: 20px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 10px;
|
|
||||||
:deep(.el-card__body) {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-name {
|
&::after {
|
||||||
text-align: center;
|
background-color: #409eff;
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-text {
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
.el-text{
|
|
||||||
font-size: 12px !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.content-list{
|
|
||||||
overflow-x: auto
|
.mode-dialog-header {
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
.icon-guanbi {
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.content-list::-webkit-scrollbar {
|
|
||||||
height: 8px;
|
.form-btn {
|
||||||
|
text-align: right;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,394 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-left flex" v-loading="loading">
|
|
||||||
<div class="left-header flex">
|
|
||||||
<span>教学模式</span>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" link @click="resetSelect">重置</el-button>
|
|
||||||
<el-button type="primary" link @click="
|
|
||||||
addVisible = true,
|
|
||||||
addChild = false,
|
|
||||||
isEdit = false
|
|
||||||
"><i class="iconfont icon-jiahao"></i>新增</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="left-list">
|
|
||||||
<div class="item" v-for="item in templateList" :key="item.id">
|
|
||||||
<div class="item-name flex" @mouseenter="item.isAdd = true" @mouseleave="item.isAdd = false"
|
|
||||||
@click="toggleParent(item)">
|
|
||||||
<div class="flex">
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<!--个人教学模式才会有添加、编辑-->
|
|
||||||
<div v-if="!item.ex3" class="ml-3">
|
|
||||||
<el-button type="primary" link @click.stop="addModeChild(item)">添加</el-button>
|
|
||||||
<el-button type="primary" link @click.stop="editMode(item)">编辑</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--加号 数鼠标悬浮 显示-->
|
|
||||||
<i v-show="item.isAdd && !item.selected" class="iconfont icon-jiahao"></i>
|
|
||||||
<!--减号 选中之后显示-->
|
|
||||||
<i v-show="item.selected" class="iconfont icon-zuixiaohua"></i>
|
|
||||||
</div>
|
|
||||||
<div class="item-child flex" :class="child.selected ? 'act-child' : ''" v-for="child in item.children"
|
|
||||||
:key="child.id" @mouseenter="child.isAdd = true" @mouseleave="child.isAdd = false"
|
|
||||||
@click="toggleChild(item, child)">
|
|
||||||
<div>
|
|
||||||
<span>{{ child.name }}</span>
|
|
||||||
<!--个人教学模式才会有编辑-->
|
|
||||||
<el-button v-if="!child.ex3" class="ml-3" type="primary" link @click.stop="editMode(child)">编辑</el-button>
|
|
||||||
</div>
|
|
||||||
<i v-show="child.isAdd && !child.selected" class="iconfont icon-jiahao"></i>
|
|
||||||
<i v-show="child.selected" class="iconfont icon-zuixiaohua"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--弹窗-->
|
|
||||||
<el-dialog v-model="addVisible" append-to-body :show-close="false" width="550" :before-close="handleBeforeClose"
|
|
||||||
style="border-radius: 10px; padding: 10px 15px">
|
|
||||||
<template #header>
|
|
||||||
<div class="mode-dialog-header flex">
|
|
||||||
<span>{{ addChild ? '教学环节' : '教学模式' }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="addVisible = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"
|
|
||||||
class="demo-ruleForm">
|
|
||||||
<el-form-item :label="`教学${addChild ? '环节' : '模式'}名称`" prop="name">
|
|
||||||
<div class="flex" style="width: 100%">
|
|
||||||
<el-input v-model="ruleForm.name" />
|
|
||||||
<el-button v-if="isEdit" link type="danger" class="ml-5 mr-3" @click="onDel">删除</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="说明" v-if="addChild" prop="prompt">
|
|
||||||
<el-input v-model="ruleForm.prompt" type="textarea" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div class="mt-10 form-btn">
|
|
||||||
<el-button @click="closeDialog">关闭</el-button>
|
|
||||||
<el-button type="primary" @click="onSubmit(ruleFormRef)">保存</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
|
||||||
import { modelList } from '@/api/mode/index'
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import emitter from '@/utils/mitt'
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
import { addChildTemp, editChildTemp, removeChildTemp, syllabusList } from '@/api/mode'
|
|
||||||
import { cloneDeep } from 'lodash'
|
|
||||||
|
|
||||||
const { user } = useUserStore()
|
|
||||||
|
|
||||||
// 获取模板
|
|
||||||
const loading = ref(false)
|
|
||||||
const templateList = ref([])
|
|
||||||
const getTemplate = async () => {
|
|
||||||
loading.value = true
|
|
||||||
const { rows } = await modelList({
|
|
||||||
createUser: user.userId,
|
|
||||||
model: 4,
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10000
|
|
||||||
})
|
|
||||||
loading.value = false
|
|
||||||
|
|
||||||
// 过滤出 type 为 1 的项
|
|
||||||
let ary1 = rows.filter((item) => item.type === 1)
|
|
||||||
templateList.value = ary1.map((parent) => {
|
|
||||||
parent.children = rows.filter((child) => child.type === 2 && child.parentId === parent.id)
|
|
||||||
return parent
|
|
||||||
})
|
|
||||||
getSyllabus()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询生成大纲记录
|
|
||||||
const getSyllabus = async () => {
|
|
||||||
const { rows } = await syllabusList({
|
|
||||||
createUserId: user.userId,
|
|
||||||
eduId: curNode.id,
|
|
||||||
sourceType: 1,
|
|
||||||
pageSize: 1,
|
|
||||||
orderByColumn: 'createTime',
|
|
||||||
isAsc: 'desc'
|
|
||||||
})
|
|
||||||
if (rows && rows.length) {
|
|
||||||
const idsAry = rows
|
|
||||||
.at(-1)
|
|
||||||
.modelIds.split(',')
|
|
||||||
.map((item) => Number(item))
|
|
||||||
// 设置选中
|
|
||||||
let ary = []
|
|
||||||
templateList.value.forEach((parent) => {
|
|
||||||
parent.children.forEach((child) => {
|
|
||||||
child.selected = idsAry.includes(child.id)
|
|
||||||
if (child.selected) {
|
|
||||||
ary.push(child)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 更新父项的 selected 状态
|
|
||||||
parent.selected = parent.children.every((child) => child.selected)
|
|
||||||
})
|
|
||||||
// 回显大纲数据
|
|
||||||
emitter.emit('onShow', rows.at(-1))
|
|
||||||
emitter.emit('selected', ary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置选中
|
|
||||||
const resetSelect = () => {
|
|
||||||
templateList.value.forEach((item) => {
|
|
||||||
item.selected = false
|
|
||||||
item.children.forEach((el) => {
|
|
||||||
el.selected = false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
emitter.emit('selected', [])
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter.on('resetSelect', () => {
|
|
||||||
resetSelect()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 点击教学模式
|
|
||||||
const toggleParent = (parent) => {
|
|
||||||
parent.selected = !parent.selected
|
|
||||||
parent.children.forEach((child) => {
|
|
||||||
child.selected = parent.selected
|
|
||||||
})
|
|
||||||
const selectedData = templateList.value
|
|
||||||
.map((item) => item.children.filter((child) => child.selected))
|
|
||||||
.flat()
|
|
||||||
// .flat()将二维数组扁平化为一维数组
|
|
||||||
emitter.emit('selected', selectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击教学环节
|
|
||||||
const toggleChild = (parent, child) => {
|
|
||||||
child.selected = !child.selected
|
|
||||||
updateParentSelection(parent)
|
|
||||||
const selectedData = templateList.value
|
|
||||||
.map((item) => item.children.filter((child) => child.selected))
|
|
||||||
.flat()
|
|
||||||
emitter.emit('selected', selectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查父项(教学模式)的所有子项(教学环节)是否都被选中,如果是,则设置父项为选中状态,否则取消选中
|
|
||||||
const updateParentSelection = (parent) => {
|
|
||||||
parent.selected = parent.children.every((child) => child.selected)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 弹窗
|
|
||||||
const addVisible = ref(false)
|
|
||||||
|
|
||||||
const ruleFormRef = ref()
|
|
||||||
const rules = reactive({
|
|
||||||
name: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const ruleForm = reactive({
|
|
||||||
name: '',
|
|
||||||
prompt: ''
|
|
||||||
})
|
|
||||||
// 提交
|
|
||||||
const onSubmit = async (formEl) => {
|
|
||||||
if (!formEl) return
|
|
||||||
await formEl.validate(async (valid, fields) => {
|
|
||||||
if (valid) {
|
|
||||||
// 修改
|
|
||||||
if (isEdit.value) {
|
|
||||||
let data = cloneDeep(curEditItem)
|
|
||||||
data.name = ruleForm.name
|
|
||||||
await editChildTemp(data)
|
|
||||||
}
|
|
||||||
// 新增
|
|
||||||
else {
|
|
||||||
let obj = {
|
|
||||||
ex1: curNode.edustage,
|
|
||||||
ex2: curNode.edusubject,
|
|
||||||
model: 4,
|
|
||||||
name: ruleForm.name,
|
|
||||||
sortNum: 1,
|
|
||||||
type: 1
|
|
||||||
}
|
|
||||||
// 新增教学环节
|
|
||||||
if (addChild.value) {
|
|
||||||
obj.parentId = curEditItem.id
|
|
||||||
obj.prompt = ruleForm.prompt
|
|
||||||
obj.type = 2
|
|
||||||
}
|
|
||||||
await addChildTemp(obj)
|
|
||||||
}
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
resetForm()
|
|
||||||
addVisible.value = false
|
|
||||||
getTemplate()
|
|
||||||
} else {
|
|
||||||
console.log('error submit!', fields)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否编辑
|
|
||||||
const isEdit = ref(false)
|
|
||||||
// 区分是新增教学模式false 还是现在教学环节true
|
|
||||||
const addChild = ref(false)
|
|
||||||
|
|
||||||
const curEditItem = reactive({})
|
|
||||||
const addModeChild = (item) => {
|
|
||||||
isEdit.value = false
|
|
||||||
Object.assign(curEditItem, item)
|
|
||||||
addChild.value = true
|
|
||||||
addVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
const editMode = (item) => {
|
|
||||||
isEdit.value = true
|
|
||||||
addChild.value = false
|
|
||||||
Object.assign(curEditItem, item)
|
|
||||||
ruleForm.name = item.name
|
|
||||||
addVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除 教学模式 教学环节
|
|
||||||
const onDel = () => {
|
|
||||||
ElMessageBox.confirm('确定要删除模板吗?', '温馨提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
await removeChildTemp(curEditItem.id)
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
resetForm()
|
|
||||||
addVisible.value = false
|
|
||||||
getTemplate()
|
|
||||||
})
|
|
||||||
.catch(() => { })
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeDialog = () => {
|
|
||||||
handleBeforeClose(() => (addVisible.value = false))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBeforeClose = (done) => {
|
|
||||||
resetForm()
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
const resetForm = () => {
|
|
||||||
if (ruleFormRef.value) {
|
|
||||||
ruleFormRef.value.resetFields()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const curNode = reactive({})
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data)
|
|
||||||
getTemplate()
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('resetSelect')
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container-left {
|
|
||||||
height: 100%;
|
|
||||||
font-size: 15px;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.left-header {
|
|
||||||
height: 32px;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-jiahao {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-list {
|
|
||||||
flex: 1;
|
|
||||||
margin-top: 5px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: left;
|
|
||||||
padding: 0 10px;
|
|
||||||
line-height: 30px;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.item-name {
|
|
||||||
line-height: 40px;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-child {
|
|
||||||
padding-left: 3em;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: pointer;
|
|
||||||
align-items: center;
|
|
||||||
padding-right: 10px;
|
|
||||||
margin-bottom: 3px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
width: 5px;
|
|
||||||
height: 5px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #666;
|
|
||||||
position: absolute;
|
|
||||||
left: 2em;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(0, -50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-jiahao {
|
|
||||||
color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-zuixiaohua {
|
|
||||||
color: #f56c6c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.act-child {
|
|
||||||
border-radius: 5px;
|
|
||||||
background: #eaf3ff;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
background-color: #409eff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mode-dialog-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
font-size: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-btn {
|
|
||||||
text-align: right;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,107 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container-right flex">
|
<div class="container-right flex">
|
||||||
<div class="right-header flex">
|
<div class="right-header flex">
|
||||||
<div class="header-left">
|
<span>课件预览</span>
|
||||||
<!-- <el-button type="primary" link>
|
<div>
|
||||||
<i class="iconfont icon-jiahao"></i>新活动
|
<el-button type="danger" @click="onCreate">一键生成</el-button>
|
||||||
</el-button>
|
<el-button :disabled="!result?.parentId" @click="openAiPPT">编辑课件</el-button>
|
||||||
<el-button type="primary" link>
|
|
||||||
<i class="iconfont icon-baocun"></i>保存为教学模式
|
|
||||||
</el-button> -->
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<el-select v-model="curMode" placeholder="Select" class="mr-4 w-30">
|
|
||||||
<el-option v-for="item in modeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
||||||
</el-select>
|
|
||||||
<el-button type="primary" :disabled="!(resultList.length)" @click="getCompletion">一键研读</el-button>
|
|
||||||
<el-button type="danger" @click="onCreate">生成PPT</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-con flex" ref="listRef">
|
<div class="right-con">
|
||||||
<el-empty v-if="!(resultList.length)" description="暂无数据" />
|
<el-empty v-if="!result?.parentId" description="请先生成教学大纲,再生成教学课件" />
|
||||||
<div class="con-item flex" v-for="(item, index) in resultList" :key="item.id" v-loading="item.loading">
|
<div v-for="(item,index) in pptSlides" class="right-con-item">
|
||||||
<div class="item-top flex">
|
<div>{{index+1}}</div><img :src="item.fileurl">
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<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, false)">编辑</el-button>
|
|
||||||
<el-button type="primary" link @click="removeItem(item, true)">移除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
<div class="item-bom">
|
|
||||||
<div class="item-prompt">{{ item.prompt }}</div>
|
|
||||||
<div class="item-answer" v-if="item.answer">
|
|
||||||
<div class="answer-text">
|
|
||||||
<TypingEffect v-if="isStarted[index]" :text="item.answer" :delay="10" :aiShow="item.aiShow"
|
|
||||||
@complete="handleCompleteText($event, index)" @updateScroll="scrollToBottom($event, index)" />
|
|
||||||
</div>
|
|
||||||
<div class="item-btn flex">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<EditDialog v-model="isEdit" :item="curItem" />
|
<PptDialog @close-dialogs="pptDialog = false" @add-success="addAiPPT" :dataList="result" v-model="pptDialog" />
|
||||||
<AdjustDialog v-model="isAdjust" :item="curItem" :curMode="curMode" :conversation_id="conversation_id" />
|
|
||||||
<PptDialog @add-success="addAiPPT" :dataList="resultList" v-model="pptDialog" />
|
|
||||||
<progress-dialog v-model:visible="pgDialog.visible" v-bind="pgDialog" />
|
<progress-dialog v-model:visible="pgDialog.visible" v-bind="pgDialog" />
|
||||||
<!--添加、编辑提示词-->
|
|
||||||
<keywordDialog v-model="isWordDialog" :item="curItem" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, reactive, nextTick } from 'vue'
|
import {ref, onUnmounted, onMounted, reactive} from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import emitter from '@/utils/mitt'
|
import emitter from '@/utils/mitt'
|
||||||
import EditDialog from './edit-dialog.vue'
|
|
||||||
import AdjustDialog from './adjust-dialog.vue'
|
|
||||||
import progressDialog from './progress-dialog.vue'
|
import progressDialog from './progress-dialog.vue'
|
||||||
import { completion, tempResult, tempSave, removeChildTemp, editTempResult, modelList } from '@/api/mode/index.js'
|
import PptDialog from "@/views/prepare/container/pptist-dialog.vue";
|
||||||
import { createChart, sendChart } from '@/api/ai/index'
|
import msgUtils from "@/plugins/modal";
|
||||||
// import { dataSetJson } from '@/utils/comm.js'
|
import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport";
|
||||||
|
import {slidesToImg} from "@/utils/ppt";
|
||||||
|
import {getEntpcoursefile, listEntpcoursefileNew} from "@/api/education/entpcoursefile";
|
||||||
|
import {sessionStore} from "@/utils/store";
|
||||||
|
import * as API_smarttalk from "@/api/file";
|
||||||
|
import * as API_entpcourse from "@/api/education/entpcourse";
|
||||||
|
import * as API_entpcoursefile from "@/api/education/entpcoursefile";
|
||||||
import * as commUtils from '@/utils/comm.js'
|
import * as commUtils from '@/utils/comm.js'
|
||||||
import PptDialog from '@/views/prepare/container/pptist-dialog.vue'
|
|
||||||
import keywordDialog from './keyword-dialog.vue'
|
|
||||||
import TypingEffect from '@/components/typing-effect/index.vue'
|
|
||||||
import { cloneDeep } from 'lodash'
|
|
||||||
|
|
||||||
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 * as Api_server from '@/api/apiService' // 相关api
|
||||||
import * as API_smarttalk from '@/api/file' // 文件相关api
|
import {createWindow} from "@/utils/tool";
|
||||||
import msgUtils from '@/plugins/modal'
|
import {editSyllabus} from "@/api/mode";
|
||||||
import { getEntpcoursefile } from '@/api/education/entpcoursefile'
|
import { getSmarttalkPage } from "@/api/file"
|
||||||
import { createWindow } from '@/utils/tool' // 消息工具
|
import useUserStore from '@/store/modules/user'
|
||||||
import { slidesToImg } from '@/utils/ppt' // ppt相关工具
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const pptDialog = ref(false)
|
const pptDialog = ref(false)
|
||||||
const resultList = ref([])
|
const result = ref(null)
|
||||||
const courseObj = reactive({
|
const courseObj = reactive({
|
||||||
node: null, // 选择的课程节点
|
node: null, // 选择的课程节点
|
||||||
})
|
})
|
||||||
|
const curNode = reactive({})
|
||||||
const pgDialog = reactive({ // 弹窗-进度条
|
const pgDialog = reactive({ // 弹窗-进度条
|
||||||
visible: false,
|
visible: false,
|
||||||
title: 'PPT解析中...',
|
title: 'PPT解析中...',
|
||||||
|
@ -119,214 +61,27 @@ const pgDialog = reactive({ // 弹窗-进度条
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const curMode = ref(2)
|
const pptSlides = ref([])
|
||||||
const modeOptions = ref([
|
|
||||||
{
|
|
||||||
label: '教学大模型',
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '知识库模型',
|
|
||||||
value: 2
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
emitter.on('changeMode', (item) => {
|
emitter.on('onResult', (data)=>{
|
||||||
resultList.value = item.child
|
result.value = data
|
||||||
getTempResult(item.id)
|
if (!!result.value.parentId) {
|
||||||
|
listEntpcoursefileNew({parentid: result.value.parentId}).then(res=>{
|
||||||
|
pptSlides.value = res.rows
|
||||||
|
})
|
||||||
|
}else {
|
||||||
|
pptSlides.value = []
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onCreate = () =>{
|
const onCreate = () =>{
|
||||||
let isAnswer = resultList.value.every(item => !item.answer)
|
if(!result.value){
|
||||||
if(isAnswer){
|
|
||||||
ElMessage.warning('请先进行研读')
|
ElMessage.warning('请先进行研读')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pptDialog.value = true
|
pptDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 一键研读
|
|
||||||
const getCompletion = async () => {
|
|
||||||
isStarted.value = new Array(resultList.length).fill(false)
|
|
||||||
isStarted.value[0] = true
|
|
||||||
|
|
||||||
resultList.value.forEach(item => {
|
|
||||||
if (item.answer) {
|
|
||||||
item.answer = ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for (let item of resultList.value) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
item.loading = true
|
|
||||||
item.aiShow = true
|
|
||||||
|
|
||||||
let str = cloneDeep(prompt.value)
|
|
||||||
str = str.replace(/{模板名称}/g, item.name)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
item.answer = getResult(data.answer)
|
|
||||||
onSaveTemp(item)
|
|
||||||
} finally {
|
|
||||||
item.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCompleteText = async (answer, index) => {
|
|
||||||
if (index < resultList.value.length - 1) {
|
|
||||||
isStarted.value[index + 1] = true; // 开始显示下一个文本
|
|
||||||
}
|
|
||||||
if (isAgain.value) {
|
|
||||||
try {
|
|
||||||
await editTempResult({ id: resultList.value[index].resultId, content: answer })
|
|
||||||
} finally {
|
|
||||||
isAgain.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存模板
|
|
||||||
const onSaveTemp = async (item) => {
|
|
||||||
if (item.answer == '') return
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
mainModelId: item.parentId,
|
|
||||||
modelId: item.id,
|
|
||||||
examDocld: '',
|
|
||||||
content: item.answer,
|
|
||||||
ex1: curNode.id
|
|
||||||
}
|
|
||||||
const res = await tempSave(data)
|
|
||||||
|
|
||||||
if(!item.resultId){
|
|
||||||
item.resultId = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isWordDialog = ref(false)
|
|
||||||
const editKeyWord = (item, val) => {
|
|
||||||
/**
|
|
||||||
* isAdd: 子模板中的移除 为编辑false 头部删除 添加提示词为新增 true
|
|
||||||
*/
|
|
||||||
Object.assign(curItem, item)
|
|
||||||
curItem.isAdd = val
|
|
||||||
isWordDialog.value = true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 移除模板
|
|
||||||
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) {
|
|
||||||
// 获取子模板
|
|
||||||
emitter.emit('onGetChild', item)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 获取主模板
|
|
||||||
// getTemplateList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// editKeyWord(item, !isChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const listRef = ref()
|
|
||||||
// 查询模板结果
|
|
||||||
const isStarted = ref([]);
|
|
||||||
const getTempResult = (id) => {
|
|
||||||
tempResult({ mainModelId: id, pageNum: 1, pageSize: 10000, ex1: curNode.id }).then(res => {
|
|
||||||
let rows = res.rows
|
|
||||||
if (rows.length > 0) {
|
|
||||||
isStarted.value = new Array(rows.length).fill(true)
|
|
||||||
resultList.value.forEach(item => {
|
|
||||||
rows.forEach(el => {
|
|
||||||
if (item.id == el.modelId) {
|
|
||||||
item.answer = getResult(el.content)
|
|
||||||
item.resultId = el.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollToBottom = (height, index) => {
|
|
||||||
|
|
||||||
if (listRef.value) {
|
|
||||||
let sum = 0
|
|
||||||
let listDom = listRef.value.children
|
|
||||||
|
|
||||||
if (index == 0) {
|
|
||||||
// 220 去掉头部
|
|
||||||
let screenHeight = window.innerHeight - 220
|
|
||||||
if (height > screenHeight) {
|
|
||||||
listRef.value.scrollTop = (height - screenHeight + 50)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (let i = 0; i < index; i++) {
|
|
||||||
sum += listDom[i].clientHeight
|
|
||||||
}
|
|
||||||
listRef.value.scrollTop = sum + height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 去掉字符串中的 ### **
|
|
||||||
let getResult = (str) => {
|
|
||||||
let newStr = str.replace(/#+|(\*\*)/g, '');
|
|
||||||
return newStr
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = reactive(
|
|
||||||
{
|
|
||||||
prompt: '',
|
|
||||||
dataset_id: '',
|
|
||||||
template: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const prompt = ref('')
|
|
||||||
|
|
||||||
|
|
||||||
const addAiPPT = async (res) => {
|
const addAiPPT = async (res) => {
|
||||||
// res = { 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' }
|
// res = { 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' }
|
||||||
let node = courseObj.node
|
let node = courseObj.node
|
||||||
|
@ -373,6 +128,10 @@ const addAiPPT = async (res) => {
|
||||||
if (res_3 && res_3.code == 200) {
|
if (res_3 && res_3.code == 200) {
|
||||||
msgUtils.msgSuccess('生成PPT课件成功')
|
msgUtils.msgSuccess('生成PPT课件成功')
|
||||||
//TODO 打开生成的课件
|
//TODO 打开生成的课件
|
||||||
|
updateGen(parentid)
|
||||||
|
listEntpcoursefileNew({parentid: parentid}).then(res=>{
|
||||||
|
pptSlides.value = res.rows
|
||||||
|
})
|
||||||
const res = await getEntpcoursefile(parentid)
|
const res = await getEntpcoursefile(parentid)
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
||||||
|
@ -384,7 +143,25 @@ const addAiPPT = async (res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}).finally(()=>{
|
||||||
|
pgDialog.visible = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const openAiPPT = async () =>{
|
||||||
|
let parentid = result.value.parentId
|
||||||
|
const res = await getEntpcoursefile(parentid)
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
const smarttalk = getSmarttalkPage({fileId: parentid})
|
||||||
|
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg||'文件获取异常!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateGen = (parentid)=>{
|
||||||
|
result.value.parentId = parentid
|
||||||
|
editSyllabus(result.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openPublicScreen = (type, resource, currData)=> {
|
const openPublicScreen = (type, resource, currData)=> {
|
||||||
|
@ -401,92 +178,50 @@ const openPublicScreen = (type, resource, currData)=> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const isEdit = ref(false)
|
|
||||||
// 当前操作的索引
|
|
||||||
const curIndex = ref(-1)
|
|
||||||
// 当前操作的item
|
|
||||||
const curItem = reactive({})
|
|
||||||
|
|
||||||
// 重新生成
|
// 图片|音频|视频 转换为在线地址
|
||||||
const isAgain = ref(false)
|
const toRousrceUrl = async (o) => {
|
||||||
const againResult = async (index, item) => {
|
if (!!o.src) { // 如果有src就转换
|
||||||
isAgain.value = true
|
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
|
||||||
isStarted.value[index] = false
|
const isBlobUrl = /^blob:/.test(o.src)
|
||||||
resultList.value[index].answer = ''
|
console.log('isBase64', o, isBase64)
|
||||||
if (index == 0) {
|
if (isBase64) {
|
||||||
listRef.value.scrollTop = 0
|
const bolb = commUtils.base64ToBlob(o.src)
|
||||||
|
const fileName = Date.now() + '.png'
|
||||||
} else {
|
const file = commUtils.blobToFile(bolb, fileName)
|
||||||
scrollToBottom(50, index)
|
// o.src = fileName
|
||||||
}
|
// console.log('file', file)
|
||||||
|
const formData = new FormData()
|
||||||
try {
|
formData.append('file', file)
|
||||||
await nextTick()
|
const res = await Api_server.Other.uploadFile(formData)
|
||||||
resultList.value[index].loading = true
|
if (res && res.code == 200) {
|
||||||
item.aiShow = true
|
const url = res?.url
|
||||||
|
url && (o.src = url)
|
||||||
let str = cloneDeep(prompt.value)
|
}
|
||||||
str = str.replace(/{模板名称}/g, item.name)
|
} else if (isBlobUrl) { // 视频和音频
|
||||||
params.prompt = str
|
const res = await fetch(o.src)
|
||||||
params.template = item.prompt
|
const blob = await res.blob()
|
||||||
|
const fileName = o.type == 'video' ? Date.now() + '.mp4' : Date.now() + '.mp3'
|
||||||
let data = null;
|
const file = commUtils.blobToFile(blob, fileName)
|
||||||
// 教学大模型
|
// o.src = fileName
|
||||||
if (curMode.value == 1) {
|
// console.log('file', file)
|
||||||
const res = await sendChart({
|
const formData = new FormData()
|
||||||
content: params.prompt,
|
formData.append('file', file)
|
||||||
conversationId: conversation_id.value,
|
const ress = await Api_server.Other.uploadFile(formData)
|
||||||
stream: false
|
if (ress && ress.code == 200) {
|
||||||
})
|
const url = ress?.url
|
||||||
data = res.data
|
url && (o.src = url)
|
||||||
} else {
|
}
|
||||||
// 知识库模型
|
}
|
||||||
const res = await completion(params)
|
}
|
||||||
data = res.data
|
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
||||||
|
if (o?.elements) {
|
||||||
|
for (let element of o.elements) {
|
||||||
|
await toRousrceUrl(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
resultList.value[index].answer = getResult(data.answer)
|
|
||||||
isStarted.value[index] = true
|
|
||||||
} finally {
|
|
||||||
resultList.value[index].loading = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 对话调整
|
|
||||||
const isAdjust = ref(false)
|
|
||||||
const onAdjust = (index, item) => {
|
|
||||||
curIndex.value = index
|
|
||||||
Object.assign(curItem, item)
|
|
||||||
isAdjust.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 替换分析结果
|
|
||||||
emitter.on('onSaveAdjust', (item) => {
|
|
||||||
resultList.value[curIndex.value].answer = item
|
|
||||||
onEditSave(resultList.value[curIndex.value])
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// 保存 重新研读后的结果
|
|
||||||
const onEditSave = async (item) => {
|
|
||||||
const { msg } = await editTempResult({ id: item.resultId, content: item.answer })
|
|
||||||
ElMessage.success(msg)
|
|
||||||
getChildTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑
|
|
||||||
const onEdit = (index, item) => {
|
|
||||||
curIndex.value = index
|
|
||||||
Object.assign(curItem, item)
|
|
||||||
isEdit.value = true
|
|
||||||
}
|
|
||||||
emitter.on('changeResult', (item) => {
|
|
||||||
resultList.value[curIndex.value].answer = item
|
|
||||||
})
|
|
||||||
|
|
||||||
// ======== zdg start ============
|
|
||||||
// 统一HTTP处理
|
|
||||||
const HTTP_SERVER_API = (type, params = {}) => {
|
const HTTP_SERVER_API = (type, params = {}) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'addSmarttalk': { // 获取课程
|
case 'addSmarttalk': { // 获取课程
|
||||||
|
@ -564,214 +299,43 @@ const getDefParams = (params) => {
|
||||||
}
|
}
|
||||||
return Object.assign(def, params)
|
return Object.assign(def, params)
|
||||||
}
|
}
|
||||||
// 图片|音频|视频 转换为在线地址
|
onMounted(()=>{
|
||||||
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) { // 视频和音频
|
|
||||||
const res = await fetch(o.src)
|
|
||||||
const blob = await res.blob()
|
|
||||||
const fileName = o.type == 'video' ? Date.now() + '.mp4' : Date.now() + '.mp3'
|
|
||||||
const file = commUtils.blobToFile(blob, fileName)
|
|
||||||
// o.src = fileName
|
|
||||||
// console.log('file', file)
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', file)
|
|
||||||
const ress = await Api_server.Other.uploadFile(formData)
|
|
||||||
if (ress && ress.code == 200) {
|
|
||||||
const url = ress?.url
|
|
||||||
url && (o.src = url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
|
||||||
if (o?.elements) {
|
|
||||||
for (let element of o.elements) {
|
|
||||||
await toRousrceUrl(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ======== zdg end ============
|
|
||||||
|
|
||||||
// 创建对话
|
|
||||||
const conversation_id = ref('')
|
|
||||||
const getChartId = () => {
|
|
||||||
createChart({ app_id: '712ff0df-ed6b-470f-bf87-8cfbaf757be5' }).then(res => {
|
|
||||||
localStorage.setItem("conversation_id", res.data.conversation_id);
|
|
||||||
conversation_id.value = res.data.conversation_id;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询prompt 替换
|
|
||||||
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.split('-')[1] + '版本'
|
|
||||||
str = str.replace('{教材版本}', bookV)
|
|
||||||
str = str.replace('{课程名称}', `《${curNode.itemtitle}》`)
|
|
||||||
prompt.value = str
|
|
||||||
}
|
|
||||||
|
|
||||||
const curNode = reactive({})
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data);
|
||||||
courseObj.node = data
|
courseObj.node = data
|
||||||
// 框架设计 用课标的dataset_id
|
|
||||||
let jsonKey = `课标-${data.edustage}-${data.edusubject}`
|
|
||||||
params.dataset_id = commUtils.dataSetJson[jsonKey]
|
|
||||||
|
|
||||||
// 获取百度千帆会话ID
|
|
||||||
conversation_id.value = localStorage.getItem('conversation_id')
|
|
||||||
if (!conversation_id.value) {
|
|
||||||
getChartId();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取prompt
|
|
||||||
getPrompt()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(()=>{
|
||||||
// 解绑
|
emitter.off('onResult')
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('changeMode')
|
|
||||||
emitter.off('changeResult')
|
|
||||||
emitter.off('changeAdjust')
|
|
||||||
emitter.off('onSaveAdjust');
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container-right {
|
.container-right{
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
font-size: 15px;
|
||||||
.right-header {
|
flex-direction: column;
|
||||||
height: 45px;
|
.right-header{
|
||||||
background: #fff;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 20px;
|
.icon-jiahao{
|
||||||
border-radius: 0 5px 0 0;
|
font-size: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.right-con{
|
||||||
.right-con {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #F6F6F6;
|
margin-top: 5px;
|
||||||
padding: 15px;
|
background-color: #fff;
|
||||||
flex-direction: column;
|
border-radius: 5px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
.right-con-item{
|
||||||
.con-item {
|
display: flex;
|
||||||
width: 100%;
|
margin: 10px 0;
|
||||||
flex-direction: column;
|
justify-content: space-around;
|
||||||
padding-bottom: 20px;
|
img{
|
||||||
position: relative;
|
width: 150px;
|
||||||
padding-left: 15px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #409eff;
|
|
||||||
position: absolute;
|
|
||||||
left: -8px;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
width: 2px;
|
|
||||||
height: 100%;
|
|
||||||
background: #409eff;
|
|
||||||
position: absolute;
|
|
||||||
left: -1px;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
width: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-top {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.icon-shenglvehao {
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-bom {
|
|
||||||
background: #fff;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
.item-prompt {
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-answer {
|
|
||||||
padding: 10px;
|
|
||||||
background: #F2F2F2;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
.answer-text {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-btn {
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-ai1 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
<style>
|
|
||||||
.template-custom-popover {
|
|
||||||
width: 110px !important;
|
|
||||||
min-width: 110px !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,340 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-right flex">
|
|
||||||
<div class="right-header flex">
|
|
||||||
<span>课件预览</span>
|
|
||||||
<div>
|
|
||||||
<el-button type="danger" @click="onCreate">一键生成</el-button>
|
|
||||||
<el-button :disabled="!result?.parentId" @click="openAiPPT">编辑课件</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-con">
|
|
||||||
<el-empty v-if="!result?.parentId" description="请先生成教学大纲,再生成教学课件" />
|
|
||||||
<div v-for="(item,index) in pptSlides" class="right-con-item">
|
|
||||||
<div>{{index+1}}</div><img :src="item.fileurl">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<PptDialog @close-dialogs="pptDialog = false" @add-success="addAiPPT" :dataList="result" v-model="pptDialog" />
|
|
||||||
<progress-dialog v-model:visible="pgDialog.visible" v-bind="pgDialog" />
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import {ref, onUnmounted, onMounted, reactive} from 'vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import emitter from '@/utils/mitt'
|
|
||||||
import progressDialog from './progress-dialog.vue'
|
|
||||||
import PptDialog from "@/views/prepare/container/pptist-dialog.vue";
|
|
||||||
import msgUtils from "@/plugins/modal";
|
|
||||||
import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport";
|
|
||||||
import {slidesToImg} from "@/utils/ppt";
|
|
||||||
import {getEntpcoursefile, listEntpcoursefileNew} from "@/api/education/entpcoursefile";
|
|
||||||
import {sessionStore} from "@/utils/store";
|
|
||||||
import * as API_smarttalk from "@/api/file";
|
|
||||||
import * as API_entpcourse from "@/api/education/entpcourse";
|
|
||||||
import * as API_entpcoursefile from "@/api/education/entpcoursefile";
|
|
||||||
import * as commUtils from '@/utils/comm.js'
|
|
||||||
import * as Api_server from '@/api/apiService' // 相关api
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import {createWindow} from "@/utils/tool";
|
|
||||||
import {editSyllabus} from "@/api/mode";
|
|
||||||
import { getSmarttalkPage } from "@/api/file"
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
const pptDialog = ref(false)
|
|
||||||
const result = ref(null)
|
|
||||||
const courseObj = reactive({
|
|
||||||
node: null, // 选择的课程节点
|
|
||||||
})
|
|
||||||
const curNode = reactive({})
|
|
||||||
const pgDialog = reactive({ // 弹窗-进度条
|
|
||||||
visible: false,
|
|
||||||
title: 'PPT解析中...',
|
|
||||||
width: 300,
|
|
||||||
showClose: false,
|
|
||||||
draggable: true,
|
|
||||||
beforeClose: done => { }, // 阻止-弹窗事件
|
|
||||||
pg: { // 进度条-参数
|
|
||||||
percentage: 0, // 百分比
|
|
||||||
color: [
|
|
||||||
{ color: '#1989fa', percentage: 50 }, // 蓝色
|
|
||||||
{ color: '#e6a23c', percentage: 80 }, // 橙色
|
|
||||||
{ color: '#5cb87a', percentage: 100 }, // 绿色
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const pptSlides = ref([])
|
|
||||||
|
|
||||||
emitter.on('onResult', (data)=>{
|
|
||||||
result.value = data
|
|
||||||
if (!!result.value.parentId) {
|
|
||||||
listEntpcoursefileNew({parentid: result.value.parentId}).then(res=>{
|
|
||||||
pptSlides.value = res.rows
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const onCreate = () =>{
|
|
||||||
if(!result.value){
|
|
||||||
ElMessage.warning('请先进行研读')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pptDialog.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const addAiPPT = async (res) => {
|
|
||||||
// res = { 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' }
|
|
||||||
let node = courseObj.node
|
|
||||||
pptDialog.value = false;
|
|
||||||
if (!node) return msgUtils.msgWarning('请选择章节?')
|
|
||||||
pgDialog.visible = true
|
|
||||||
pgDialog.pg.percentage = 0
|
|
||||||
//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
|
|
||||||
// 生成缩略图
|
|
||||||
const thumbnails = await slidesToImg(slides, content.width)
|
|
||||||
// 转换图片|音频|视频 为线上地址
|
|
||||||
let completed = 0
|
|
||||||
const total = slides.length
|
|
||||||
for (let o of slides) {
|
|
||||||
completed++
|
|
||||||
await toRousrceUrl(o)
|
|
||||||
// 设置进度条
|
|
||||||
pgDialog.pg.percentage = Math.floor(completed / total * 100)
|
|
||||||
}
|
|
||||||
pgDialog.pg.percentage = 0
|
|
||||||
pgDialog.visible = false
|
|
||||||
// 生成ppt课件-父级
|
|
||||||
const p_params = { parentContent: JSON.stringify(content) }
|
|
||||||
const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
|
||||||
if (!!parentid ?? null) { // 生成内容幻灯片
|
|
||||||
// 生成备课资源-Smarttalk
|
|
||||||
const smarttalk = await HTTP_SERVER_API('addSmarttalk', { fileId: parentid })
|
|
||||||
if (slides.length > 0) {
|
|
||||||
const resSlides = slides.map(({ id, ...slide }) => JSON.stringify(slide))
|
|
||||||
const params = { parentid, filetype: 'slide', title: '', thumbnails, slides: resSlides }
|
|
||||||
const res_3 = await HTTP_SERVER_API('batchAddNew', params)
|
|
||||||
if (res_3 && res_3.code == 200) {
|
|
||||||
msgUtils.msgSuccess('生成PPT课件成功')
|
|
||||||
//TODO 打开生成的课件
|
|
||||||
updateGen(parentid)
|
|
||||||
listEntpcoursefileNew({parentid: parentid}).then(res=>{
|
|
||||||
pptSlides.value = res.rows
|
|
||||||
})
|
|
||||||
const res = await getEntpcoursefile(parentid)
|
|
||||||
if (res && res.code === 200) {
|
|
||||||
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
|
||||||
} else {
|
|
||||||
ElMessage.warning(res.msg||'文件获取异常!')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
msgUtils.msgWarning('生成PPT课件失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).finally(()=>{
|
|
||||||
pgDialog.visible = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const openAiPPT = async () =>{
|
|
||||||
let parentid = result.value.parentId
|
|
||||||
const res = await getEntpcoursefile(parentid)
|
|
||||||
if (res && res.code === 200) {
|
|
||||||
const smarttalk = getSmarttalkPage({fileId: parentid})
|
|
||||||
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
|
||||||
} else {
|
|
||||||
ElMessage.warning(res.msg||'文件获取异常!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateGen = (parentid)=>{
|
|
||||||
result.value.parentId = parentid
|
|
||||||
editSyllabus(result.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const openPublicScreen = (type, resource, currData)=> {
|
|
||||||
sessionStore.set('curr.resource', resource) // 缓存当前资源信息
|
|
||||||
if (type=='edit') sessionStore.set('curr.smarttalk', currData) // 缓存当前文件smarttalk
|
|
||||||
else sessionStore.set('curr.classcourse', currData) // 缓存当前当前上课
|
|
||||||
createWindow('open-win', {
|
|
||||||
url: '/pptist', // 窗口关闭时,清除缓存
|
|
||||||
close: () => {
|
|
||||||
sessionStore.set('curr.resource', null) // 清除缓存
|
|
||||||
if (type=='edit') {
|
|
||||||
sessionStore.set('curr.smarttalk', null) // 清除缓存
|
|
||||||
} else sessionStore.set('curr.classcourse', null) // 清除缓存
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 图片|音频|视频 转换为在线地址
|
|
||||||
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) { // 视频和音频
|
|
||||||
const res = await fetch(o.src)
|
|
||||||
const blob = await res.blob()
|
|
||||||
const fileName = o.type == 'video' ? Date.now() + '.mp4' : Date.now() + '.mp3'
|
|
||||||
const file = commUtils.blobToFile(blob, fileName)
|
|
||||||
// o.src = fileName
|
|
||||||
// console.log('file', file)
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', file)
|
|
||||||
const ress = await Api_server.Other.uploadFile(formData)
|
|
||||||
if (ress && ress.code == 200) {
|
|
||||||
const url = ress?.url
|
|
||||||
url && (o.src = url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
|
||||||
if (o?.elements) {
|
|
||||||
for (let element of o.elements) {
|
|
||||||
await toRousrceUrl(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HTTP_SERVER_API = (type, params = {}) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'addSmarttalk': { // 获取课程
|
|
||||||
const node = courseObj.node || {}
|
|
||||||
const def = {
|
|
||||||
fileId: '', // 文件id - Entpcoursefile 对应id
|
|
||||||
fileFlag: 'aippt',
|
|
||||||
fileShowName: node.itemtitle + '.aippt',
|
|
||||||
textbookId: node.rootid,
|
|
||||||
levelFirstId: node.parentid || node.id,
|
|
||||||
levelSecondId: node.parentid && node.id,
|
|
||||||
fileSource: '个人',
|
|
||||||
fileRoot: '备课'
|
|
||||||
}
|
|
||||||
return API_smarttalk.creatAPT({ ...def, ...params })
|
|
||||||
}
|
|
||||||
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: 'aippt',
|
|
||||||
datacontent: '',
|
|
||||||
filekey: '',
|
|
||||||
filetag: '',
|
|
||||||
fileidx: 0,
|
|
||||||
dflag: 0,
|
|
||||||
status: '',
|
|
||||||
edituserid: userStore.id
|
|
||||||
}
|
|
||||||
return Object.assign(def, params)
|
|
||||||
}
|
|
||||||
onMounted(()=>{
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
courseObj.node = data
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(()=>{
|
|
||||||
emitter.off('onResult')
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container-right{
|
|
||||||
height: 100%;
|
|
||||||
font-size: 15px;
|
|
||||||
flex-direction: column;
|
|
||||||
.right-header{
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
.icon-jiahao{
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.right-con{
|
|
||||||
flex: 1;
|
|
||||||
margin-top: 5px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow-y: auto;
|
|
||||||
.right-con-item{
|
|
||||||
display: flex;
|
|
||||||
margin: 10px 0;
|
|
||||||
justify-content: space-around;
|
|
||||||
img{
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,54 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-design">
|
<div class="page-design">
|
||||||
<!-- <div class="page-left">
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="5">
|
||||||
<left />
|
<Left />
|
||||||
</div>
|
</el-col>
|
||||||
<div class="page-right">
|
<el-col :span="14">
|
||||||
|
<Center />
|
||||||
<right />
|
</el-col>
|
||||||
</div> -->
|
<el-col :span="5">
|
||||||
<el-row :gutter="20">
|
<Right />
|
||||||
<el-col :span="5">
|
</el-col>
|
||||||
<Left/>
|
</el-row>
|
||||||
</el-col>
|
|
||||||
<el-col :span="15">
|
|
||||||
<Center/>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="4">
|
|
||||||
<Right/>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
// import left from './container/left.vue';
|
import Left from './container/left.vue'
|
||||||
// import right from './container/right.vue';
|
|
||||||
import Left from './container/left2.vue'
|
|
||||||
import Center from './container/center.vue'
|
import Center from './container/center.vue'
|
||||||
import Right from './container/right2.vue'
|
import Right from './container/right.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-design {
|
.page-design {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.el-row{
|
|
||||||
|
.el-row {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.el-col{
|
|
||||||
|
.el-col {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .page-design {
|
|
||||||
// height: 100%;
|
|
||||||
// .page-left{
|
|
||||||
// width: 50%;
|
|
||||||
// }
|
|
||||||
// .page-right{
|
|
||||||
// width: 50%;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { env } = require('process');
|
||||||
|
|
||||||
|
// 读取当前的package.json文件
|
||||||
|
const packageJsonPath = path.join(__dirname, 'package.json');
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||||
|
console.log(env)
|
||||||
|
let res = env.npm_lifecycle_event.replace("build", "").replace(":", "");
|
||||||
|
res = res?"-" + res:"";
|
||||||
|
packageJson.name = "aix-win-ws" + res
|
||||||
|
|
||||||
|
// 将修改后的内容写回package.json文件
|
||||||
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
Loading…
Reference in New Issue