lyc-dev #67
|
@ -45,6 +45,7 @@ import { conversation, completion } from '@/api/mode/index'
|
|||
import { sessionStore } from '@/utils/store'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { dataSetJson } from '@/utils/comm.js'
|
||||
import emitter from '@/utils/mitt';
|
||||
|
||||
const textarea = ref('')
|
||||
|
||||
|
@ -109,13 +110,16 @@ const getCompletion = async (val) => {
|
|||
}
|
||||
|
||||
const saveAdjust = (item) =>{
|
||||
emit('saveAdjust', item.msg)
|
||||
|
||||
isDialog.value = false
|
||||
ElMessage.success('操作成功')
|
||||
emitter.on('saveAdjust', item.msg)
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let data = sessionStore.get('subject.curNode')
|
||||
|
||||
Object.assign(curNode, data);
|
||||
let text = props.modeType == 1 ? '课标': props.modeType == 2 ? '教材' : '考试'
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
import { ref, watch} from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { editTempResult } from '@/api/mode/index.js'
|
||||
import emitter from '@/utils/mitt';
|
||||
|
||||
const textarea = ref('')
|
||||
|
||||
|
@ -49,12 +50,12 @@ watch(() => props.item.answer, (newVal) => {
|
|||
}
|
||||
},{ deep: true })
|
||||
|
||||
const emit = defineEmits(['saveEdit'])
|
||||
|
||||
const onSave = () =>{
|
||||
editTempResult({id: props.item.reultId, content: textarea.value}).then( res =>{
|
||||
isDialog.value = false
|
||||
ElMessage.success('操作成功')
|
||||
emit('saveEdit', textarea.value)
|
||||
emitter.emit('onGetChild', textarea.value)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
<template>
|
||||
<div class="container-header flex">
|
||||
<div class="header-left flex">
|
||||
<el-button link @click="onClick">
|
||||
{{ curNode.edustage}}{{ curNode.edusubject }}{{ type == 1 ? '课标研读': '教材分析'}}<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="danger">删除</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
|
||||
}
|
||||
|
||||
const onClick = () =>{
|
||||
if(props.type == 1) return
|
||||
showDialog.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>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-dialog v-model="mode" :show-close="false" width="600" destroy-on-close>
|
||||
<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' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
||||
|
@ -12,12 +12,10 @@
|
|||
<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">
|
||||
|
@ -52,14 +50,9 @@ const props = defineProps({
|
|||
return { ex3: '' }
|
||||
}
|
||||
},
|
||||
tempId: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
prompt: '',
|
||||
|
@ -73,16 +66,17 @@ watch(() => props.isAdd, (newVal) => {
|
|||
|
||||
}, { 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')
|
||||
emitter.emit('onGetMain')
|
||||
ElMessage.success(msg)
|
||||
mode.value = false
|
||||
} finally {
|
||||
|
@ -138,7 +132,6 @@ const onAddChildTemp = async (parentId) => {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div class="container-left-page flex">
|
||||
<div class="container-left-header flex">
|
||||
<el-button link @click="onClick">
|
||||
{{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : '教材分析' }}<i
|
||||
class="iconfont icon-xiangxia"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="container-left-pdf">
|
||||
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
|
||||
</div>
|
||||
<!--弹窗-->
|
||||
<LeftDialog v-model="showDialog" :modeType="type" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import PDF from '@/components/PdfJs/index.vue'
|
||||
import LeftDialog from './left-dialog.vue'
|
||||
|
||||
const props = defineProps(['curNode', 'type'])
|
||||
|
||||
const showDialog = ref(false)
|
||||
const onClick = () => {
|
||||
if (props.type == 1) return
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
// 加载PDF
|
||||
const pdfUrl = ref('')
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
const { fileurl } = props.curNode
|
||||
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-left-page {
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
.container-left-header {
|
||||
height: 45px;
|
||||
background: #fff;
|
||||
border-radius: 5px 0 0 0;
|
||||
justify-content: flex-start;
|
||||
padding-left: 10px;
|
||||
|
||||
.icon-xiangxia {
|
||||
margin-left: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.container-left-pdf {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,28 +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()
|
||||
const { fileurl } = sessionStore.get('subject.curBook')
|
||||
console.log(fileurl,'fileurl');
|
||||
|
||||
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt','.pdf')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-pdf{
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,505 +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 } 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.reultId = 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 = `根据${curNode.edustage}${curNode.edusubject}课标,提炼出${item.name}`
|
||||
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 = `根据${curNode.edustage}${curNode.edusubject}课标,提炼出${item.name}`
|
||||
const { data } = await completion(params)
|
||||
let answer = data.answer
|
||||
childTempList.value[index].oldAnswer = answer
|
||||
childTempList.value[index].answer = getResult(answer);
|
||||
} finally {
|
||||
childTempList.value[index].loading = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 分析获取课标对话结果
|
||||
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>
|
|
@ -0,0 +1,451 @@
|
|||
<template>
|
||||
<div class="container-right-page flex">
|
||||
<!--头部-->
|
||||
<div class="container-right-header 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="danger" link @click="removeItem(curTemplate, false)">
|
||||
删除
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="onAdd">
|
||||
<i class="iconfont icon-jiahao"></i>
|
||||
添加提示词
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="!(childTempList.length)" @click="getCompletion">一键研读</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!--List-->
|
||||
<div class="container-right-list">
|
||||
<template v-for="(item, index) in childTempList">
|
||||
<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, true)">移除</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>
|
||||
|
||||
</template>
|
||||
<el-empty v-if="!childTempList.length" description="暂无模板数据" />
|
||||
</div>
|
||||
</div>
|
||||
<!--编辑结果-->
|
||||
<EditDialog v-model="isEdit" :item="editItem" />
|
||||
<!--AI 对话调整-->
|
||||
<AdjustDialog v-model="isAdjust" :modeType="type" :item="editItem" />
|
||||
<!--添加、编辑提示词-->
|
||||
<keywordDialog v-model="isWordDialog" :isAdd="isAdd" :item="editItem" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, onUnmounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { tempSave, completion, modelList, removeChildTemp, tempResult } from '@/api/mode/index'
|
||||
import keywordDialog from './keyword-dialog.vue';
|
||||
import AdjustDialog from './adjust-dialog.vue'
|
||||
import EditDialog from './edit-dialog.vue'
|
||||
import emitter from '@/utils/mitt';
|
||||
import { dataSetJson } from '@/utils/comm.js'
|
||||
|
||||
const props = defineProps(['curNode', 'type'])
|
||||
|
||||
/*****************提示词相关****************/
|
||||
|
||||
/**
|
||||
* isWordDialog : 提示词弹窗
|
||||
* isAdd : 是否添加 默认false
|
||||
* editItem: 当前操作的item (添加的时候不需要这个)
|
||||
*/
|
||||
|
||||
const isWordDialog = ref(false)
|
||||
const isAdd = ref(false)
|
||||
const editItem = reactive({})
|
||||
const onAdd = () => {
|
||||
isAdd.value = true
|
||||
Object.assign(editItem, curTemplate)
|
||||
isWordDialog.value = true
|
||||
|
||||
}
|
||||
|
||||
const editKeyWord = (item, val) => {
|
||||
/**
|
||||
* isAdd: 字模板中的移除 为编辑 头部删除 添加提示词为新增
|
||||
*/
|
||||
isAdd.value = val
|
||||
Object.assign(editItem, item)
|
||||
isWordDialog.value = true
|
||||
}
|
||||
|
||||
/*******************模板相关**********************/
|
||||
|
||||
/**
|
||||
* curTemplate :当前模板
|
||||
* templateList :主模板List
|
||||
* childTempList : 子模板List
|
||||
* getTemplateList() : 获取主模板
|
||||
* getChildTemplate() :获取子模板
|
||||
*/
|
||||
const tempLoading = ref(false)
|
||||
const curTemplate = reactive({ name: '', id: '' })
|
||||
const templateList = ref([])
|
||||
const childTempList = 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]);
|
||||
getChildTemplate()
|
||||
})
|
||||
}
|
||||
const getChildTemplate = () => {
|
||||
tempLoading.value = true
|
||||
modelList({ model: props.type, type: 2, parentId: curTemplate.id }).then(res => {
|
||||
childTempList.value = res.rows
|
||||
|
||||
getTempResult()
|
||||
}).finally(() => {
|
||||
tempLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 查询模板结果
|
||||
const getTempResult = () => {
|
||||
tempResult({ mainModelId: curTemplate.id }).then(res => {
|
||||
let rows = res.rows
|
||||
childTempList.value.forEach(item => {
|
||||
rows.forEach(el => {
|
||||
if (item.id == el.modelId) {
|
||||
item.answer = el.content
|
||||
item.reultId = el.id
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 模板切换
|
||||
const changeTemplate = (val) => {
|
||||
ElMessageBox.confirm(
|
||||
'切换模板将清除当前研读结果?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
Object.assign(curTemplate, val);
|
||||
getChildTemplate()
|
||||
})
|
||||
}
|
||||
|
||||
// 移除模板
|
||||
const removeItem = async (item, isChild) => {
|
||||
/**
|
||||
* item: 当前操作的模板
|
||||
* isChild: 子模板中的移除为 true 否则为false
|
||||
*/
|
||||
if (item.ex3 != '1') {
|
||||
ElMessageBox.confirm(
|
||||
'确认是否移除?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
removeChildTemp(item.id).then(res => {
|
||||
ElMessage.success('操作成功')
|
||||
if(isChild){
|
||||
// 获取子模板
|
||||
getChildTemplate()
|
||||
}
|
||||
else{
|
||||
// 获取主模板
|
||||
getTemplateList()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
editKeyWord(item,!isChild)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Ai对话调整
|
||||
const curIndex = ref(-1)
|
||||
const isAdjust = ref(false)
|
||||
const onAdjust = (index, item) => {
|
||||
curIndex.value = index
|
||||
Object.assign(editItem, item)
|
||||
isAdjust.value = true
|
||||
}
|
||||
|
||||
// 编辑结果
|
||||
const isEdit = ref(false)
|
||||
const onEdit = (index, item) => {
|
||||
curIndex.value = index
|
||||
Object.assign(editItem, item)
|
||||
isEdit.value = true
|
||||
}
|
||||
|
||||
|
||||
const modeType = computed(() => {
|
||||
if (props.type == 1) return '课标';
|
||||
if (props.type == 2) return '教材';
|
||||
if (props.type == 3) return '考试';
|
||||
})
|
||||
// 重新研读
|
||||
const params = reactive(
|
||||
{
|
||||
prompt: '',
|
||||
dataset_id: ''
|
||||
}
|
||||
)
|
||||
// 重新研读
|
||||
const againResult = async (index, item) => {
|
||||
try {
|
||||
childTempList.value[index].loading = true
|
||||
params.prompt = `按照${item.name}的要求,针对${props.curNode.edustage}${props.curNode.edusubject}${modeType} 对${props.curNode.itemtitle}进行教学分析`
|
||||
const { data } = await completion(params)
|
||||
let answer = data.answer
|
||||
childTempList.value[index].oldAnswer = answer
|
||||
childTempList.value[index].answer = getResult(answer);
|
||||
onSaveTemp(item)
|
||||
} finally {
|
||||
childTempList.value[index].loading = false
|
||||
}
|
||||
}
|
||||
// 一键研读
|
||||
const getCompletion = async () => {
|
||||
for (let item of childTempList.value) {
|
||||
try {
|
||||
item.loading = true
|
||||
params.prompt = `按照${item.name}的要求,针对${props.curNode.edustage}${props.curNode.edusubject}${modeType} 对${props.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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 替换分析结果
|
||||
emitter.on('saveAdjust', (item) => {
|
||||
childTempList.value[curIndex.value].oldAnswer = item
|
||||
let answer = getResult(item);
|
||||
childTempList.value[curIndex.value].answer = answer
|
||||
})
|
||||
|
||||
// 分析获取课标对话结果
|
||||
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
|
||||
}
|
||||
|
||||
// 操作之后获取字模板
|
||||
emitter.on('onGetChild', () => {
|
||||
getChildTemplate()
|
||||
})
|
||||
// 操作之后获取主模板
|
||||
emitter.on('onGetMain', () => {
|
||||
getTemplateList()
|
||||
})
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getTemplateList()
|
||||
let jsonKey = `${modeType}-${props.curNode.edustage}-${props.curNode.edusubject}`
|
||||
params.dataset_id = dataSetJson[jsonKey]
|
||||
})
|
||||
|
||||
// 解绑
|
||||
onUnmounted(() => {
|
||||
emitter.off('onGetChild');
|
||||
emitter.off('saveAdjust');
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-right-page {
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
.container-right-header {
|
||||
height: 45px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
border-left: solid #EBF0F4 1px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 15px;
|
||||
border-radius: 0 5px 0 0
|
||||
}
|
||||
|
||||
.container-right-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 5px 15px;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.template-custom-popover {
|
||||
width: 110px !important;
|
||||
min-width: 110px !important;
|
||||
}
|
||||
</style>
|
|
@ -1,26 +1,21 @@
|
|||
<template>
|
||||
<div class="page-template flex">
|
||||
<!--头部-->
|
||||
<Header :type="type" @changeTemp="changeTemp" @onRead="onRead"/>
|
||||
<el-row :gutter="20" class="tempalte-main">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<!--左侧pdf-->
|
||||
<Pdf />
|
||||
<Left :curNode="curNode" :type="type" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<!--右侧模板研读-->
|
||||
<Result ref="resultRef" :modeType="type" :tempId="tempId"/>
|
||||
<Right :curNode="curNode" :type="type" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import Header from './container/header.vue'
|
||||
import Pdf from './container/pdf.vue'
|
||||
import Result from './container/result.vue'
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import Left from './container/left.vue'
|
||||
import Right from './container/right.vue'
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
|
@ -29,22 +24,24 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const curNode = reactive({})
|
||||
onMounted(() =>{
|
||||
let data = sessionStore.get('subject.curNode')
|
||||
Object.assign(curNode, data);
|
||||
})
|
||||
|
||||
const resultRef = ref()
|
||||
const tempId = ref('')
|
||||
const changeTemp = (id) =>{
|
||||
tempId.value = id
|
||||
}
|
||||
|
||||
const onRead = () =>{
|
||||
resultRef.value.getConversation()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-template {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
.el-row{
|
||||
height: 100%;
|
||||
.el-col{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.tempalte-main {
|
||||
flex: 1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue