edit 模板

This commit is contained in:
lyc 2024-12-03 10:11:51 +08:00
parent cd6ca2e4e7
commit 3ad4391e10
4 changed files with 255 additions and 180 deletions

View File

@ -142,13 +142,18 @@ const handleNodeClick = (data) => {
//label label //label label
nodeData.label = nodeData.itemtitle nodeData.label = nodeData.itemtitle
// null let parentNode
let parent = { // id == rootid
id: nodeData.parentid, if(nodeData.parentid == nodeData.rootid){
label: nodeData.parenttitle, parentNode = null
itemtitle: nodeData.parenttitle }
} else{
const parentNode = nodeData.parentid ? parent : null parentNode = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
}
nodeData.parentNode = parentNode nodeData.parentNode = parentNode
let curData = { let curData = {
textBook: { textBook: {

View File

@ -26,52 +26,52 @@
</div> </div>
</div> </div>
<!--List--> <!--List-->
<div class="container-right-list"> <div class="container-right-list" ref="listRef">
<template v-for="(item, index) in childTempList"> <template v-for="(item, index) in childTempList">
<div class="template-item" v-loading="item.loading"> <div class="template-item" v-loading="item.loading">
<div class="item-header"> <div class="item-header">
<div> <div>
<span class="blue">#</span>{{ item.name }} <span class="blue">#</span>{{ item.name }}
</div> </div>
<el-popover placement="bottom-end" trigger="hover" popper-class="template-custom-popover"> <el-popover placement="bottom-end" trigger="hover" popper-class="template-custom-popover">
<template #reference> <template #reference>
<el-button link type="primary"> <el-button link type="primary">
<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)">编辑</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>
</div> </div>
<div class="item-text"> <div class="item-text">
{{ item.prompt }} {{ item.prompt }}
</div> </div>
<div class="item-text text-answer" v-if="item.answer"> <div class="item-text text-answer" v-if="item.answer">
<div class="item-icon"> <div class="item-icon">
<i class="iconfont icon-ai"></i> <i class="iconfont icon-ai"></i>
</div> </div>
<div class="item-answer"> <div class="item-answer">
<TypingEffect :text="item.oldAnswer" :delay="10" :aiShow="item.aiShow" @complete="onSaveTemp(item)" /> <TypingEffect v-if="isStarted[index]" :text="item.answer" :delay="10" :aiShow="item.aiShow" @complete="handleCompleteText(index)" @updateScroll="scrollToBottom($event,index)" />
</div> </div>
</div> </div>
<div class="ai-btn" v-if="item.answer"> <div class="ai-btn" v-if="item.answer">
<el-button type="primary" link @click="againResult(index, item)"> <el-button type="primary" link @click="againResult(index, item)">
<i class="iconfont icon-ai1"></i> <i class="iconfont icon-ai1"></i>
重新研读 重新研读
</el-button> </el-button>
<el-button type="primary" link @click="onAdjust(index, item)"> <el-button type="primary" link @click="onAdjust(index, item)">
<i class="iconfont icon-duihua"></i> <i class="iconfont icon-duihua"></i>
AI对话调整 AI对话调整
</el-button> </el-button>
<el-button type="primary" link @click="onEdit(index, item)"> <el-button type="primary" link @click="onEdit(index, item)">
<i class="iconfont icon-bianji1"></i> <i class="iconfont icon-bianji1"></i>
手动编辑结果 手动编辑结果
</el-button> </el-button>
</div> </div>
</div> </div>
</template> </template>
<el-empty v-if="!childTempList.length" description="暂无模板数据" /> <el-empty v-if="!childTempList.length" description="暂无模板数据" />
</div> </div>
</div> </div>
<!--编辑结果--> <!--编辑结果-->
@ -83,7 +83,7 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue' import { ref, reactive, onMounted, watch, onUnmounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { tempSave, completion, modelList, removeChildTemp, tempResult, editTempResult } from '@/api/mode/index' import { tempSave, completion, modelList, removeChildTemp, tempResult, editTempResult } from '@/api/mode/index'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
@ -146,14 +146,16 @@ const getChildTemplate = () => {
tempLoading.value = true tempLoading.value = true
modelList({ model: props.type, type: 2, parentId: curTemplate.id }).then(res => { modelList({ model: props.type, type: 2, parentId: curTemplate.id }).then(res => {
childTempList.value = res.rows childTempList.value = res.rows
if(childTempList.value.length){
childTempList.value.forEach(item => item.answer = '')
}
getTempResult() getTempResult()
}).finally(() => { }).finally(() => {
tempLoading.value = false tempLoading.value = false
}) })
} }
const isStarted = ref([]);
const listRef = ref()
// //
const getTempResult = () => { const getTempResult = () => {
tempResult({ mainModelId: curTemplate.id, pageNum: 1, pageSize: 10000 }).then(res => { tempResult({ mainModelId: curTemplate.id, pageNum: 1, pageSize: 10000 }).then(res => {
@ -161,14 +163,45 @@ const getTempResult = () => {
childTempList.value.forEach(item => { childTempList.value.forEach(item => {
rows.forEach(el => { rows.forEach(el => {
if (item.id == el.modelId) { if (item.id == el.modelId) {
item.answer = el.content item.answer = getResult(el.content)
item.resultId = el.id item.resultId = el.id
} }
}) })
}) })
if(rows.length > 0){
isStarted.value = new Array(rows.length).fill(true)
}
}) })
} }
const scrollToBottom = (height,index) =>{
if (listRef.value) {
let listDom = listRef.value.children
for(let i = 0; i < index; i++){
sum += listDom[i].clientHeight
}
listRef.value.scrollTop = sum + height
// isStarted.value[index] = height
// const sum = isStarted.value.reduce((accumulator, currentValue) => {
// //
// if (typeof currentValue === 'number') {
// return accumulator + currentValue;
// }
// //
// return accumulator;
// }, 0);
// listRef.value.scrollTop = sum - 150;
}
}
// //
const changeTemplate = (val) => { const changeTemplate = (val) => {
ElMessageBox.confirm( ElMessageBox.confirm(
@ -203,11 +236,11 @@ const removeItem = async (item, isChild) => {
).then(() => { ).then(() => {
removeChildTemp(item.id).then(res => { removeChildTemp(item.id).then(res => {
ElMessage.success('操作成功') ElMessage.success('操作成功')
if(isChild){ if (isChild) {
// //
getChildTemplate() getChildTemplate()
} }
else{ else {
// //
getTemplateList() getTemplateList()
} }
@ -215,7 +248,7 @@ const removeItem = async (item, isChild) => {
}) })
} }
else { else {
editKeyWord(item,!isChild) editKeyWord(item, !isChild)
} }
} }
@ -240,19 +273,18 @@ const onEdit = (index, item) => {
const modeType = ref('课标') const modeType = ref('课标')
watch(() => props.type, (newVal) => { watch(() => props.type, (newVal) => {
if (newVal == 1){ if (newVal == 1) {
modeType.value = '课标' modeType.value = '课标'
} }
if (newVal == 2){ if (newVal == 2) {
modeType.value = '教材' modeType.value = '教材'
} }
if (newVal == 2){ if (newVal == 3) {
modeType.value = '考试' modeType.value = '考试'
} }
}, { immediate: false }) }, { immediate: false })
// //
const params = reactive( const params = reactive(
{ {
@ -260,17 +292,35 @@ const params = reactive(
dataset_id: '' dataset_id: ''
} }
) )
// //
const againResult = async (index, item) => { const againResult = async (index, item) => {
let sTop = isStarted.value[index]
isStarted.value[index] = false
childTempList.value[index].answer = ''
console.log(listRef.value.scrollTop,listRef.value.children[index].clientHeight);
if(index == 0){
listRef.value.scrollTop = 0
}else{
console.log( listRef.value.scrollTop - (listRef.value.children[index].clientHeight),'index')
listRef.value.scrollTop =listRef.value.scrollTop - listRef.value.children[index].clientHeight - 50
}
try { try {
await nextTick()
childTempList.value[index].loading = true childTempList.value[index].loading = true
item.aiShow = true item.aiShow = true
childTempList.value[index].oldAnswer = '' //params.prompt = `${item.name}${curNode.edustage}${curNode.edusubject}${modeType.value} ${curNode.itemtitle}`
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${curNode.itemtitle}进行教学分析` //const { data } = await completion(params)
const { data } = await completion(params) //childTempList.value[index].answer = getResult(data.answer);
let answer = data.answer childTempList.value[index].answer = '根据知识库的内容,核心素\n\n综上所述在大足石刻研学课程中应充分考虑语文学科核心素养的要求通过实践活动提高学生语言运用能力、思维品质及文化认同'
childTempList.value[index].oldAnswer = answer isStarted.value[index] = true
childTempList.value[index].answer = getResult(answer);
// onEditSave(item) // onEditSave(item)
} finally { } finally {
childTempList.value[index].loading = false childTempList.value[index].loading = false
@ -278,58 +328,67 @@ const againResult = async (index, item) => {
} }
// //
const getCompletion = async () => { const getCompletion = async () => {
isStarted.value = new Array(childTempList.length).fill(false)
isStarted.value[0] = true
childTempList.value.forEach(item =>{
if(item.answer){
item.answer = ''
}
})
for (let item of childTempList.value) { for (let item of childTempList.value) {
try { try {
item.loading = true item.loading = true
item.aiShow = true item.aiShow = true
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${curNode.itemtitle}进行教学分析` params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${curNode.itemtitle}进行教学分析`
const { data } = await completion(params) const { data } = await completion(params)
let answer = data.answer item.answer = getResult(data.answer)
item.oldAnswer = answer onSaveTemp(item)
item.answer = getResult(answer);
} finally { } finally {
item.loading = false item.loading = false
} }
} }
} }
const handleCompleteText = (index) =>{
if (index < childTempList.value.length - 1) {
isStarted.value[index + 1] = true; //
}
}
// //
emitter.on('onSaveAdjust', (item) => { emitter.on('onSaveAdjust', (item) => {
childTempList.value[curIndex.value].oldAnswer = item childTempList.value[curIndex.value].answer = item
let answer = getResult(item);
childTempList.value[curIndex.value].oldAnswer = item
childTempList.value[curIndex.value].answer = answer
onEditSave(childTempList.value[curIndex.value]) onEditSave(childTempList.value[curIndex.value])
}) })
// //
const onEditSave = async (item) =>{ const onEditSave = async (item) => {
const { res } = await editTempResult({id: item.resultId, content: item.oldAnswer}) const { res } = await editTempResult({ id: item.resultId, content: item.answer })
ElMessage.success(res) ElMessage.success(res)
getChildTemplate() getChildTemplate()
} }
// //
const onSaveTemp = (item) => { const onSaveTemp = (item) => {
if(item.oldAnswer == '') return if (item.answer == '') return
const data = { const data = {
mainModelId: curTemplate.id, mainModelId: curTemplate.id,
modelId: item.id, modelId: item.id,
examDocld: '', examDocld: '',
content: item.oldAnswer content: item.answer
} }
tempSave(data).then(res => {}) tempSave(data).then(res => { })
} }
// // ### **
let getResult = (text) => { let getResult = (str) => {
text = text.replace(/^\n\n(.*?)\n\n$/s, '<div>$1</div>'); let newStr = str.replace(/#+|(\*\*)/g, '');
text = text.replace(/^\n(.*?)\n$/s, '<p>$1</p>'); return newStr
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
} }
// //
@ -381,87 +440,85 @@ onUnmounted(() => {
padding: 5px 15px; padding: 5px 15px;
box-sizing: border-box; box-sizing: border-box;
.template-item {
background: #fff;
padding: 10px;
margin-top: 10px;
border-radius: 5px;
.template-item { .item-header {
background: #fff; display: flex;
padding: 10px; align-items: center;
margin-top: 10px; font-size: 16px;
border-radius: 5px; font-weight: bold;
color: #000;
justify-content: space-between;
.item-header { .blue {
display: flex; font-size: 22px;
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;
width: 100%;
:deep(.text-tit) {
font-weight: bold;
margin: 10px 0;
}
:deep(.text-num) {
padding-left: 2em;
}
}
}
.text-answer {
color: #409eff; color: #409eff;
} margin-right: 5px;
.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;
}
} }
} }
.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;
width: 100%;
:deep(.text-tit) {
font-weight: bold;
margin: 10px 0;
}
:deep(.text-num) {
padding-left: 2em;
}
}
}
.text-answer {
color: #409eff;
}
.ai-btn {
margin-top: 10px;
display: flex;
justify-content: flex-end;
.iconfont {
margin-right: 3px;
}
:deep(.el-button) {
font-size: 13px;
}
.icon-ai1 {
font-size: 18px;
}
}
}
} }
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="typing-effect"> <div class="typing-effect" ref="typingEffectRef">
<!-- <span v-html="displayedText"></span> --> <!-- <span v-html="displayedText"></span> -->
<el-input <el-input
v-model="displayedText" v-model="displayedText"
@ -14,11 +14,11 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch, nextTick } from 'vue';
const props = defineProps({ const props = defineProps({
text: { text: {
type: String, type: [String, Object],
required: true required: true
}, },
delay: { delay: {
@ -26,19 +26,27 @@ const props = defineProps({
default: 100 // default: 100 //
}, },
aiShow: { aiShow: {
type: [Boolean] type: [Boolean] // true
} }
}); });
const emit = defineEmits(['complete']); const typingEffectRef = ref(null);
const emit = defineEmits(['complete', 'updateScroll']);
const displayedText = ref(''); const displayedText = ref('');
const index = ref(0); const index = ref(0);
const type = () => { const type = async () => {
if(!props.aiShow) return await nextTick()
if(!props.aiShow) {
displayedText.value = props.text
return
}
if (index.value <= props.text.length) { if (index.value <= props.text.length) {
displayedText.value += props.text.charAt(index.value); displayedText.value += props.text.charAt(index.value);
index.value++; index.value++;
setTimeout(() => type(), props.delay); setTimeout(() => {
type();
emit('updateScroll', typingEffectRef.value.offsetHeight); //
}, props.delay);
} else { } else {
// complete // complete
emit('complete'); emit('complete');
@ -46,15 +54,20 @@ const type = () => {
}; };
onMounted(() => { onMounted(() => {
type(); resetAndType();
}); });
// props 便 text delay const resetAndType = () =>{
watch([() => props.text, () => props.delay], () => {
displayedText.value = ''; displayedText.value = '';
index.value = 0; index.value = 0;
type(); type();
}); }
// props 便 text delay
watch([() => props.text, () => props.delay], resetAndType);
</script> </script>
<style scoped> <style scoped>

View File

@ -194,7 +194,7 @@ onMounted(() =>{
margin-bottom: 10px; margin-bottom: 10px;
.img { .img {
width: 100px; width: 100%;
height: 130px; height: 130px;
border: solid #ccc 1px; border: solid #ccc 1px;
margin-bottom: 10px; margin-bottom: 10px;