This commit is contained in:
zhangxuelin 2024-12-11 09:58:08 +08:00
commit bace41e12e
24 changed files with 369 additions and 1656 deletions

View File

@ -60,7 +60,6 @@ export class PPTApi {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 } const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 }
const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params) const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params)
console.log(res.rows,'res.rows');
if (res.code === 200) { if (res.code === 200) {
const slides = (res.rows || []).map(o => { const slides = (res.rows || []).map(o => {
if (!!o.datacontent) { if (!!o.datacontent) {
@ -74,7 +73,8 @@ export class PPTApi {
// 活动列表处理 // 活动列表处理
const workList = (res.rows || []).map(o => o.activityContent) const workList = (res.rows || []).map(o => o.activityContent)
const workItem = [...res.rows] const workItem = [...res.rows]
slidesStore.updateSlideIndex(0) // 下标0 为第一页 // 加入活动后刷新ppt数据内容不跟换为第一页
// slidesStore.updateSlideIndex(0) // 下标0 为第一页
slidesStore.setSlides(slides) // 写入数据 slidesStore.setSlides(slides) // 写入数据
// 写入作业列表数据 // 写入作业列表数据
slidesStore.setWorkList(workList) slidesStore.setWorkList(workList)

View File

@ -30,7 +30,7 @@
<Divider /> <Divider />
<!-- 作业列表 --> <!-- 作业列表 -->
<div class="c-apt-r"> <div class="c-apt-r" v-loading='loadingActive'>
<!-- 显示-作业内容 --> <!-- 显示-作业内容 -->
<template v-for="(item, index) in workList" :key="index"> <template v-for="(item, index) in workList" :key="index">
<div class="item"> <div class="item">
@ -45,9 +45,11 @@
</template> </template>
</div> </div>
<!-- // --> <!-- // -->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="80%" height="500"> <el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="90%" height="500">
<el-scrollbar height="500"> <el-scrollbar height="550">
<div style="height: 550px;">
<NewClassTsakAssign :currentCourse='currentCourse' @getData="getData" /> <NewClassTsakAssign :currentCourse='currentCourse' @getData="getData" />
</div>
</el-scrollbar> </el-scrollbar>
</el-dialog> </el-dialog>
<!-- 活动引用 --> <!-- 活动引用 -->
@ -142,7 +144,6 @@ const currentCourse = reactive<CurrentCourse>({
worktype: '', worktype: '',
}) })
const dataList = ref<WorkItem[]>([])
const dialogVisible = ref<boolean>(false) const dialogVisible = ref<boolean>(false)
const tasklist_loading = ref<boolean>(false) const tasklist_loading = ref<boolean>(false)
@ -152,11 +153,6 @@ const taskList = ref<WorkItem[]>([])
// //
const activeVisible = ref<boolean>(false) const activeVisible = ref<boolean>(false)
const params = reactive<Params>({
parentid: 14766,
pageSize: 500,
orderby: 'fileidx'
})
const type = ref<WorkType[]>([ const type = ref<WorkType[]>([
{ {
@ -179,6 +175,8 @@ const workList = ref<WorkItem[]>([])
// //
const selectedWorkList = ref<WorkItem[]>([]) const selectedWorkList = ref<WorkItem[]>([])
// loading
const loadingActive = ref<boolean>(false)
const paramData = ref<{ id: number, activityContent: string }>({} as { id: number, activityContent: string }) const paramData = ref<{ id: number, activityContent: string }>({} as { id: number, activityContent: string })
@ -196,7 +194,6 @@ const formatClassWorkFile = async (postData: WorkItem[]): Promise<void> => {
} }
break; break;
case '习题训练': { case '习题训练': {
console.log(item,'item');
// let workIds = item.quizlist!.map(items => items.id).join(','); // let workIds = item.quizlist!.map(items => items.id).join(',');
// let ress = await listEntpcoursework({ ids: workIds }); // let ress = await listEntpcoursework({ ids: workIds });
// const arr = ress.rows.map((item:{id:number}) => { // const arr = ress.rows.map((item:{id:number}) => {
@ -214,22 +211,19 @@ const formatClassWorkFile = async (postData: WorkItem[]): Promise<void> => {
// item.prevData = JSON.parse(item.workcodes); // item.prevData = JSON.parse(item.workcodes);
} }
} }
const arr = paramData.value.activityContent.split(',') workList.value.push(item)
arr.push(item.id.toString()) loadingActive.value = false
await PPTApi.updateSlide(paramData.value)
addWorkList(item)
} }
await nextTick(); await nextTick();
} }//
//
const addWorkList = (item: WorkItem) => {
workList.value.push(item)
}
//
const handleRemoveDemoActivityClassWork = (item: WorkItem) => { const handleRemoveDemoActivityClassWork = (item: WorkItem) => {
ElMessageBox.confirm('是否确认删除?') ElMessageBox.confirm('是否确认删除?')
.then(() => { .then(() => {
workList.value.splice(workList.value.indexOf(item), 1); workList.value = []
const arr = paramData.value.activityContent.split(',')
const filterArr = arr.filter(itemId => itemId!== item.id.toString())
paramData.value.activityContent = filterArr.join(',')
upDateData()
}) })
.catch(() => { }); .catch(() => { });
} }
@ -269,13 +263,51 @@ const savePPtData = async () => {
ElMessage.warning('请选择活动') ElMessage.warning('请选择活动')
return return
} }
workList.value = []
const arr = selectedWorkList.value.map(item => item.id) const arr = selectedWorkList.value.map(item => item.id)
// //
paramData.value.activityContent = arr.join(',') const existingIds = paramData.value.activityContent ? paramData.value.activityContent.split(',') : []
await PPTApi.updateSlide(paramData.value) paramData.value.activityContent = Array.from(new Set([...existingIds, ...arr])).join(',')
upDateData()
activeVisible.value = false activeVisible.value = false
} }
// ppt
const getCurrentPPtData = async () => {
workList.value = []
objItem.value = workItem.value[slideIndex.value]
paramData.value.id = objItem.value.id
paramData.value.activityContent = objItem.value?.activityContent
if (objItem.value?.activityContent) {
loadingActive.value = true
const res = await homeworklist({ ids: objItem.value?.activityContent, pageSize: 100 })
await formatClassWorkFile(res.rows)
}
}
//
const getData = async (data: WorkItem) => {
workList.value = []
if(paramData.value.activityContent){
const arr = paramData.value.activityContent.split(',')
arr.push(data.id.toString())
const unitArr = Array.from(new Set(arr))
paramData.value.activityContent = unitArr.join(',')
}else{
paramData.value.activityContent = data.id.toString()
}
upDateData()
dialogVisible.value = false
}
const upDateData = async () => {
await PPTApi.updateSlide(paramData.value)
loadingActive.value = true
const res = await homeworklist({ ids: paramData.value.activityContent, pageSize: 100 })
await formatClassWorkFile(res.rows)
const resource = sessionStore.get('curr.resource')
await PPTApi.getSlideList(resource.id)
}
onMounted(() => { onMounted(() => {
const curNode = sessionStore.get('subject.curNode') as CourseNode const curNode = sessionStore.get('subject.curNode') as CourseNode
currentCourse.textbookId = curNode.rootid currentCourse.textbookId = curNode.rootid
@ -283,33 +315,12 @@ onMounted(() => {
currentCourse.levelSecondId = curNode.id currentCourse.levelSecondId = curNode.id
currentCourse.coursetitle = curNode.itemtitle currentCourse.coursetitle = curNode.itemtitle
currentCourse.node = curNode currentCourse.node = curNode
listEntpcoursefile(params).then((res: { rows: WorkItem[] }) => {
dataList.value = [...res.rows]
})
objItem.value = workItem.value[slideIndex.value] objItem.value = workItem.value[slideIndex.value]
getCurrentPPtData() getCurrentPPtData()
}) })
watch(() => slideIndex.value, () => { watch(() => slideIndex.value, () => {
getCurrentPPtData() getCurrentPPtData()
}) })
// ppt
const getCurrentPPtData = async () => {
workList.value = []
objItem.value = workItem.value[slideIndex.value]
paramData.value.id = objItem.value.id
if (objItem.value?.activityContent) {
paramData.value.activityContent = objItem.value?.activityContent
const res = await homeworklist({ ids: objItem.value?.activityContent, pageSize: 100 })
await formatClassWorkFile(res.rows)
}
}
//
const getData = async (data: WorkItem) => {
console.log(data, 'data')
await formatClassWorkFile([data])
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.buttonDiv{ .buttonDiv{
@ -375,5 +386,8 @@ const getData = async (data: WorkItem) => {
--el-border-color: var(--current-color); --el-border-color: var(--current-color);
} }
} }
.page .page-resource{
height: 500px !important;
}
} }
</style> </style>

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4723712 */ font-family: "iconfont"; /* Project id 4723712 */
src: url('iconfont.woff2?t=1732240267757') format('woff2'), src: url('iconfont.woff2?t=1733880548695') format('woff2'),
url('iconfont.woff?t=1732240267757') format('woff'), url('iconfont.woff?t=1733880548695') format('woff'),
url('iconfont.ttf?t=1732240267757') format('truetype'); url('iconfont.ttf?t=1733880548695') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,26 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-yuyin:before {
content: "\e648";
}
.icon-dianying:before {
content: "\e693";
}
.icon-jiqirenfushi:before {
content: "\e624";
}
.icon-xiangmuicon_maobishufa:before {
content: "\e651";
}
.icon-meishu-F:before {
content: "\e638";
}
.icon-shangchuan:before { .icon-shangchuan:before {
content: "\e61b"; content: "\e61b";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,41 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "6338162",
"name": "语音生成",
"font_class": "yuyin",
"unicode": "e648",
"unicode_decimal": 58952
},
{
"icon_id": "6880941",
"name": "视频生成",
"font_class": "dianying",
"unicode": "e693",
"unicode_decimal": 59027
},
{
"icon_id": "11532042",
"name": "数字人生成",
"font_class": "jiqirenfushi",
"unicode": "e624",
"unicode_decimal": 58916
},
{
"icon_id": "13522843",
"name": "文生图片",
"font_class": "xiangmuicon_maobishufa",
"unicode": "e651",
"unicode_decimal": 58961
},
{
"icon_id": "37635062",
"name": "文生连环画",
"font_class": "meishu-F",
"unicode": "e638",
"unicode_decimal": 58936
},
{ {
"icon_id": "4942656", "icon_id": "4942656",
"name": "上传", "name": "上传",

View File

@ -36,6 +36,11 @@ const getFileTypeIcon = () => {
rar: 'icon-rar', rar: 'icon-rar',
apt: 'icon-A', apt: 'icon-A',
aippt: 'icon-A', aippt: 'icon-A',
aiyuyin: 'icon-yuyin', //
aivideo: 'icon-dianying', //
airobot: 'icon-jiqirenfushi', //
aiimg: 'icon-xiangmuicon_maobishufa', //
aidraw: 'icon-meishu-F', //
} }
if (iconObj[name]) { if (iconObj[name]) {
return '#' + iconObj[name] return '#' + iconObj[name]

View File

@ -29,6 +29,20 @@
</template> </template>
</div> </div>
</el-scrollbar> </el-scrollbar>
<div class="file-list">
<el-dropdown @command="changeFile">
<span class="el-dropdown-link">
{{ curFile.fileName }}
<i class="iconfont icon-xiangxia"></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in fileList" :command="item" :key="item.id">{{ item.fileName
}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="input-box flex"> <div class="input-box flex">
<el-input v-model="textarea" @keyup.enter="send" :disabled="loaded"/> <el-input v-model="textarea" @keyup.enter="send" :disabled="loaded"/>
<div class="ipt-icon" @click="send"> <div class="ipt-icon" @click="send">
@ -40,13 +54,15 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, watch } from 'vue' import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
import { completion } from '@/api/mode/index' import { completion, docList } from '@/api/mode/index'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import { ElMessage } from 'element-plus'
import { dataSetJson } from '@/utils/comm.js' import { dataSetJson } from '@/utils/comm.js'
import useUserStore from '@/store/modules/user'
import emitter from '@/utils/mitt'; import emitter from '@/utils/mitt';
const userInfo = useUserStore().user
const textarea = ref('') const textarea = ref('')
const isDialog = defineModel() const isDialog = defineModel()
@ -106,7 +122,6 @@ const getCompletion = async (val) => {
const saveAdjust = (item) =>{ const saveAdjust = (item) =>{
isDialog.value = false isDialog.value = false
ElMessage.success('操作成功')
emitter.emit('onSaveAdjust', item.msg) emitter.emit('onSaveAdjust', item.msg)
} }
@ -124,6 +139,27 @@ watch(() => props.type, (newVal) => {
}, { immediate: false }) }, { immediate: false })
const curFile = reactive({})
const dataset_id = ref('')
const fileList = ref([])
const getList = () =>{
docList({
userId: userInfo.userId,
dataset_id: dataset_id.value
}).then( res =>{
fileList.value = res.rows
Object.assign(curFile, fileList.value[0])
params.document_ids = fileList.value[0].docId
})
}
emitter.on('changeCurFile', (item) =>{
changeFile(item)
})
const changeFile = (val) =>{
Object.assign(curFile, val);
params.document_ids = val.docId
}
onMounted(() => { onMounted(() => {
let data = sessionStore.get('subject.curNode') let data = sessionStore.get('subject.curNode')
@ -131,6 +167,15 @@ onMounted(() => {
Object.assign(curNode, data); Object.assign(curNode, data);
let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}` let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey] params.dataset_id = dataSetJson[jsonKey]
if(props.type == 3){
getList()
}
})
//
onUnmounted(() => {
emitter.off('changeCurFile');
}) })
@ -267,4 +312,9 @@ onMounted(() => {
transform: scale(0.01); transform: scale(0.01);
} }
} }
.file-list{
display: flex;
margin-bottom: 10px;
}
</style> </style>

View File

@ -2,7 +2,7 @@
<el-dialog v-model="mode" :show-close="false" width="600" append-to-body destroy-on-close> <el-dialog v-model="mode" :show-close="false" width="600" append-to-body destroy-on-close>
<template #header> <template #header>
<div class="custom-header flex"> <div class="custom-header flex">
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span> <span>{{ item.ex3 == '1' ? '请输入新的模板名称' : item.isAdd ? '添加提示词' : '编辑提示词' }}</span>
<i class="iconfont icon-guanbi" @click="mode = false"></i> <i class="iconfont icon-guanbi" @click="mode = false"></i>
</div> </div>
</template> </template>
@ -40,10 +40,6 @@ const props = defineProps({
type: Number, type: Number,
default: 1 default: 1
}, },
isAdd: {
type: Boolean,
default: true
},
item: { // item: { //
type: Object, type: Object,
default: () => { default: () => {
@ -58,21 +54,27 @@ const form = reactive({
prompt: '', prompt: '',
}) })
watch(() => props.isAdd, (newVal) => {
if (!newVal) { watch(() => mode.value, (newVal) => {
if(newVal){
if (props.item.isAdd) {
form.name = ''
form.prompt = ''
}
else{
form.name = props.item?.name form.name = props.item?.name
form.prompt = props.item?.prompt form.prompt = props.item?.prompt
} }
}
}, { immediate: false }) },{ deep: true})
const loading = ref(false) const loading = ref(false)
const saveAdd = async () => { const saveAdd = async () => {
loading.value = true loading.value = true
if (props.item.ex3 == '1') { if (props.item.ex3 == '1') {
if (props.isAdd) { if (props.item.isAdd) {
try { try {
// copy // copy
const { msg } = await addKeyWords({ name: form.name, id: props.item.id }) const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
@ -88,7 +90,7 @@ const saveAdd = async () => {
} }
} else { } else {
if (props.isAdd) { if (props.item.isAdd) {
onAddChildTemp(props.item.id) onAddChildTemp(props.item.id)
} }
else { else {

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog v-model="isDialog" :show-close="false" width="900" destroy-on-close> <el-dialog v-model="isDialog" :show-close="false" width="900" append-to-body destroy-on-close>
<template #header> <template #header>
<div class="custom-header flex"> <div class="custom-header flex">
<span>选择{{ title }}</span> <span>选择{{ title }}</span>
@ -7,37 +7,79 @@
</div> </div>
</template> </template>
<div class="dialog-content"> <div class="dialog-content">
<div class="flex">
<el-radio-group v-model="radio" @change="changeRadio">
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
</el-radio-group>
</div>
<div class="content-list"> <div class="content-list">
<ul> <ul>
<li v-for="(item, index) in list" :class="activeIndex == index ? 'li-active' : ''" @click="clickItem(index)"> <li v-for="(item, index) in fileList" :class="activeIndex == index ? 'li-active' : ''"
<el-image class="img" :src="item.url" /> @click="clickItem(index, item)">
<span>{{ item.name }}</span> <el-image class="img" :src="url" />
<el-button type="primary" class="prev-btn" @click.stop="onPrevItem(item)">预览</el-button>
<el-text truncated>{{ item.fileName }}</el-text>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-upload class="upload-demo" :action="uploadFileUrl" :limit="1" :show-file-list="false" :headers="headers"
:on-success="onSuccess">
<el-button type="primary">上传</el-button>
</el-upload>
<div>
<el-button @click="isDialog = false">取消</el-button> <el-button @click="isDialog = false">取消</el-button>
<el-button type="primary" @click="isDialog = false"> <el-button type="primary" @click="isDialog = false">
确定 确定
</el-button> </el-button>
</div> </div>
</div>
</template>
</el-dialog>
<el-dialog v-model="prevVisible" fullscreen :show-close="false" class="prev-dialog">
<template #header>
<div class="custom-header flex">
<span>预览</span>
<i class="iconfont icon-guanbi" @click="prevVisible = false"></i>
</div>
</template>
<div style="height: calc(100vh - 120px);">
<template v-if="getFileSuffix(prevItem.fileUrl) == 'pdf'">
<iframe :src="prevItem.fileUrl"
frameborder="0" width="100%" height="100%"></iframe>
</template>
<template v-else>
<el-image :src="prevItem.fileUrl" style="height:100%"/>
</template>
</div>
<template #footer>
<div class="dialog-footer">
<div></div>
<el-button type="primary" @click="prevVisible = false">
关闭
</el-button>
</div>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed, onMounted, reactive } from 'vue'
import { completion, addDoc, docList } from '@/api/mode/index.js'
import { getToken } from "@/utils/auth";
import { sessionStore } from '@/utils/store'
import { dataSetJson } from '@/utils/comm.js'
import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user'
import { getFileSuffix } from '@/utils/ruoyi.js'
import emitter from '@/utils/mitt';
const userInfo = useUserStore().user
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
const headers = ref({ Authorization: "Bearer " + getToken() });
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35' const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35'
const isDialog = defineModel() const isDialog = defineModel()
const prevVisible = ref(false)
const props = defineProps({ const props = defineProps({
modeType: { modeType: {
@ -52,14 +94,13 @@ const title = computed(() => {
if (props.modeType == 3) return '考试'; if (props.modeType == 3) return '考试';
}) })
const radio = ref(1) const radio = ref(1)
const radioList = ref([ const radioList = ref([
{ label: '浏览研读', value: 1 }, { label: '浏览研读', value: 1 },
// { label: '', value: 2 }, { label: '跨学科研读', value: 2 },
// { label: '', value: 3 }, { label: '跨学段研读', value: 3 },
// { label: '', value: 4 }, { label: '课标修订研读', value: 4 },
// { label: '', value: 5 }, { label: '自由研读', value: 5 },
]) ])
const list = ref([ const list = ref([
{ {
@ -76,15 +117,70 @@ const changeRadio = () => {
}) })
} }
} }
const activeIndex = ref(0)
const activeIndex = ref(-1) const dataset_id = ref('')
const clickItem = (index) => { //
activeIndex.value = index const onSuccess = async (response) => {
let data = {
url: response.url,
dataset_id: dataset_id.value
}
const res = await completion(data)
if (res.data.code != 200) return
let docData = {
fileUrl: response.url,
fileId: response.file.id,
fileName: response.file.fileName,
filesize: response.file.fileSize,
datasetId: dataset_id.value,
docId: res.data.document_id,
edustage: curNode.edustage,
edusubject: curNode.edusubject
}
const { msg } = await addDoc(docData)
ElMessage.success(msg)
getList()
}
const curNode = reactive({})
const fileList = ref([])
const curFile = reactive({})
const getList = () => {
docList({
userId: userInfo.userId,
dataset_id: dataset_id.value
}).then(res => {
fileList.value = [...res.rows]
Object.assign(curFile, fileList.value[0])
})
} }
</script> const clickItem = (index, item) => {
activeIndex.value = index
Object.assign(curFile, item)
emitter.emit('changeCurFile', item)
}
const prevItem = reactive({})
const onPrevItem = (item) => {
Object.assign(prevItem, item)
prevVisible.value = true
}
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
// "-"
let jsonKey = `考试-${curNode.edustage}-${curNode.edusubject}`
dataset_id.value = dataSetJson[jsonKey]
getList()
})
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.custom-header { .custom-header {
justify-content: space-between; justify-content: space-between;
@ -98,13 +194,16 @@ const clickItem = (index) => {
.dialog-content { .dialog-content {
padding-top: 10px; padding-top: 10px;
.content-list { .content-list {
padding-top: 10px; padding-top: 10px;
ul { ul {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
li { li {
width: 130px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: 13px; font-size: 13px;
@ -114,9 +213,11 @@ const clickItem = (index) => {
overflow: hidden; overflow: hidden;
margin-right: 20px; margin-right: 20px;
margin-bottom: 10px; margin-bottom: 10px;
position: relative;
overflow: hidden;
.img { .img {
width: 100px; width: 100%;
height: 130px; height: 130px;
border: solid #ccc 1px; border: solid #ccc 1px;
margin-bottom: 10px; margin-bottom: 10px;
@ -125,6 +226,10 @@ const clickItem = (index) => {
&:hover { &:hover {
background: #E0EAFF; background: #E0EAFF;
} }
&:hover .prev-btn {
transform: translate(-50%, -40px)
}
} }
.li-active { .li-active {
@ -134,4 +239,20 @@ const clickItem = (index) => {
} }
} }
} }
.dialog-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.prev-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateY(-110px);
/* 按钮初始位置在容器外 */
transition: transform 0.3s ease-in-out;
/* 设置过渡效果 */
}
</style> </style>

View File

@ -2,12 +2,13 @@
<div class="container-left-page flex"> <div class="container-left-page flex">
<div class="container-left-header flex"> <div class="container-left-header flex">
<el-button link @click="onClick"> <el-button link @click="onClick">
{{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : '教材分析' }}<i {{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : type == 2 ? '教材分析' : '考试分析' }}<i
class="iconfont icon-xiangxia"></i> class="iconfont icon-xiangxia"></i>
</el-button> </el-button>
</div> </div>
<div class="container-left-pdf"> <div class="container-left-pdf">
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" /> <PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
<el-empty v-else description="暂无数据" />
</div> </div>
<!--弹窗--> <!--弹窗-->
<LeftDialog v-model="showDialog" :modeType="type" /> <LeftDialog v-model="showDialog" :modeType="type" />
@ -24,7 +25,7 @@ const props = defineProps(['curNode', 'type'])
const showDialog = ref(false) const showDialog = ref(false)
const onClick = () => { const onClick = () => {
if (props.type == 1) return if (props.type != 3) return
showDialog.value = true showDialog.value = true
} }
@ -37,7 +38,9 @@ onMounted(async () => {
if(props.type == 1){ if(props.type == 1){
fileurl = `${data.edustage}-${data.edusubject}-课标.txt` fileurl = `${data.edustage}-${data.edusubject}-课标.txt`
} }
if(fileurl == '') return
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf') pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
}) })
</script> </script>

View File

@ -39,7 +39,7 @@
<i class="iconfont icon-shenglvehao"></i></el-button> <i class="iconfont icon-shenglvehao"></i></el-button>
</template> </template>
<template #default> <template #default>
<el-button type="primary" link @click="editKeyWord(item)">编辑</el-button> <el-button type="primary" link @click="editKeyWord(item, false)">编辑</el-button>
<el-button type="primary" link @click="removeItem(item, true)">移除</el-button> <el-button type="primary" link @click="removeItem(item, true)">移除</el-button>
</template> </template>
</el-popover> </el-popover>
@ -79,7 +79,7 @@
<!--AI 对话调整--> <!--AI 对话调整-->
<AdjustDialog v-model="isAdjust" :type="type" :item="editItem" /> <AdjustDialog v-model="isAdjust" :type="type" :item="editItem" />
<!--添加编辑提示词--> <!--添加编辑提示词-->
<keywordDialog v-model="isWordDialog" :isAdd="isAdd" :item="editItem" /> <keywordDialog v-model="isWordDialog" :item="editItem" />
</template> </template>
<script setup> <script setup>
@ -105,21 +105,22 @@ const props = defineProps(['curNode', 'type'])
*/ */
const isWordDialog = ref(false) const isWordDialog = ref(false)
const isAdd = ref(false)
const editItem = reactive({}) const editItem = reactive({})
const onAdd = () => { const onAdd = () => {
isAdd.value = true
Object.assign(editItem, curTemplate) Object.assign(editItem, curTemplate)
editItem.isAdd = true
isWordDialog.value = true isWordDialog.value = true
} }
const editKeyWord = (item, val) => { const editKeyWord = (item, val) => {
/** /**
* isAdd: 字模板中的移除 为编辑 头部删除 添加提示词为新增 * isAdd: 子模板中的移除 为编辑false 头部删除 添加提示词为新增 true
*/ */
isAdd.value = val
Object.assign(editItem, item) Object.assign(editItem, item)
editItem.isAdd = val
isWordDialog.value = true isWordDialog.value = true
} }
/*******************模板相关**********************/ /*******************模板相关**********************/
@ -361,8 +362,8 @@ emitter.on('onSaveAdjust', (item) => {
// //
const onEditSave = async (item) => { const onEditSave = async (item) => {
const { res } = await editTempResult({ id: item.resultId, content: item.answer }) const { msg } = await editTempResult({ id: item.resultId, content: item.answer })
ElMessage.success(res) ElMessage.success(msg)
getChildTemplate() getChildTemplate()
} }

View File

@ -8,7 +8,7 @@
readonly readonly
resize="none" resize="none"
style="width: 100%;" style="width: 100%;"
input-style="border:none;outline: none;box-shadow:none;color:000;fontSize:15px" input-style="border:none;outline: none;box-shadow:none;color:000;fontSize:14px"
/> />
</div> </div>
</template> </template>
@ -71,9 +71,6 @@ watch([() => props.text, () => props.delay], resetAndType);
</script> </script>
<style scoped> <style scoped>
.typing-effect {
font-family: monospace;
}
:deep(.el-textarea__inner:hover){ :deep(.el-textarea__inner:hover){
box-shadow: none; box-shadow: none;
} }

View File

@ -157,6 +157,7 @@ const props = defineProps({
currentCourse: Object, currentCourse: Object,
}) })
const emits = defineEmits(['getData']) const emits = defineEmits(['getData'])
// ppt
const isShow = ref(false) const isShow = ref(false)
const propsQueryCourseObj = route.query.courseObj;// const propsQueryCourseObj = route.query.courseObj;//
@ -490,11 +491,13 @@ const handleClassWorkFormQuizRemove = (index) =>{
// [] newWorkSpaceEdit true // [] newWorkSpaceEdit true
if(isShow.value === false){
if(classWorkForm.id != '' ) {// id if(classWorkForm.id != '' ) {// id
editWork(cform); // editWork(cform); //
return; return;
} }
}
if (classWorkForm.worktype === "课堂展示") { if (classWorkForm.worktype === "课堂展示") {
boardLoading.value = true boardLoading.value = true
@ -596,7 +599,11 @@ const handleClassWorkFormQuizRemove = (index) =>{
} }
console.log('该清空左侧列表数据了'); console.log('该清空左侧列表数据了');
// //
if(isShow.value){
currentRow.value = {id:1};
}else{
currentRow.value = {id:0}; currentRow.value = {id:0};
}
initHomeWork(); initHomeWork();

View File

@ -1,306 +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="file-list">
<el-dropdown @command="changeFile">
<span class="el-dropdown-link">
{{ curFile.fileName }}
<i class="iconfont icon-xiangxia"></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in fileList" :command="item" :key="item.id">{{ item.fileName
}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<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 { conversation, completion, docList } from '@/api/mode/index'
import { sessionStore } from '@/utils/store'
import { ElMessage } from 'element-plus'
import { dataSetJson } from '@/utils/comm.js'
import useUserStore from '@/store/modules/user'
import emitter from '@/utils/mitt';
const userInfo = useUserStore().user
const textarea = ref('')
const isDialog = defineModel()
const props = defineProps({
item: {
type: Object,
default: () => {
return { name: '11' }
}
},
modeType: {
type: Number,
default: 1
}
})
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: '',
document_ids: '',
}
)
// ID
const getConversation = (val) => {
getCompletion(val)
}
//
const getCompletion = async (val) => {
try {
params.prompt = `按照${val}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
const { data } = await completion(params)
let answer = data.answer
msgList.value.push({
type: 'robot',
msg: answer,
})
} finally {
loaded.value = false
}
}
const saveAdjust = (item) =>{
emit('saveAdjust', item.msg)
isDialog.value = false
ElMessage.success('操作成功')
}
const curFile = reactive({})
const dataset_id = ref('')
const fileList = ref([])
const getList = () =>{
docList({
userId: userInfo.userId,
dataset_id: dataset_id.value
}).then( res =>{
fileList.value = res.rows
Object.assign(curFile, fileList.value[0])
params.document_ids = fileList.value[0].docId
})
}
emitter.on('curFile', (item) =>{
changeFile(item)
})
const changeFile = (val) =>{
Object.assign(curFile, val);
params.document_ids = val.docId
}
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
let text = props.modeType == 1 ||props.modeType == 2 ? '课标' : '考试'
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey]
dataset_id.value = dataSetJson[jsonKey]
getList()
})
</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);
}
}
.file-list{
display: flex;
margin-bottom: 10px;
}
</style>

View File

@ -1,268 +0,0 @@
<template>
<el-dialog v-model="isDialog" :show-close="false" width="900" append-to-body destroy-on-close>
<template #header>
<div class="custom-header flex">
<span>选择{{ title }}</span>
<i class="iconfont icon-guanbi" @click="isDialog = false"></i>
</div>
</template>
<div class="dialog-content">
<div class="flex dialog-top">
<!-- <el-radio-group v-model="radio" @change="changeRadio">
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
</el-radio-group> -->
</div>
<div class="content-list">
<ul>
<li v-for="(item, index) in fileList" :class="activeIndex == index ? 'li-active' : ''"
@click="clickItem(index, item)">
<el-image class="img" :src="url" />
<el-button type="primary" class="prev-btn" @click.stop="onPrevItem(item)">预览</el-button>
<el-text truncated>{{ item.fileName }}</el-text>
</li>
</ul>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-upload class="upload-demo" :action="uploadFileUrl" :limit="1" :show-file-list="false" :headers="headers"
:on-success="onSuccess">
<el-button type="primary">上传</el-button>
</el-upload>
<div>
<el-button @click="isDialog = false">取消</el-button>
<el-button type="primary" @click="isDialog = false">
确定
</el-button>
</div>
</div>
</template>
</el-dialog>
<el-dialog v-model="prevVisible" fullscreen :show-close="false" class="prev-dialog">
<template #header>
<div class="custom-header flex">
<span>预览</span>
<i class="iconfont icon-guanbi" @click="prevVisible = false"></i>
</div>
</template>
<div style="height: calc(100vh - 120px);">
<template v-if="getFileSuffix(prevItem.fileUrl) == 'pdf'">
<iframe :src="prevItem.fileUrl"
frameborder="0" width="100%" height="100%"></iframe>
</template>
<template v-else>
<el-image :src="prevItem.fileUrl" style="height:100%"/>
</template>
</div>
<template #footer>
<div class="dialog-footer">
<div></div>
<el-button type="primary" @click="prevVisible = false">
关闭
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, computed, onMounted, reactive } from 'vue'
import { completion, addDoc, docList } from '@/api/mode/index.js'
import { getToken } from "@/utils/auth";
import { sessionStore } from '@/utils/store'
import { dataSetJson } from '@/utils/comm.js'
import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user'
import { getFileSuffix } from '@/utils/ruoyi.js'
import emitter from '@/utils/mitt';
const userInfo = useUserStore().user
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
const headers = ref({ Authorization: "Bearer " + getToken() });
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35'
const isDialog = defineModel()
const prevVisible = ref(false)
const props = defineProps({
modeType: {
type: Number,
default: 1
}
})
const title = computed(() => {
if (props.modeType == 1) return '课标';
if (props.modeType == 2) return '教材';
if (props.modeType == 3) return '考试';
})
const radio = ref(1)
const radioList = ref([
{ label: '浏览研读', value: 1 },
{ label: '跨学科研读', value: 2 },
{ label: '跨学段研读', value: 3 },
{ label: '课标修订研读', value: 4 },
{ label: '自由研读', value: 5 },
])
const list = ref([
{
name: '高中语文课程标准',
url
}
])
const changeRadio = () => {
list.value = []
for (let i = 0; i < Math.floor(Math.random() * 5) + 1; i++) {
list.value.push({
name: '高中语文课程标准',
url
})
}
}
const activeIndex = ref(0)
const dataset_id = ref('')
//
const onSuccess = async (response) => {
let data = {
url: response.url,
dataset_id: dataset_id.value
}
const res = await completion(data)
if (res.data.code != 200) return
let docData = {
fileUrl: response.url,
fileId: response.file.id,
fileName: response.file.fileName,
filesize: response.file.fileSize,
datasetId: dataset_id.value,
docId: res.data.document_id,
edustage: curNode.edustage,
edusubject: curNode.edusubject
}
const { msg } = await addDoc(docData)
ElMessage.success(msg)
getList()
}
const curNode = reactive({})
const fileList = ref([])
const curFile = reactive({})
const getList = () => {
docList({
userId: userInfo.userId,
dataset_id: dataset_id.value
}).then(res => {
fileList.value = [...res.rows]
Object.assign(curFile, fileList.value[0])
})
}
const clickItem = (index, item) => {
activeIndex.value = index
Object.assign(curFile, item)
emitter.emit('curFile', item)
}
const prevItem = reactive({})
const onPrevItem = (item) => {
Object.assign(prevItem, item)
prevVisible.value = true
}
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
let jsonKey = `考试-${curNode.edustage}-${curNode.edusubject}`
dataset_id.value = dataSetJson[jsonKey]
getList()
})
</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;
.dialog-top {
justify-content: space-between;
}
.content-list {
padding-top: 10px;
ul {
display: flex;
flex-wrap: wrap;
li {
width: 130px;
display: flex;
flex-direction: column;
font-size: 13px;
padding: 10px;
cursor: pointer;
border-radius: 5px;
overflow: hidden;
margin-right: 20px;
margin-bottom: 10px;
position: relative;
overflow: hidden;
.img {
width: 100%;
height: 130px;
border: solid #ccc 1px;
margin-bottom: 10px;
}
&:hover {
background: #E0EAFF;
}
&:hover .prev-btn {
transform: translate(-50%, -50%)
}
}
.li-active {
background: #E0EAFF;
}
}
}
}
.dialog-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.prev-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateY(-110px);
/* 按钮初始位置在容器外 */
transition: transform 0.3s ease-in-out;
/* 设置过渡效果 */
}
</style>

View File

@ -1,78 +0,0 @@
<template>
<el-dialog v-model="isDialog" :show-close="false" width="900">
<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-row>
<el-col :span="24">
<el-input v-model="textarea" :autosize="{ minRows: 5, maxRows: 15 }" type="textarea" />
</el-col>
</el-row>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="isDialog = false">取消</el-button>
<el-button type="primary" @click="onSave">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch} from 'vue'
import { ElMessage } from 'element-plus'
import { editTempResult } from '@/api/mode/index.js'
const textarea = ref('')
const isDialog = defineModel()
const props = defineProps({
item: {
type: Object,
default: () => {
return { name: '11' }
}
}
})
watch(() => props.item.answer, (newVal) => {
if (newVal) {
textarea.value = newVal
}
},{ deep: true })
const emit = defineEmits(['saveEdit'])
const onSave = () =>{
editTempResult({id: props.item.resultId, content: textarea.value}).then( res =>{
isDialog.value = false
ElMessage.success('操作成功')
emit('saveEdit', textarea.value)
})
}
</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;
}
</style>

View File

@ -1,143 +0,0 @@
<template>
<div class="container-header flex">
<div class="header-left flex">
<el-button link @click="showDialog = true">
{{ curNode.edustage}}{{ curNode.edusubject }}考试分析<i class="iconfont icon-xiangxia"></i>
</el-button>
</div>
<div class="header-right flex">
<el-dropdown @command="changeTemplate" :hide-on-click="false">
<span class="el-dropdown-link">
{{ curTemplate.name }}
<i class="iconfont icon-xiangxia"></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.name
}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div>
<el-button type="primary" link @click="onAdd">
<el-icon>
<Plus />
</el-icon>
添加提示词
</el-button>
<el-button type="primary" link>保存模板</el-button>
<el-button type="primary" @click="aiRead">一键研读</el-button>
</div>
</div>
</div>
<!--添加提示词-->
<keywordDialog v-model="wordDialog" :modeType="type" :isAdd="wordDialog" :item="curTemplate" />
<Dialog v-model="showDialog" :modeType="type" />
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
import { Plus } from '@element-plus/icons-vue'
import { ElMessageBox } from 'element-plus'
import { modelList } from '@/api/mode/index'
import Dialog from './dialog.vue'
import keywordDialog from './keyword-dialog.vue'
import emitter from '@/utils/mitt';
import { sessionStore } from '@/utils/store'
const wordDialog = ref(false)
const props = defineProps({
type: {
type: Number,
default: 1
}
})
const emit = defineEmits(['changeTemp', 'onRead'])
const showDialog = ref(false)
const aiRead = () => {
emit('onRead')
}
//
const curTemplate = reactive({ name: '', id: '' })
//
const templateList = ref([])
//
const getTemplateList = () => {
modelList({ model: props.type, type: 1, pageNum: 1, pageSize: 10000 }).then(res => {
templateList.value = res.rows
Object.assign(curTemplate, res.rows[0]);
emit('changeTemp', res.rows[0].id)
})
}
//
const changeTemplate = (val) => {
ElMessageBox.confirm(
'切换模板将清除当前研读结果?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
Object.assign(curTemplate, val);
emit('changeTemp', curTemplate.id)
})
}
emitter.on('onGetMain', () =>{
getTemplateList()
})
const onAdd = () => {
wordDialog.value = true
}
onUnmounted(() => {
emitter.off('onGetMain')
})
const curNode = reactive({})
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
getTemplateList()
})
</script>
<style lang="scss" scoped>
.container-header {
height: 45px;
background: #fff;
border-radius: 5px 5px 0 0;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
.header-left {
width: 50%;
align-items: center;
padding-left: 20px;
}
.header-right {
width: 50%;
justify-content: space-between;
align-items: center;
padding: 0 10px;
}
.icon-xiangxia {
margin-left: 5px;
font-weight: bold;
}
}
</style>

View File

@ -1,161 +0,0 @@
<template>
<el-dialog v-model="mode" :show-close="false" width="600" destroy-on-close>
<template #header>
<div class="custom-header flex">
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : 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
},
isAdd: {
type: Boolean,
default: true
},
item: { //
type: Object,
default: () => {
return { ex3: '' }
}
},
tempId: {
type: [String, Number],
default: ''
},
})
const form = reactive({
name: '',
prompt: '',
})
watch(() => props.isAdd, (newVal) => {
if (!newVal) {
form.name = props.item?.name
form.prompt = props.item?.prompt
}
}, { immediate: false })
const loading = ref(false)
const saveAdd = async () => {
loading.value = true
if (props.item.ex3 == '1') {
if (props.isAdd) {
try {
// copy
const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
emitter.emit('onGetChild')
ElMessage.success(msg)
mode.value = false
} finally {
loading.value = false
}
}
else{
onAddChildTemp(props.item.parentId)
}
} else {
if (props.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')
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>

View File

@ -1,32 +0,0 @@
<template>
<div class="container-pdf">
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
import PDF from '@/components/PdfJs/index.vue'
import { sessionStore } from '@/utils/store'
const pdfUrl = ref('')
onMounted(async () =>{
await nextTick()
let data = sessionStore.get('subject.curBook')
let fileurl = data.fileurl
if(fileurl == ''){
fileurl = `${data.edustage}-${data.edusubject}-课标.txt`
}
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
})
</script>
<style lang="scss" scoped>
.container-pdf{
height: 100%;
}
</style>

View File

@ -1,513 +0,0 @@
<template>
<div class="read-container">
<el-scrollbar height="100%">
<div class="template-list">
<el-row v-for="(item, index) in childTempList">
<el-col :span="24">
<div class="template-item" v-loading="item.loading">
<div class="item-header">
<div>
<span class="blue">#</span>{{ item.name }}
</div>
<el-popover placement="bottom-end" trigger="hover" popper-class="template-custom-popover">
<template #reference>
<el-button link type="primary">
<i class="iconfont icon-shenglvehao"></i></el-button>
</template>
<template #default>
<el-button type="primary" link @click="editKeyWord(item)">编辑</el-button>
<el-button type="primary" link @click="removeItem(item)">移除</el-button>
</template>
</el-popover>
</div>
<div class="item-text" >
{{ item.prompt }}
</div>
<div class="item-text text-answer" v-if="item.answer">
<div class="item-icon">
<i class="iconfont icon-ai"></i>
</div>
<div class="item-answer" v-html="item.answer"></div>
</div>
<div class="ai-btn" v-if="item.answer">
<el-button type="primary" link @click="againResult(index, item)">
<i class="iconfont icon-ai1"></i>
重新研读
</el-button>
<el-button type="primary" link @click="onAdjust(index, item)">
<i class="iconfont icon-duihua"></i>
AI对话调整
</el-button>
<el-button type="primary" link @click="onEdit(index, item)">
<i class="iconfont icon-bianji1"></i>
手动编辑结果
</el-button>
</div>
</div>
</el-col>
</el-row>
<el-empty v-if="!childTempList.length" description="暂无模板数据" />
</div>
</el-scrollbar>
<!--编辑结果-->
<EditDialog v-model="isEdit" :item="editItem" @saveEdit="saveEdit" />
<!--AI 对话调整-->
<AdjustDialog v-model="isAdjust" :modeType="modeType" :item="editItem" @saveAdjust="saveAdjust" />
<!--编辑提示词-->
<keywordDialog v-model="isEditKeyWord" :isAdd="isAdd" :item="keywordItem" :tempId="tempId" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import EditDialog from './edit-dialog.vue'
import AdjustDialog from './adjust-dialog.vue'
import keywordDialog from './keyword-dialog.vue';
import { sessionStore } from '@/utils/store'
import useUserStore from '@/store/modules/user'
import { tempSave, completion, modelList, removeChildTemp, tempResult, editTempResult } from '@/api/mode/index'
import { dataSetJson } from '@/utils/comm.js'
import emitter from '@/utils/mitt';
const userStore = useUserStore()
const props = defineProps({
tempId: {
type: [String, Number],
default: ''
},
modeType: {
type: Number,
default: 1
}
})
emitter.on('onGetChild', () =>{
getChildTemplate()
})
//
const tempLoading = ref(false)
const childTempList = ref([])
const getChildTemplate = () => {
tempLoading.value = true
modelList({ model: props.modeType, type: 2, parentId: props.tempId }).then(res => {
childTempList.value = res.rows
getTempResult()
}).finally(() => {
tempLoading.value = false
})
}
//
const getTempResult = () => {
tempResult({ mainModelId: props.tempId }).then(res => {
let rows = res.rows
childTempList.value.forEach(item => {
rows.forEach(el => {
if (item.id == el.modelId) {
item.answer = el.content
item.resultId = el.id
}
})
})
})
}
const isEdit = ref(false)
watch(() => props.tempId, (newVal) => {
if (newVal) {
// isEdit.value = true
getChildTemplate()
}
})
// ID
const params = reactive(
{
prompt: '',
dataset_id: ''
}
)
const curNode = reactive({})
const getConversation = () => {
getCompletion()
}
//
const getCompletion = async () => {
for (let item of childTempList.value) {
try {
item.loading = true
params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
const { data } = await completion(params)
let answer = data.answer
item.oldAnswer = answer
item.answer = getResult(answer);
onSaveTemp(item)
} finally {
item.loading = false
}
}
}
//
const onSaveTemp = (item) => {
const data = {
mainModelId: props.tempId,
modelId: item.id,
examDocld: '',
content: item.oldAnswer
}
tempSave(data).then(res => {
console.log(res)
})
}
//
const againResult = async (index, item) => {
try {
childTempList.value[index].loading = true
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
const { data } = await completion(params)
let answer = data.answer
childTempList.value[index].oldAnswer = answer
childTempList.value[index].answer = getResult(answer);
onEditSave(childTempList.value[index])
} finally {
childTempList.value[index].loading = false
}
}
//
const onEditSave = async (item) =>{
const { res } = await editTempResult({id: item.resultId, content: item.oldAnswer})
ElMessage.success(res)
getChildTemplate()
}
//
let getResult = (text) => {
text = text.replace(/^\n\n(.*?)\n\n$/s, '<div>$1</div>');
text = text.replace(/^\n(.*?)\n$/s, '<p>$1</p>');
text = text.replace(/\*\*(.*?)\*\*/g, "<div class='text-tit'>$1</div>");
text = text.replace(/(\d+\..*?)\n/g, "<div class='text-num'>$1</div>\n");
return text
}
//
const curIndex = ref(-1)
const editItem = reactive({})
const onEdit = (index, item) => {
curIndex.value = index
Object.assign(editItem, item)
isEdit.value = true
}
//
const saveEdit = (data) => {
// childTempList.value[curIndex.value].oldAnswer = data
// let answer = getResult(data);
// childTempList.value[curIndex.value].answer = answer
getChildTemplate()
}
const isAdjust = ref(false)
const onAdjust = (index, item) => {
curIndex.value = index
Object.assign(editItem, item)
isAdjust.value = true
}
const saveAdjust = (item) => {
childTempList.value[curIndex.value].oldAnswer = item
let answer = getResult(item);
childTempList.value[curIndex.value].answer = answer
}
//
const keywordItem = reactive({})
const isEditKeyWord = ref(false)
const isAdd = ref(true)
const editKeyWord = (item) => {
console.log(item)
isAdd.value = false
isEditKeyWord.value = true
Object.assign(keywordItem, item)
}
//
const removeItem = async (item) => {
if (item.ex3 != '1') {
ElMessageBox.confirm(
'确认是否移除?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
console.log(item)
removeChildTemp(item.id).then(res => {
ElMessage.success('操作成功')
getChildTemplate()
})
})
}
else{
isAdd.value = false
Object.assign(keywordItem, item)
isEditKeyWord.value = true
}
// const { msg } = await removeChildTemp(item.id)
// ElMessage.success(msg)
// getChildTemplate()
}
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
let text = props.modeType == 1 || props.modeType == 2? '课标' : '考试'
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey]
})
//
onUnmounted(() => {
emitter.off('onGetChild')
})
defineExpose({
getConversation
})
</script>
<style lang="scss" scoped>
.read-container {
display: flex;
flex-direction: column;
width: 100%;
padding: 15px 0;
height: 100%;
position: relative;
.el-scrollbar {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
.el-dropdown-link {
font-weight: bold;
.el-icon--right {
font-weight: bold;
}
}
.read-header {
justify-content: space-between;
align-items: center;
.add-btn {
font-size: 13px;
.icon-jiahao {
margin-right: 3px;
font-size: 14px;
}
}
}
.right-con {
display: flex;
}
.template-list {
.template-item {
background: #fff;
padding: 10px;
margin-top: 10px;
border-radius: 5px;
.item-header {
display: flex;
align-items: center;
font-size: 16px;
font-weight: bold;
color: #000;
justify-content: space-between;
.blue {
font-size: 22px;
color: #409eff;
margin-right: 5px;
}
}
.item-text {
display: flex;
margin-top: 10px;
font-size: 14px;
text-align: left;
color: #606266;
.item-icon {
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
background: #F6F6F6;
border-radius: 50%;
margin-right: 10px;
flex-shrink: 0;
}
.item-answer {
flex-direction: column;
padding-top: 5px;
:deep(.text-tit) {
font-weight: bold;
margin: 10px 0;
}
:deep(.text-num) {
padding-left: 2em;
}
}
}
.text-answer {
color: #409eff;
}
.ai-btn {
margin-top: 10px;
display: flex;
justify-content: flex-end;
.iconfont {
margin-right: 3px;
}
:deep(.el-button) {
font-size: 13px;
}
.icon-ai1 {
font-size: 18px;
}
}
}
}
.template-item-result {
background: #DDEAFD !important;
.result-item-header {
display: flex;
align-items: flex-start;
text-align: left;
font-size: 16px;
font-weight: bold;
color: #3D3D3D;
.icon-xiaoxi {
color: #5881D5;
font-weight: bold;
font-size: 20px;
margin-right: 10px;
}
}
.result-icon-btn {
justify-content: space-between;
font-size: 13px;
margin-top: 5px;
span {
display: flex;
align-items: center;
cursor: pointer;
margin-right: 10px;
&:hover {
background: #cfe0fa
}
}
.iconfont {
margin-right: 3px;
color: #3498fc;
}
}
.line {
height: 1px;
background: #D8D8D8;
margin: 10px 0;
}
.other-msg {
font-size: 13px;
.other-user {
align-items: center;
color: #BA4B0F;
font-size: 12px;
.icon-touxiang {
color: #BA4B0F;
font-weight: bold;
font-size: 16px;
margin-right: 5px;
}
}
.other-text {
color: #191919;
text-align: left;
}
}
}
}
.icon-shenglvehao {
font-weight: bold;
font-size: 22px;
}
:deep(.el-popover) {
min-width: 50px;
width: 50px !important;
}
.pl-25 {
padding-left: 25px;
}
</style>
<style>
.template-custom-popover {
width: 110px !important;
min-width: 110px !important;
}
</style>

View File

@ -1,52 +1,11 @@
<template> <template>
<div class="page-template flex"> <TemplateStudy :type="3"/>
<!--头部-->
<Header :type="type" @changeTemp="changeTemp" @onRead="onRead"/>
<el-row :gutter="20" class="tempalte-main">
<el-col :span="12">
<!--左侧pdf-->
<Pdf />
</el-col>
<el-col :span="12">
<!--右侧模板研读-->
<Result ref="resultRef" :modeType="type" :tempId="tempId"/>
</el-col>
</el-row>
</div>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import TemplateStudy from '@/components/template-study/index.vue'
import Header from './container/header.vue'
import Pdf from './container/pdf.vue'
import Result from './container/result.vue'
const props = defineProps({
type: {
type: Number,
default: 3
},
})
const resultRef = ref()
const tempId = ref('')
const changeTemp = (id) =>{
tempId.value = id
}
const onRead = () =>{
resultRef.value.getConversation()
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.page-template {
flex-direction: column;
height: 100%;
.tempalte-main {
flex: 1;
}
}
</style> </style>