Merge pull request 'lyc-dev' (#207) from lyc-dev into main
This commit is contained in:
commit
284ae40519
|
@ -136,14 +136,6 @@ export function syllabusList(params) {
|
|||
})
|
||||
}
|
||||
|
||||
// 大纲详情
|
||||
export function syllabuss(id) {
|
||||
return request({
|
||||
url: '/education/generate/' + id,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 删除大纲
|
||||
export function removeSyllabus(id) {
|
||||
return request({
|
||||
|
|
|
@ -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>
|
||||
删除大纲
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" @click="isEdit = true">
|
||||
<i class="iconfont icon-bianji"></i>编辑大纲
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center-con" v-loading="loading">
|
||||
<!-- <TypingEffect v-if="answer" :text="answer" :delay="10" :aiShow="aiShow"/> -->
|
||||
<div style="font-size: 18px;color: #409eff;">封面页</div>
|
||||
<template v-if="answer.title">
|
||||
<div class="flex justify-between">
|
||||
<span style="font-size: 18px;color: #409eff;">封面页</span>
|
||||
<el-button type="primary" link @click="onEdit(item, -1)">编辑</el-button>
|
||||
</div>
|
||||
<div class="con-item mb-5">
|
||||
<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">{{index + 1}}:{{ item.chapterTitle }}</div>
|
||||
<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>
|
||||
<el-empty v-if="!answer.title" description="请选择符合您需要的教学模式,生成教学大纲" />
|
||||
</template>
|
||||
<el-empty v-else description="请选择符合您需要的教学模式,生成教学大纲" />
|
||||
</div>
|
||||
</div>
|
||||
<EditDialog v-model="isEdit" :item="curItem" />
|
||||
<EditDialog v-model="isEdit" :item="editItem" :index="editIndex" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
@ -47,9 +51,10 @@ import EditDialog from './edit-dialog.vue'
|
|||
import emitter from '@/utils/mitt'
|
||||
import * as commUtils from '@/utils/comm.js'
|
||||
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 useUserStore from '@/store/modules/user'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
const curMode = ref(2)
|
||||
const isEdit = ref(false)
|
||||
|
@ -76,20 +81,14 @@ emitter.on('selected', (data)=>{
|
|||
// 回显大纲
|
||||
const curItem = reactive({})
|
||||
emitter.on('onShow', (data) => {
|
||||
console.log(data)
|
||||
aiShow.value = false
|
||||
Object.assign(answer, JSON.parse(data.outline))
|
||||
|
||||
Object.assign(curItem, data)
|
||||
curItem.answer = curItem.outline
|
||||
getDetails(data.id)
|
||||
curItem.outline = JSON.parse(curItem.outline)
|
||||
emitter.emit('onResult',curItem)
|
||||
})
|
||||
|
||||
const getDetails = (id) =>{
|
||||
syllabuss(id).then( res =>{
|
||||
Object.assign(curItem, res.data)
|
||||
emitter.emit('onResult', res.data)
|
||||
})
|
||||
}
|
||||
|
||||
const params = reactive(
|
||||
{
|
||||
|
@ -135,15 +134,56 @@ const createAi = async ()=>{
|
|||
data = res.data
|
||||
}
|
||||
const res = await createOutlineV2({ query: data.answer })
|
||||
console.log(res)
|
||||
emitter.emit('onResult', res)
|
||||
|
||||
Object.assign(answer, res.outline)
|
||||
curItem.outline = res.outline
|
||||
emitter.emit('onResult', curItem)
|
||||
onSaveTemp(JSON.stringify(res.outline))
|
||||
} finally {
|
||||
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) => {
|
||||
|
@ -195,6 +235,8 @@ const getChartId = () => {
|
|||
onUnmounted(() => {
|
||||
emitter.off('selected')
|
||||
emitter.off('onShow')
|
||||
emitter.off('editItem')
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
@ -220,13 +262,16 @@ onMounted(() => {
|
|||
height: 100%;
|
||||
font-size: 15px;
|
||||
flex-direction: column;
|
||||
|
||||
.center-header {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.icon-jiahao {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.center-con {
|
||||
flex: 1;
|
||||
margin-top: 5px;
|
||||
|
@ -235,10 +280,15 @@ onMounted(() => {
|
|||
text-align: left;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
|
||||
.con-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 15px;
|
||||
.item-name{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.item-text {
|
||||
background: #F2F2F2;
|
||||
padding: 15px;
|
||||
|
|
|
@ -7,11 +7,28 @@
|
|||
</div>
|
||||
</template>
|
||||
<div class="dialog-content" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-input v-model="textarea" :autosize="{ minRows: 5, maxRows: 15 }" type="textarea" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template v-if="props.index == -1">
|
||||
<div class="flex mb-5">
|
||||
<span class="name">标题:</span>
|
||||
<el-input v-model="editItem.title" />
|
||||
</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>
|
||||
<template #footer>
|
||||
|
@ -26,13 +43,13 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch} from 'vue'
|
||||
import { reactive, ref, watch} from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { editSyllabus } from '@/api/mode/index.js'
|
||||
import { cloneDeep } from 'lodash';
|
||||
import emitter from '@/utils/mitt';
|
||||
|
||||
const textarea = ref('')
|
||||
|
||||
|
||||
const isDialog = defineModel()
|
||||
const loading = ref(false)
|
||||
|
@ -40,20 +57,28 @@ const loading = ref(false)
|
|||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return { name: '11' }
|
||||
}
|
||||
|
||||
},
|
||||
index: {
|
||||
type: [Number, String]
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.item.answer, (newVal) => {
|
||||
const editItem = reactive({})
|
||||
|
||||
watch(() => isDialog.value, (newVal) => {
|
||||
if(newVal){
|
||||
textarea.value = newVal
|
||||
let data = cloneDeep(props.item)
|
||||
Object.assign(editItem, data)
|
||||
}
|
||||
|
||||
},{ deep: true })
|
||||
|
||||
const emit = defineEmits(['saveEdit'])
|
||||
const onSave = () =>{
|
||||
emitter.emit('editItem', editItem)
|
||||
isDialog.value = false
|
||||
return
|
||||
loading.value = true
|
||||
let data = cloneDeep(props.item)
|
||||
data.outline = textarea.value
|
||||
|
@ -83,6 +108,12 @@ const onSave = () =>{
|
|||
|
||||
.dialog-content {
|
||||
padding-top: 10px;
|
||||
|
||||
.name{
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.edit-con{
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
<div class="container-left flex">
|
||||
<div class="left-header flex">教学模式</div>
|
||||
<div class="left-con" v-loading="loading">
|
||||
<el-empty v-if="!(tempList.length)" description="暂无数据" />
|
||||
<div class="con-item" v-for="item in tempList" :key="item.id" :class=" actId == item.id ? 'item-act' : ''">
|
||||
<div class="item-header flex">
|
||||
<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>
|
||||
<el-button type="primary" link @click="onSelect(item)">选择模式</el-button>
|
||||
<!--个人教学模式才会有添加、编辑-->
|
||||
<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 class="content-list">
|
||||
<div class="item-list flex" >
|
||||
<el-card class="item-card" shadow="never" v-for="el in item.child" :key="el.id">
|
||||
<p class="card-name">
|
||||
<el-text line-clamp="1" :title="el.name">
|
||||
{{ el.name }}
|
||||
</el-text>
|
||||
</p>
|
||||
<div class="card-text">
|
||||
<el-text line-clamp="4" :title="el.prompt">
|
||||
{{ el.prompt }}
|
||||
</el-text>
|
||||
</div>
|
||||
</el-card>
|
||||
<!--加号 数鼠标悬浮 显示-->
|
||||
<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 emitter from '@/utils/mitt';
|
||||
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()
|
||||
|
||||
// 获取模板
|
||||
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 tempList = ref([])
|
||||
const getChildTemp = async (parentId) => {
|
||||
tempList.value.length = 0
|
||||
const templateList = ref([])
|
||||
const getTemplate = async () => {
|
||||
loading.value = true
|
||||
for (let item of list.value) {
|
||||
try {
|
||||
const { rows } = await modelList({ model: 4, type: 2, parentId: item.id })
|
||||
tempList.value.push({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
child: rows
|
||||
const { rows } = await modelList({
|
||||
createUser: user.userId,
|
||||
model: 4,
|
||||
pageNum: 1,
|
||||
pageSize: 10000
|
||||
})
|
||||
} finally {
|
||||
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()
|
||||
}
|
||||
actId.value = tempList.value[0].id
|
||||
|
||||
// 查询生成大纲记录
|
||||
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)
|
||||
}
|
||||
if(parentId){
|
||||
const item = tempList.value.find(item => item.id == parentId)
|
||||
emitter.emit('changeMode', item)
|
||||
})
|
||||
// 更新父项的 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 {
|
||||
emitter.emit('changeMode', tempList.value[0])
|
||||
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)
|
||||
|
||||
// 操作之后获取子模板
|
||||
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)
|
||||
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);
|
||||
Object.assign(curNode, data)
|
||||
getTemplate()
|
||||
})
|
||||
|
||||
|
||||
// 解绑
|
||||
onUnmounted(() => {
|
||||
|
||||
emitter.off('onGetMain');
|
||||
emitter.off('onGetChild');
|
||||
emitter.off('resetSelect')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-left {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 15px;
|
||||
flex-direction: column;
|
||||
|
||||
.left-header {
|
||||
height: 45px;
|
||||
background: #F6F6F6;
|
||||
border-radius: 5px 0 0 0;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-indent: 2em;
|
||||
}
|
||||
|
||||
.left-con {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 0 0 0 5px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
overflow: auto;
|
||||
.item-header {
|
||||
height: 32px;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.con-item {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
}
|
||||
.item-list{
|
||||
align-items: center;
|
||||
|
||||
padding: 10px;
|
||||
.icon-jiahao {
|
||||
font-size: 12px;
|
||||
}
|
||||
.item-act{
|
||||
border: solid 1px #409eff;
|
||||
}
|
||||
|
||||
.left-list {
|
||||
flex: 1;
|
||||
margin-top: 5px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.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 {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
text-align: left;
|
||||
padding: 0 10px;
|
||||
line-height: 30px;
|
||||
overflow-y: auto;
|
||||
|
||||
.el-text{
|
||||
font-size: 12px !important;
|
||||
.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;
|
||||
}
|
||||
.content-list{
|
||||
overflow-x: auto
|
||||
}
|
||||
.content-list::-webkit-scrollbar {
|
||||
height: 8px;
|
||||
|
||||
.form-btn {
|
||||
text-align: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</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>
|
||||
<div class="container-right flex">
|
||||
<div class="right-header flex">
|
||||
<div class="header-left">
|
||||
<!-- <el-button type="primary" link>
|
||||
<i class="iconfont icon-jiahao"></i>新活动
|
||||
</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>
|
||||
<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 flex" ref="listRef">
|
||||
<el-empty v-if="!(resultList.length)" description="暂无数据" />
|
||||
<div class="con-item flex" v-for="(item, index) in resultList" :key="item.id" v-loading="item.loading">
|
||||
<div class="item-top flex">
|
||||
<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 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>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<EditDialog v-model="isEdit" :item="curItem" />
|
||||
<AdjustDialog v-model="isAdjust" :item="curItem" :curMode="curMode" :conversation_id="conversation_id" />
|
||||
<PptDialog @add-success="addAiPPT" :dataList="resultList" v-model="pptDialog" />
|
||||
<PptDialog @close-dialogs="pptDialog = false" @add-success="addAiPPT" :dataList="result" v-model="pptDialog" />
|
||||
<progress-dialog v-model:visible="pgDialog.visible" v-bind="pgDialog" />
|
||||
<!--添加、编辑提示词-->
|
||||
<keywordDialog v-model="isWordDialog" :item="curItem" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, reactive, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import {ref, onUnmounted, onMounted, reactive} from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import emitter from '@/utils/mitt'
|
||||
import EditDialog from './edit-dialog.vue'
|
||||
import AdjustDialog from './adjust-dialog.vue'
|
||||
import progressDialog from './progress-dialog.vue'
|
||||
import { completion, tempResult, tempSave, removeChildTemp, editTempResult, modelList } from '@/api/mode/index.js'
|
||||
import { createChart, sendChart } from '@/api/ai/index'
|
||||
// import { dataSetJson } from '@/utils/comm.js'
|
||||
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 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_smarttalk from '@/api/file' // 文件相关api
|
||||
import msgUtils from '@/plugins/modal'
|
||||
import { getEntpcoursefile } from '@/api/education/entpcoursefile'
|
||||
import { createWindow } from '@/utils/tool' // 消息工具
|
||||
import { slidesToImg } from '@/utils/ppt' // ppt相关工具
|
||||
|
||||
import {createWindow} from "@/utils/tool";
|
||||
import {editSyllabus} from "@/api/mode";
|
||||
import { getSmarttalkPage } from "@/api/file"
|
||||
import useUserStore from '@/store/modules/user'
|
||||
const userStore = useUserStore()
|
||||
const pptDialog = ref(false)
|
||||
const resultList = ref([])
|
||||
const result = ref(null)
|
||||
const courseObj = reactive({
|
||||
node: null, // 选择的课程节点
|
||||
})
|
||||
const curNode = reactive({})
|
||||
const pgDialog = reactive({ // 弹窗-进度条
|
||||
visible: false,
|
||||
title: 'PPT解析中...',
|
||||
|
@ -119,214 +61,27 @@ const pgDialog = reactive({ // 弹窗-进度条
|
|||
}
|
||||
})
|
||||
|
||||
const curMode = ref(2)
|
||||
const modeOptions = ref([
|
||||
{
|
||||
label: '教学大模型',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '知识库模型',
|
||||
value: 2
|
||||
}
|
||||
])
|
||||
const pptSlides = ref([])
|
||||
|
||||
emitter.on('changeMode', (item) => {
|
||||
resultList.value = item.child
|
||||
getTempResult(item.id)
|
||||
emitter.on('onResult', (data)=>{
|
||||
result.value = data
|
||||
if (!!result.value.parentId) {
|
||||
listEntpcoursefileNew({parentid: result.value.parentId}).then(res=>{
|
||||
pptSlides.value = res.rows
|
||||
})
|
||||
}else {
|
||||
pptSlides.value = []
|
||||
}
|
||||
})
|
||||
|
||||
const onCreate = () =>{
|
||||
let isAnswer = resultList.value.every(item => !item.answer)
|
||||
if(isAnswer){
|
||||
if(!result.value){
|
||||
ElMessage.warning('请先进行研读')
|
||||
return
|
||||
}
|
||||
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) => {
|
||||
// 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
|
||||
|
@ -373,6 +128,10 @@ const addAiPPT = async (res) => {
|
|||
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) // 打开公屏-窗口
|
||||
|
@ -384,9 +143,27 @@ 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)=> {
|
||||
sessionStore.set('curr.resource', resource) // 缓存当前资源信息
|
||||
if (type=='edit') sessionStore.set('curr.smarttalk', currData) // 缓存当前文件smarttalk
|
||||
|
@ -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 againResult = async (index, item) => {
|
||||
isAgain.value = true
|
||||
isStarted.value[index] = false
|
||||
resultList.value[index].answer = ''
|
||||
if (index == 0) {
|
||||
listRef.value.scrollTop = 0
|
||||
|
||||
} else {
|
||||
scrollToBottom(50, index)
|
||||
// 图片|音频|视频 转换为在线地址
|
||||
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)
|
||||
}
|
||||
|
||||
try {
|
||||
await nextTick()
|
||||
resultList.value[index].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
|
||||
} 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);
|
||||
}
|
||||
|
||||
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 = {}) => {
|
||||
switch (type) {
|
||||
case 'addSmarttalk': { // 获取课程
|
||||
|
@ -564,214 +299,43 @@ const getDefParams = (params) => {
|
|||
}
|
||||
return Object.assign(def, params)
|
||||
}
|
||||
// 图片|音频|视频 转换为在线地址
|
||||
const toRousrceUrl = async (o) => {
|
||||
if (!!o.src) { // 如果有src就转换
|
||||
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
|
||||
const isBlobUrl = /^blob:/.test(o.src)
|
||||
console.log('isBase64', o, isBase64)
|
||||
if (isBase64) {
|
||||
const bolb = commUtils.base64ToBlob(o.src)
|
||||
const fileName = Date.now() + '.png'
|
||||
const file = commUtils.blobToFile(bolb, fileName)
|
||||
// o.src = fileName
|
||||
// console.log('file', file)
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const res = await Api_server.Other.uploadFile(formData)
|
||||
if (res && res.code == 200) {
|
||||
const url = res?.url
|
||||
url && (o.src = url)
|
||||
}
|
||||
} else if (isBlobUrl) { // 视频和音频
|
||||
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')
|
||||
Object.assign(curNode, 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('changeMode')
|
||||
emitter.off('changeResult')
|
||||
emitter.off('changeAdjust')
|
||||
emitter.off('onSaveAdjust');
|
||||
emitter.off('onResult')
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-right{
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
font-size: 15px;
|
||||
flex-direction: column;
|
||||
.right-header{
|
||||
height: 45px;
|
||||
background: #fff;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
border-radius: 0 5px 0 0;
|
||||
.icon-jiahao{
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-con{
|
||||
flex: 1;
|
||||
background: #F6F6F6;
|
||||
padding: 15px;
|
||||
flex-direction: column;
|
||||
margin-top: 5px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
overflow-y: auto;
|
||||
|
||||
.con-item {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
padding-bottom: 20px;
|
||||
position: relative;
|
||||
padding-left: 15px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
background: #409eff;
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 5px;
|
||||
.right-con-item{
|
||||
display: flex;
|
||||
margin: 10px 0;
|
||||
justify-content: space-around;
|
||||
img{
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
&::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>
|
||||
|
|
|
@ -1,341 +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 {createWindow} from "@/utils/tool";
|
||||
import {editSyllabus} from "@/api/mode";
|
||||
import { getSmarttalkPage } from "@/api/file"
|
||||
import useUserStore from '@/store/modules/user'
|
||||
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
|
||||
})
|
||||
}else {
|
||||
pptSlides.value = []
|
||||
}
|
||||
})
|
||||
|
||||
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>
|
||||
<div class="page-design">
|
||||
<!-- <div class="page-left">
|
||||
|
||||
<left />
|
||||
</div>
|
||||
<div class="page-right">
|
||||
|
||||
<right />
|
||||
</div> -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="5">
|
||||
<Left />
|
||||
</el-col>
|
||||
<el-col :span="15">
|
||||
<el-col :span="14">
|
||||
<Center />
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-col :span="5">
|
||||
<Right />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import left from './container/left.vue';
|
||||
// import right from './container/right.vue';
|
||||
import Left from './container/left2.vue'
|
||||
import Left from './container/left.vue'
|
||||
import Center from './container/center.vue'
|
||||
import Right from './container/right2.vue'
|
||||
import Right from './container/right.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-design {
|
||||
height: 100%;
|
||||
|
||||
.el-row {
|
||||
height: 100%;
|
||||
|
||||
.el-col {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .page-design {
|
||||
// height: 100%;
|
||||
// .page-left{
|
||||
// width: 50%;
|
||||
// }
|
||||
// .page-right{
|
||||
// width: 50%;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue