This commit is contained in:
zdg 2024-11-27 13:45:57 +08:00
commit a10d3b078b
41 changed files with 1929 additions and 987 deletions

View File

@ -108,4 +108,21 @@ export function pyOCRAPI(path) {
imageBas64: path, imageBas64: path,
} }
}) })
}
/**
* @desc: 菁优网转发
* @return: {*}
* @param {*} url
* @param {*} config
*/
export function getJYPath(url,config) {
config.params = config.params?config.params:{}
config.params["getjypath"] = url;
return request({
url: "/jy/proxy",
method: config.method||"get",
params: config.params
})
} }

View File

@ -2,12 +2,21 @@
import request from '@/utils/request' import request from '@/utils/request'
// 查询知识点列表 // 查询知识点列表
export function listKnowlegepoint(query) { export function listKnowlegepoint(query) {
return request({ return request({
url: '/point/list', url: '/point/list',
method: 'get', method: 'get',
params: query params: query
}) })
} }
// 查询知识点列表
export function listKnowlegepointFormat(query) {
return request({
url: '/point/formatList',
method: 'get',
params: query
})
}
// 查询一级知识点查下级所有层级 // 查询一级知识点查下级所有层级

View File

@ -71,4 +71,9 @@ defineExpose({
justify-content: flex-end; justify-content: flex-end;
box-sizing: border-box; box-sizing: border-box;
} }
:deep(.format-work-desc > :is(div):first-child){
display: inline-block;
}
</style> </style>

View File

@ -40,11 +40,12 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted, watch } from 'vue'
import { conversation, completion } from '@/api/mode/index' import { completion } from '@/api/mode/index'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { dataSetJson } from '@/utils/comm.js' import { dataSetJson } from '@/utils/comm.js'
import emitter from '@/utils/mitt';
const textarea = ref('') const textarea = ref('')
@ -57,7 +58,7 @@ const props = defineProps({
return { name: '11' } return { name: '11' }
} }
}, },
modeType: { type: {
type: Number, type: Number,
default: 1 default: 1
} }
@ -76,7 +77,7 @@ const send = () =>{
msg: textarea.value msg: textarea.value
}) })
loaded.value = true loaded.value = true
getConversation(textarea.value) getCompletion(textarea.value)
textarea.value = '' textarea.value = ''
} }
const curNode = reactive({}) const curNode = reactive({})
@ -86,42 +87,53 @@ const params = reactive(
dataset_id: '' dataset_id: ''
} }
) )
// ID
const getConversation = (val) => {
getCompletion(val)
}
// //
const getCompletion = async (val) => { const getCompletion = async (val) => {
try { try {
params.prompt = `按照${val}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${curNode.itemtitle}进行教学分析`
params.prompt = `根据${curNode.edustage}${curNode.edusubject}课标${props.item.name}${val}`
const { data } = await completion(params) const { data } = await completion(params)
let answer = data.answer let answer = data.answer
msgList.value.push({ msgList.value.push({
type: 'robot', type: 'robot',
msg: answer, msg: answer,
}) })
} finally { } finally {
loaded.value = false loaded.value = false
} }
} }
const saveAdjust = (item) =>{ const saveAdjust = (item) =>{
emit('saveAdjust', item.msg)
isDialog.value = false isDialog.value = false
ElMessage.success('操作成功') ElMessage.success('操作成功')
emitter.on('saveAdjust', item.msg)
} }
const modeType = ref('课标')
watch(() => props.type, (newVal) => {
if (newVal == 1){
modeType.value = '课标'
}
if (newVal == 2){
modeType.value = '教材'
}
if (newVal == 2){
modeType.value = '考试'
}
}, { immediate: false })
onMounted(() => { onMounted(() => {
let data = sessionStore.get('subject.curNode') let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data); Object.assign(curNode, data);
let text = props.modeType == 1 ? '课标': props.modeType == 2 ? '教材' : '考试' let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey] params.dataset_id = dataSetJson[jsonKey]
}) })
</script> </script>
@ -152,7 +164,7 @@ onMounted(() => {
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
flex-direction: column; flex-direction: column;
} }
.chart-item { .chart-item {
border-radius: 5px; border-radius: 5px;
@ -257,4 +269,4 @@ onMounted(() => {
transform: scale(0.01); transform: scale(0.01);
} }
} }
</style> </style>

View File

@ -29,6 +29,7 @@
import { ref, watch} from 'vue' import { ref, watch} from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { editTempResult } from '@/api/mode/index.js' import { editTempResult } from '@/api/mode/index.js'
import emitter from '@/utils/mitt';
const textarea = ref('') const textarea = ref('')
@ -49,12 +50,12 @@ watch(() => props.item.answer, (newVal) => {
} }
},{ deep: true }) },{ deep: true })
const emit = defineEmits(['saveEdit'])
const onSave = () =>{ const onSave = () =>{
editTempResult({id: props.item.reultId, content: textarea.value}).then( res =>{ editTempResult({id: props.item.reultId, content: textarea.value}).then( res =>{
isDialog.value = false isDialog.value = false
ElMessage.success('操作成功') ElMessage.success('操作成功')
emit('saveEdit', textarea.value) emitter.emit('onGetChild', textarea.value)
}) })
} }

View File

@ -1,144 +0,0 @@
<template>
<div class="container-header flex">
<div class="header-left flex">
<el-button link @click="showDialog = true">
{{ 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="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
}
})
watch(() => props.type, (newValue) => {
console.log(newValue, 'newValue2');
}, { immediate: true });
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,5 +1,5 @@
<template> <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> <template #header>
<div class="custom-header flex"> <div class="custom-header flex">
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span> <span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span>
@ -12,12 +12,10 @@
<el-form-item label="名称"> <el-form-item label="名称">
<el-input v-model="form.name" /> <el-input v-model="form.name" />
</el-form-item> </el-form-item>
<el-form-item label="提示词" v-if="item.ex3 == '1' ? false : true"> <el-form-item label="提示词" v-if="item.ex3 == '1' ? false : true">
<el-input v-model="form.prompt" type="textarea" /> <el-input v-model="form.prompt" type="textarea" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -52,14 +50,9 @@ const props = defineProps({
return { ex3: '' } return { ex3: '' }
} }
}, },
tempId: {
type: [String, Number],
default: ''
},
}) })
const form = reactive({ const form = reactive({
name: '', name: '',
prompt: '', prompt: '',
@ -73,16 +66,17 @@ watch(() => props.isAdd, (newVal) => {
}, { immediate: false }) }, { immediate: false })
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.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 })
emitter.emit('onGetChild') emitter.emit('onGetMain')
ElMessage.success(msg) ElMessage.success(msg)
mode.value = false mode.value = false
} finally { } finally {
@ -138,7 +132,6 @@ const onAddChildTemp = async (parentId) => {
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -56,10 +56,10 @@ const title = computed(() => {
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([
{ {

View File

@ -0,0 +1,65 @@
<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 { sessionStore } from '@/utils/store'
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()
let data = sessionStore.get('subject.curBook')
let fileurl = data.fileurl
if(props.type == 1){
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-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>

View File

@ -1,26 +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')
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,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>

View File

@ -0,0 +1,460 @@
<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" :type="type" :item="editItem" />
<!--添加编辑提示词-->
<keywordDialog v-model="isWordDialog" :isAdd="isAdd" :item="editItem" />
</template>
<script setup>
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { tempSave, completion, modelList, removeChildTemp, tempResult } from '@/api/mode/index'
import { sessionStore } from '@/utils/store'
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
*/
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 = ref('课标')
watch(() => props.type, (newVal) => {
if (newVal == 1){
modeType.value = '课标'
}
if (newVal == 2){
modeType.value = '教材'
}
if (newVal == 2){
modeType.value = '考试'
}
}, { immediate: false })
//
const params = reactive(
{
prompt: '',
dataset_id: ''
}
)
//
const againResult = async (index, item) => {
try {
childTempList.value[index].loading = true
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${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}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${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: curTemplate.id,
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()
})
const curNode = reactive({})
onMounted(() => {
getTemplateList()
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
let jsonKey = `${modeType.value}-${data.edustage}-${data.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>

View File

@ -1,26 +1,21 @@
<template> <template>
<div class="page-template flex"> <div class="page-template flex">
<!--头部--> <el-row>
<Header :type="type" @changeTemp="changeTemp" @onRead="onRead"/>
<el-row :gutter="20" class="tempalte-main">
<el-col :span="12"> <el-col :span="12">
<!--左侧pdf--> <Left :curNode="curNode" :type="type" />
<Pdf />
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<!--右侧模板研读--> <Right :curNode="curNode" :type="type" />
<Result ref="resultRef" :modeType="type" :tempId="tempId"/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { reactive, onMounted } from 'vue'
import Header from './container/header.vue' import { sessionStore } from '@/utils/store'
import Pdf from './container/pdf.vue' import Left from './container/left.vue'
import Result from './container/result.vue' import Right from './container/right.vue'
const props = defineProps({ const props = defineProps({
type: { 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.page-template { .page-template {
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
.el-row{
height: 100%;
.el-col{
height: 100%;
}
}
.tempalte-main { .tempalte-main {
flex: 1; flex: 1;
} }

View File

@ -15,7 +15,7 @@
</el-tooltip> </el-tooltip>
</div> </div>
<div class="blockBox"> <div class="blockBox">
<el-button @click="currentType = 'selection'"><el-image src="/src/assets/images/mouse-pointer.png" <el-button @click="currentType = 'selection'"><el-image src="../../../src/assets/images/mouse-pointer.png"
style="width: 14px; height: 14px; color: silver" /></el-button> style="width: 14px; height: 14px; color: silver" /></el-button>
</div> </div>
<template v-if="type == 'design'"> <template v-if="type == 'design'">
@ -123,7 +123,7 @@
<!-- 边框样式 --> <!-- 边框样式 -->
<div class="blockBox"> <div class="blockBox">
<el-dropdown @command="updateStyle('lineDash', $event)" placement="top"> <el-dropdown @command="updateStyle('lineDash', $event)" placement="top">
<el-button><el-image src="/src/assets/images/borderstyle.png" <el-button><el-image src="../../../src/assets/images/borderstyle.png"
style="width: 14px; height: 14px"></el-image></el-button> style="width: 14px; height: 14px"></el-image></el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
@ -145,7 +145,7 @@
<!-- 边框粗细 --> <!-- 边框粗细 -->
<div class="blockBox"> <div class="blockBox">
<el-dropdown @command="updateStyle('lineWidth', $event)" placement="top"> <el-dropdown @command="updateStyle('lineWidth', $event)" placement="top">
<el-button><el-image src="/src/assets/images/borderwidth.png" <el-button><el-image src="../../../src/assets/images/borderwidth.png"
style="width: 14px; height: 14px"></el-image></el-button> style="width: 14px; height: 14px"></el-image></el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>

View File

@ -59,14 +59,14 @@ export const processList = (row, aloneOption=false) => {
for(; j<workDescArr.length; j++){ for(; j<workDescArr.length; j++){
const char = String.fromCharCode(65+j); const char = String.fromCharCode(65+j);
if (aloneOption) { if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`; tmp += `<div class="format-work-desc" style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
} }
else { else {
if(j%2 == 0){ if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`; tmp += `<div style='width:100%;display:flex;'>`;
} }
tmp += `<div style='padding-left:10px;width:50%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`; tmp += `<div class="format-work-desc" style='padding-left:10px;width:50%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){ if(j%2 == 1){
tmp += '</div>'; tmp += '</div>';
} }
@ -153,14 +153,14 @@ export const processList = (row, aloneOption=false) => {
for(; j<optionsArr.length; j++){ for(; j<optionsArr.length; j++){
const char = String.fromCharCode(65+j); const char = String.fromCharCode(65+j);
if (aloneOption) { if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${optionsArr[j]}</div>`; tmp += `<div class="format-work-desc" style='width:100%;display:flex;padding: 2px 0'>${char}.${optionsArr[j]}</div>`;
} }
else { else {
if(j%2 == 0){ if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`; tmp += `<div style='width:100%;display:flex;'>`;
} }
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${optionsArr[j]}</div>`; tmp += `<div class="format-work-desc" style='padding-left: 10px; width: 50%'>${char}.${optionsArr[j]}</div>`;
if(j%2 == 1){ if(j%2 == 1){
tmp += '</div>'; tmp += '</div>';
} }
@ -280,14 +280,14 @@ export const processList = (row, aloneOption=false) => {
for(; j<workDescArr.length; j++){ for(; j<workDescArr.length; j++){
const char = String.fromCharCode(65+j); const char = String.fromCharCode(65+j);
if (aloneOption) { if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`; tmp += `<div class="format-work-desc" style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
} }
else { else {
if(j%2 == 0){ if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`; tmp += `<div style='width:100%;display:flex;'>`;
} }
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${workDescArr[j]}</div>`; tmp += `<div class="format-work-desc" style='padding-left: 10px; width: 50%'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){ if(j%2 == 1){
tmp += '</div>'; tmp += '</div>';
} }

View File

@ -1,16 +1,46 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { } from '@/api/classTask/index.js' import { } from '@/api/classTask/index.js'
import { listClassmain } from '@/api/classManage/index' import { listClassmain } from '@/api/classManage/index'
import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuestion/jyeoo"
const useClassTaskStore = defineStore('classTask',{ const useClassTaskStore = defineStore('classTask',{
state: () => ({ state: () => ({
classListIds: [], classListIds: [],
entpCourseWorkTypeList: [
{value: 0, label: "不限"},
{value: 1, label: "单选题"},
{value: 2, label: "填空题"},
{value: 3, label: "多选题"},
{value: 4, label: "判断题"},
{value: 5, label: "主观题"},
{value: 6, label: "复合题"},
], // 习题查询条件 - 题型
entpCourseWorkGroupList: [{
Key: -1,
Value: '不限',
}, {
Key: 1,
Value: '真题',
}, {
Key: 0,
Value: '非真题',
}
], // 习题查询条件 - 题源
entpCourseWorkYearList: [
{label: '不限', value: '-1'},
{label: '2024', value: '2024'},
{label: '2023', value: '2023'},
{label: '2022', value: '2022'},
{label: '2021', value: '2021'},
{label: '2020', value: '2020'},
], // 习题查询条件 - 年份
}), }),
actions: { actions: {
listClassmain(params) { listClassmain(params) {
// 获取班级列表 // 获取班级列表
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
listClassmain(params) const education = params.edustage + params.edusubject;
listClassmain(education)
.then((res) => { .then((res) => {
this.classListIds = res.rows&&res.rows.map((item) => item.id) this.classListIds = res.rows&&res.rows.map((item) => item.id)
resolve(res) resolve(res)
@ -20,9 +50,58 @@ const useClassTaskStore = defineStore('classTask',{
}) })
}) })
}, },
// 根据学科和学段获取菁优网对应得年份、题源、题型
initJYInfo(params){
const education = params.edustage + params.edusubject;
Promise.all([getJYYear(), getJYSO(education), getJYCT(education)])
.then(results => {
console.log('更新第三方题源+题型succ:', results);
this.entpCourseWorkYearList = results[0];
this.entpCourseWorkTypeList = results[2];
this.entpCourseWorkGroupList = results[1];
})
.catch(error => {
console.error('更新第三方题源+题型err:', error);
});
},
}, },
persist: true persist: true
}) })
export default useClassTaskStore export default useClassTaskStore
const getJYYear = () => {
return new Promise((resolve, reject) => {
JYApiListOriginYear()
.then((res) => {
resolve(res)
})
.catch((error) => {
reject(error)
})
})
}
const getJYSO = (params) => {
return new Promise((resolve, reject) => {
JYApiListSO(params)
.then((res) => {
resolve(res)
})
.catch((error) => {
reject(error)
})
})
}
const getJYCT = (params) => {
return new Promise((resolve, reject) => {
JYApiListCT(params)
.then((res) => {
resolve(res)
})
.catch((error) => {
reject(error)
})
})
}

View File

@ -1,3 +1,5 @@
import { getJYPath } from "@/api/education/entpcoursework";
const JY_TOKEN = 'CA82641DA86072DEFD39E287335E035FDA6AEEC0549B58F54F4408734C8683FFAF0585CFA3B25091E588A03A65C66A80F5FF613F539D600954007A35DFFBFDC3C7BB982771C5E13F0918642CFD7596CE3718F06E5579238D92EC809AC6F4C82A9FE4B0E232A67DD3594D4DAC1C219CCBC4A7A093344446107EB11DC317526D0594249DEBBD82B740C794CF5A7065E1982B7779AF16AD25D7'; const JY_TOKEN = 'CA82641DA86072DEFD39E287335E035FDA6AEEC0549B58F54F4408734C8683FFAF0585CFA3B25091E588A03A65C66A80F5FF613F539D600954007A35DFFBFDC3C7BB982771C5E13F0918642CFD7596CE3718F06E5579238D92EC809AC6F4C82A9FE4B0E232A67DD3594D4DAC1C219CCBC4A7A093344446107EB11DC317526D0594249DEBBD82B740C794CF5A7065E1982B7779AF16AD25D7';
const JY_SUBJECT = [ const JY_SUBJECT = [
{id: 10, subject: 'math3', name: '小学数学'}, {id: 10, subject: 'math3', name: '小学数学'},
@ -26,8 +28,31 @@ const JY_SUBJECT = [
{id: 39, subject: 'history2', name: '高中历史'}, {id: 39, subject: 'history2', name: '高中历史'},
]; ];
/**
* @desc: 获取年份
* @return: {*}
*/
export const JYApiListOriginYear = async () => {
const arrYear = [{label: '不限', value: '-1'}];
let i = 0;
for( ; i < 5; i++) {
const year = new Date().getFullYear();
const s ={
label: `${year - i}`,
value: `${year - i}`,
}
arrYear.push(s);
};
//arrYear.push({label: '更早', value: '0'})
return arrYear;
}
export const JYApiListCT = async (_this, name = '高中历史') => { /**
* @desc: 根据学科+学段获取菁优网-题型
* @return: {*}
* @param {*} name 学科+学科
*/
export const JYApiListCT = async (name = '高中历史') => {
if (name === '初中政治') { if (name === '初中政治') {
name = '初中道德与法治'; name = '初中道德与法治';
} }
@ -35,7 +60,8 @@ export const JYApiListCT = async (_this, name = '高中历史') => {
if(obj.length < 1) { if(obj.length < 1) {
return []; return [];
} }
const res = await _this.$requestGetJYW(`/${obj[0].subject}/common`, { getJYPath
const res = await getJYPath(`/${obj[0].subject}/common`, {
headers: { headers: {
authorization: `Token ${JY_TOKEN}` authorization: `Token ${JY_TOKEN}`
}, },
@ -63,22 +89,13 @@ export const JYApiListCT = async (_this, name = '高中历史') => {
return arrCT; return arrCT;
} }
export const JYApiListOriginYear = () => {
const arrYear = [{label: '不限', value: '-1'}];
let i = 0;
for( ; i < 10; i++) {
const year = new Date().getFullYear();
const s ={
label: `${year - i}`,
value: `${year - i}`,
}
arrYear.push(s);
};
//arrYear.push({label: '更早', value: '0'})
return arrYear;
}
export const JYApiListSO = async (_this, name = '高中历史') => { /**
* @desc: 根据学科+学段获取菁优网-题源
* @return: {*}
* @param {*} name 学科+学科
*/
export const JYApiListSO = async (name = '高中历史') => {
if (name === '初中政治') { if (name === '初中政治') {
name = '初中道德与法治'; name = '初中道德与法治';
} }
@ -87,7 +104,7 @@ export const JYApiListSO = async (_this, name = '高中历史') => {
if(obj.length < 1) { if(obj.length < 1) {
return []; return [];
} }
const res = await _this.$requestGetJYW(`/${obj[0].subject}/common`, { const res = await getJYPath(`/${obj[0].subject}/common`, {
headers: { headers: {
authorization: `Token ${JY_TOKEN}` authorization: `Token ${JY_TOKEN}`
}, },
@ -103,8 +120,12 @@ export const JYApiListSO = async (_this, name = '高中历史') => {
} }
/**
export const JYApiListPoint = async (_this, name = '高中历史') => { * @desc: 根据学科+学段获取菁优网-知识点
* @return: {*}
* @param {*} name 学科+学科
*/
export const JYApiListPoint = async (name = '高中历史') => {
if (name === '初中政治') { if (name === '初中政治') {
name = '初中道德与法治'; name = '初中道德与法治';
} }
@ -113,7 +134,7 @@ export const JYApiListPoint = async (_this, name = '高中历史') => {
if(obj.length < 1) { if(obj.length < 1) {
return []; return [];
} }
const res = await _this.$requestGetJYW(`/${obj[0].subject}/point`, { const res = await getJYPath(`/${obj[0].subject}/point`, {
headers: { headers: {
authorization: `Token ${JY_TOKEN}` authorization: `Token ${JY_TOKEN}`
}, },
@ -126,10 +147,12 @@ export const JYApiListPoint = async (_this, name = '高中历史') => {
/** /**
* @desc: 获取菁优网的版本内容 * @desc: 根据查询条件获取菁优网-教材版本
* @return: {*} * @return: {*}
* @param {*} query {}
* @param {*} hasPoints
*/ */
export const JYApiListVersion = async (_this, query, hasPoints=true) => { export const JYApiListVersion = async (query, hasPoints=true) => {
const listVersion = { const listVersion = {
status: 0, status: 0,
msg: '', msg: '',
@ -148,7 +171,7 @@ export const JYApiListVersion = async (_this, query, hasPoints=true) => {
listVersion.msg = `[${name}]未找到对应菁优网教材版本, 请检查学段或学科是否匹配!`; listVersion.msg = `[${name}]未找到对应菁优网教材版本, 请检查学段或学科是否匹配!`;
return listVersion; return listVersion;
} }
const JYBook = await _this.$requestGetJYW(`/${result[0].subject}/book2`, { const JYBook = await getJYPath(`/${result[0].subject}/book2`, {
headers: { headers: {
// JYToken仅占位, 实际后续已未使用该token // JYToken仅占位, 实际后续已未使用该token
authorization: `Token ${JY_TOKEN}` authorization: `Token ${JY_TOKEN}`

View File

@ -97,4 +97,34 @@ const getProgress = async (id) => {
} }
}; };
export { createOutline, getBackGround, createPPT, getProgress, createByOutline }; const getBackGroundV2 = async () => {
try {
const response = await req("/api/aipptV2/themeListV2", "GET");
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const createPPTV2 = async (data) => {
try {
const response = await req("/api/aipptV2/createV2", "POST", data);
console.log("createOutline response:", response);
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
const getProgressV2 = async (id) => {
try {
const response = await req(`/api/aipptV2/progressV2?sid=${id}`, "GET");
return response.data;
} catch (error) {
console.error("请求失败:", error);
throw error;
}
};
export { createOutline, getBackGround, createPPT, getProgress, getBackGroundV2, createPPTV2, getProgressV2, createByOutline };

View File

@ -117,4 +117,8 @@ export const coursewareTypeList = [
label:'素材', label:'素材',
value:6 value:6
}, },
{
label:'视频',
value:12
},
] ]

View File

@ -416,6 +416,7 @@ const queryPushRecords = (row) => {
// //
console.log(row,'查看该行推送历史') console.log(row,'查看该行推送历史')
pushRecordsOpen.value = true; pushRecordsOpen.value = true;
pushRecordsList.value = [];
pushRecordsLoading.value = true; pushRecordsLoading.value = true;
homeworklist({ homeworklist({
entpcourseid: entpcourseid.value, entpcourseid: entpcourseid.value,
@ -875,21 +876,6 @@ watch(() => courseObj.node, (newVal,oldVal) => {
</script> </script>
<!--
<style>
.el-table .hidden-row {
display: none !important;
/* color: #ccc !important; */
}
.el-table .father-row {
&#45;&#45;el-table-tr-bg-color: #fff;
}
.el-table .son-row {
&#45;&#45;el-table-tr-bg-color: #f0f0f08a;
}
</style>
-->
<style lang="scss" scoped> <style lang="scss" scoped>
.page-classTaskAssign { .page-classTaskAssign {
padding-top: 10px; padding-top: 10px;

View File

@ -294,8 +294,8 @@ import FileUpload from "@/components/FileUpload/index.vue";
import whiteboard from '@/components/whiteboard/whiteboard.vue' import whiteboard from '@/components/whiteboard/whiteboard.vue'
import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue' import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue'
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue' import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuestion/jyeoo"
import useClassTaskStore from '@/store/modules/classTask'
import {throttle,debounce } from '@/utils/comm' import {throttle,debounce } from '@/utils/comm'
import { useToolState } from '@/store/modules/tool' import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
@ -303,6 +303,11 @@ const userStore = useUserStore().user
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const router = useRouter() const router = useRouter()
const toolStore = useToolState() const toolStore = useToolState()
const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const props = defineProps({ const props = defineProps({
bookobj: { bookobj: {
@ -326,39 +331,11 @@ const props = defineProps({
const prevReadMsgDialogRef = ref(null);// ref const prevReadMsgDialogRef = ref(null);// ref
const classWorkFormRef = ref(null); const classWorkFormRef = ref(null);
const entpCourseWorkTypeList = ref([
{value: 0, label: "不限"},
{value: 1, label: "单选题"},
{value: 2, label: "填空题"},
{value: 3, label: "多选题"},
{value: 4, label: "判断题"},
{value: 5, label: "主观题"},
{value: 6, label: "复合题"},
]); // -
const entpCourseWorkGroupList = ref([{
Key: -1,
Value: '不限',
}, {
Key: 1,
Value: '真题',
}, {
Key: 0,
Value: '非真题',
}]); // -
const entpCourseWorkPointList = ref([ const entpCourseWorkPointList = ref([
{label: '不限', value: []}, {label: '不限', value: []},
]); // - ]); // -
const knowledgePointProps = ref({value: 'thirdId', label: 'title'}); const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
const entpCourseWorkYearList =ref([
{label: '不限', value: '-1'},
{label: '2024', value: '2024'},
{label: '2023', value: '2023'},
{label: '2022', value: '2022'},
{label: '2021', value: '2021'},
{label: '2020', value: '2020'},
]); // -
const paginationParams = reactive({ const paginationParams = reactive({
@ -1006,18 +983,6 @@ const initPageParams = () => {
onMounted(async() => { onMounted(async() => {
//
const name = userStore.edustage + userStore.edusubject;
const jyCT = await JYApiListCT(proxy, name);
if (jyCT.length == 0) {
ElMessage.error('获取题型失败!');
return;
}
entpCourseWorkTypeList.value = jyCT;
//
entpCourseWorkYearList.value = JYApiListOriginYear();
entpCourseWorkGroupList.value = await JYApiListSO(proxy, name);
}) })
// const refreshData = () => { // const refreshData = () => {

View File

@ -29,11 +29,11 @@ import { ElMessage } from 'element-plus'
const emit = defineEmits(['itemClick']) const emit = defineEmits(['itemClick'])
const items = shallowRef([ const items = shallowRef([
{ title: '自主搜题', description: '上千万高质量习题资源,历届考试真题,每道题均有习题解析', icon: '#icon-soutibao-',type:'default' }, { title: '自主搜题', description: '上千万高质量习题资源,历届考试真题,每道题均有习题解析', icon: '#icon-soutibao-',type:'primary' },
{ title: '校本题库', description: '本校公共题库资源。', icon: '#icon-soutibao-',type:'default' }, { title: '校本题库', description: '本校公共题库资源。', icon: '#icon-soutibao-',type:'primary' },
{ title: '个人题库', description: '老师上传维护自己的个人题库。', icon: '#icon-soutibao-',type:'default' }, { title: '个人题库', description: '老师上传维护自己的个人题库。', icon: '#icon-soutibao-',type:'primary' },
{ title: '智能推荐', description: '通过对学生的薄弱知识点分析,推送不同难度的习题进行强化训练。', icon: '#icon-tubiao_wuxing-',type:'default' }, { title: '智能推荐', description: '通过对学生的薄弱知识点分析,推送不同难度的习题进行强化训练。', icon: '#icon-tubiao_wuxing-',type:'primary' },
{ title: '课堂展示', description: '通过课堂白板绘制作业,提升学生的创作思维能力。', icon: '#icon-huaban',type:'primary' }, { title: '课堂展示', description: '通过课堂白板绘制作业,提升学生的创作思维能力。', icon: '#icon-huaban',type:'danger' },
{ title: '常规作业', description: '推送pdf、视频、音频、图片学生可以拍照上传。', icon: '#icon-zhaoxiangji',type:'danger' }, { title: '常规作业', description: '推送pdf、视频、音频、图片学生可以拍照上传。', icon: '#icon-zhaoxiangji',type:'danger' },
{ title: 'AI设计作业', description: '通过AI助手根据课标、教材、考试等分析结果智能创建作业。', icon: '#icon-jiqiren_o',type:'danger' }, { title: 'AI设计作业', description: '通过AI助手根据课标、教材、考试等分析结果智能创建作业。', icon: '#icon-jiqiren_o',type:'danger' },
{ title: '习题上传', description: '自己上传个人题库。', icon: '#icon-shangchuan',type:'danger' }, { title: '习题上传', description: '自己上传个人题库。', icon: '#icon-shangchuan',type:'danger' },

View File

@ -104,7 +104,7 @@
<div v-if="classWorkForm.worktype == '习题训练'" class="pageRight-list"> <div v-if="classWorkForm.worktype == '习题训练'" class="pageRight-list">
<div :style="{height: '100%', 'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}"> <div :style="{height: '100%', 'overflow': 'auto', 'border':'1px dotted blue','border-radius':'5px', 'background-color': '#f7f7f7'}">
<template v-for="(item,index) in classWorkForm.quizlist" :key="item.id"> <template v-for="(item,index) in classWorkForm.quizlist" :key="item.id">
<div style="margin: 5px; background-color: white"> <div style="margin: 5px; background-color: white; text-align: left;">
<div v-html="item.titleFormat" style="padding: 15px 20px 5px 20px"></div> <div v-html="item.titleFormat" style="padding: 15px 20px 5px 20px"></div>
<div style="display: flex;"> <div style="display: flex;">
<el-form-item label="分值"> <el-form-item label="分值">
@ -126,10 +126,10 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue' import { onMounted, ref, watch, reactive, getCurrentInstance, nextTick } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { Plus } from '@element-plus/icons-vue' import { Plus, Delete } from '@element-plus/icons-vue'
import { delClasswork } from '@/api/teaching/classwork' import { delClasswork } from '@/api/teaching/classwork'
import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork' import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
import { addClassworkReturnId } from '@/api/teaching/classwork' import { addClassworkReturnId } from '@/api/teaching/classwork'
@ -144,10 +144,6 @@ import whiteboard from '@/components/whiteboard/whiteboard.vue'
import FileUpload from "@/components/FileUpload/index.vue"; import FileUpload from "@/components/FileUpload/index.vue";
import Right from './Right/index.vue' import Right from './Right/index.vue'
import {
Delete
} from '@element-plus/icons-vue'
import SetHomework from '@/components/set-homework/index.vue' import SetHomework from '@/components/set-homework/index.vue'
import { useGetHomework } from '@/hooks/useGetHomework' import { useGetHomework } from '@/hooks/useGetHomework'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'

View File

@ -125,16 +125,26 @@ import { delEntpcoursework, updateEntpcoursework } from "@/api/education/entpCou
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue' import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
import QuesItem from "@/views/classTask/newClassTaskAssign/questionUpload/quesItem/index.vue"; import QuesItem from "@/views/classTask/newClassTaskAssign/questionUpload/quesItem/index.vue";
import { useHandleData } from "@/hooks/useHandleData"; import { useHandleData } from "@/hooks/useHandleData";
import { processList } from '@/hooks/useProcessList' import { processList } from '@/hooks/useProcessList';
import { debounce } from '@/utils/comm' import { debounce } from '@/utils/comm'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useClassTaskStore from '@/store/modules/classTask'
const router = useRouter() const router = useRouter()
// emit // emit
const emit = defineEmits(['addQuiz']) const emit = defineEmits(['addQuiz'])
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const userStore = useUserStore().user const userStore = useUserStore().user
const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const props = defineProps({ const props = defineProps({
bookobj: { bookobj: {
type: Object, type: Object,
@ -142,37 +152,7 @@ const props = defineProps({
}, },
}) })
const entpCourseWorkTypeList = ref([
{value: 0, label: "不限"},
{value: 1, label: "单选题"},
{value: 2, label: "填空题"},
{value: 3, label: "多选题"},
{value: 4, label: "判断题"},
{value: 5, label: "主观题"},
{value: 6, label: "复合题"},
]); // -
const entpCourseWorkGroupList = ref([{
Key: -1,
Value: '不限',
}, {
Key: 1,
Value: '真题',
}, {
Key: 0,
Value: '非真题',
}]); // -
const knowledgePointProps = ref({value: 'thirdId', label: 'title'}); const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
const entpCourseWorkYearList =ref([
{label: '不限', value: '-1'},
{label: '2024', value: '2024'},
{label: '2023', value: '2023'},
{label: '2022', value: '2022'},
{label: '2021', value: '2021'},
{label: '2020', value: '2020'},
]); // -
// //
@ -220,7 +200,7 @@ const dlgImportSingle = reactive({
onMounted(() => { onMounted(() => {
debounceQueryData(); // debounceQueryData(); //
}) })
/** 前往习题上传页面 */
const goToQuestUpload = () => { const goToQuestUpload = () => {
router.push({ path: '/model/questionUpload', query: { courseObj: JSON.stringify(props.bookobj) } }); router.push({ path: '/model/questionUpload', query: { courseObj: JSON.stringify(props.bookobj) } });
} }
@ -336,7 +316,7 @@ const handleQueryFromEntpCourseWork= async (queryType) => {
} else if (clueres.rows[i].cluetag == 'mapview') { } else if (clueres.rows[i].cluetag == 'mapview') {
clueres.rows[i].worktype = '学科定位'; clueres.rows[i].worktype = '学科定位';
} }
console.log("clueres.rows[i].childlist",clueres.rows[i].childlist); //console.log("clueres.rows[i].childlist",clueres.rows[i].childlist);
if (clueres.rows[i].childlist != '') { if (clueres.rows[i].childlist != '') {
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']'); clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
for (var j=0; j<clueres.rows[i].childArray.length; j++) { for (var j=0; j<clueres.rows[i].childArray.length; j++) {
@ -488,6 +468,10 @@ watch(() => props.bookobj.levelSecondId, (newVal, oldVal) => {
width: 100%; width: 100%;
height: calc(100% - 100px); height: calc(100% - 100px);
} }
:deep(.format-work-desc > :is(div):first-child){
display: inline-block;
}
// :deep(.el-dialog .question-dialog){ // :deep(.el-dialog .question-dialog){
// height: 80vh !important; // height: 80vh !important;

View File

@ -99,6 +99,8 @@ const router = useRouter()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const props = defineProps({ const props = defineProps({
}) })
const curLessionList = ref([]);
const activeParams = reactive({ const activeParams = reactive({
version: {}, version: {},
lession: {}, lession: {},

View File

@ -8,7 +8,7 @@
<el-form ref="questFormRef" :model="questForm" :rules="MainRules" label-width="80px"> <el-form ref="questFormRef" :model="questForm" :rules="MainRules" label-width="80px">
<el-form-item label="题型" prop="worktype"> <el-form-item label="题型" prop="worktype">
<el-select v-model="questForm.worktype" placeholder="请选择题型" style="width:20%" :disabled="questForm.id==0?false:true"> <el-select v-model="questForm.worktype" placeholder="请选择题型" style="width:20%" :disabled="questForm.id==0?false:true">
<el-option v-for="item in fromOptions.type" :key="item.Key" :label="item.Value" :value="item.Value"></el-option> <el-option v-for="item in fromOptions.type" :key="item.value" :label="item.label" :value="item.label"></el-option>
</el-select> </el-select>
<el-tag v-if="questForm.worktype=='填空题'" type="danger" style=" margin-left: 10px ">温馨提示填空题题目的填空位置下划线请连续输入3-10个 _ <el-tag v-if="questForm.worktype=='填空题'" type="danger" style=" margin-left: 10px ">温馨提示填空题题目的填空位置下划线请连续输入3-10个 _
符号eg今天___好日子</el-tag> 符号eg今天___好日子</el-tag>
@ -405,15 +405,24 @@
<script setup> <script setup>
import { ElMessage, ElNotification } from 'element-plus' import { ElMessage, ElNotification } from 'element-plus'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue' import { onMounted, ref, watch, reactive, getCurrentInstance,nextTick } from 'vue'
import { getBindlist } from '@/api/education/knowledgePoint' import { getBindlist, listKnowlegepointFormat } from '@/api/education/knowledgePoint'
import { listEvaluation } from '@/api/subject'
import { listEntpcoursework, getEntpcoursework, delEntpcoursework, addEntpcoursework, updateEntpcoursework, uploadEntpcourseworkFile } from "@/api/education/entpCourseWork"; import { listEntpcoursework, getEntpcoursework, delEntpcoursework, addEntpcoursework, updateEntpcoursework, uploadEntpcourseworkFile } from "@/api/education/entpCourseWork";
import { isJson } from "@/hooks/useProcessList"; import { isJson } from "@/hooks/useProcessList";
import Tinymce from "@/components/tinymce/tinymce.vue"; // import Tinymce from "@/components/tinymce/tinymce.vue"; //
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useClassTaskStore from '@/store/modules/classTask'
const userStore = useUserStore().user const userStore = useUserStore().user
const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
// emit // emit
const emit = defineEmits(['submit-exam-single-callback','cancel-exam-single-callback','cropper-exam-form-item']) const emit = defineEmits(['submit-exam-single-callback','cancel-exam-single-callback','cropper-exam-form-item'])
@ -449,12 +458,13 @@ const fromOptions = reactive({
// //
type: [ type: [
//{"Key": 0, "Value": ""}, //{"Key": 0, "Value": ""},
{"Key": 1, "Value": "单选题"}, {value: 0, label: "不限"},
{"Key": 4, "Value": "多选题"}, {value: 1, label: "单选题"},
{"Key": 2, "Value": "填空题"}, {value: 2, label: "填空题"},
{"Key": 5, "Value": "判断题"}, {value: 3, label: "多选题"},
{"Key": 6, "Value": "主观题"}, {value: 4, label: "判断题"},
{"Key": 3, "Value": "复合题"}, {value: 5, label: "主观题"},
{value: 6, label: "复合题"},
//{"Key": 4, "Value": ""}, //{"Key": 4, "Value": ""},
], ],
// //
@ -475,7 +485,7 @@ const questForm = reactive({
id: 0, id: 0,
title: '', title: '',
worktype: '单选题', worktype: '单选题',
worktagYear: '2024', worktagYear: 2024,
//worktagArea: '', //worktagArea: '',
worktag: '', worktag: '',
workgroup: 0, workgroup: 0,
@ -601,8 +611,8 @@ onMounted(() => {
lessionId: lessionid, lessionId: lessionid,
fileAlias: '单题上传', fileAlias: '单题上传',
}; };
// //
// yearList =
for(var i = 0; i < 15; i++) { for(var i = 0; i < 15; i++) {
const year = new Date().getFullYear(); const year = new Date().getFullYear();
const s ={ const s ={
@ -611,10 +621,23 @@ onMounted(() => {
} }
yearList.value.push(s) yearList.value.push(s)
}; };
//
if (entpCourseWorkTypeList.length>0) {
const flagDict = ['单选题', '多选题', '判断题', '填空题'];
fromOptions.type = entpCourseWorkTypeList.filter(item => flagDict.includes(item.label));
}
if (entpCourseWorkGroupList.length>0) {
fromOptions.flag = entpCourseWorkGroupList;
}
// if (entpCourseWorkYearList.length>0) {
// yearList.value = entpCourseWorkYearList;
// }
}) })
const checkBoxChange=(v)=>{ const checkBoxChange=(v)=>{
// v: [] // v: []
console.log(v,'vvvvvv') console.log(v,'vvvvvv')
@ -986,16 +1009,26 @@ const updateForm= async(item, submitIndex=0, submitType=1) =>{
lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId; lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId;
console.log('lessionid', lessionid.value); console.log('lessionid', lessionid.value);
// //
const res = await getBindlist({ eid: lessionid.value }) if( props.bookobj.node.edustage == '高中' && (props.bookobj.node.edusubject == '语文' || props.bookobj.node.edusubject == '英语') ){
if (!res.data || res.data.length < 1) { const res = await listEvaluation({ edusubject: props.bookobj.node.edusubject, edustage: props.bookobj.node.edustage, itemkey: "subject", pageSize: 10 });
ElMessage.warning('当前章节下未绑定知识点,暂不更新该试题知识点!'); const id = res.rows[0]?.id;
curKnowledgePointList.value = []; if (id) {
const res = await listKnowlegepointFormat({evalId: id, pageNum: 1, pageSize: 5000,});
curKnowledgePointList.value = updateKnowledgePoint(res.rows);
//console.log('updateKnowledgePoint->', res.rows);
}
} else {
const res = await getBindlist({ eid: lessionid.value })
if (!res.data || res.data.length < 1) {
ElMessage.warning('当前章节下未绑定知识点,暂不更新该试题知识点!');
curKnowledgePointList.value = [];
}
else {
curKnowledgePointList.value = res.data;
}
} }
else {
curKnowledgePointList.value = res.data;
}
} }
//item.evalnodeid = '3772b,374112,374233'; //item.evalnodeid = '3772b,374112,374233';
@ -1666,10 +1699,56 @@ const myMessageShow=(title, msg, status)=>{
// }, // },
// }, // },
watch(() => props.bookobj.levelSecondId, (newVal, oldVal) => { /**
* @desc: 遍历原知识点数据, 将title字段转为knowTitle以供knowledgePointProps进行tree的格式转换显示
* @return: {*}
* @param {*} list
*/
const updateKnowledgePoint = (list) => {
list.forEach(item => {
if (item.title && item.title != '') {
item.knowTitle = item.title;
}
if (item.children && Array.isArray(item.children)) {
updateKnowledgePoint(item.children);
}
});
return list;
};
watch(() => props.bookobj.levelSecondId, async (newVal, oldVal) => {
console.log(props.bookobj,'课程选择') console.log(props.bookobj,'课程选择')
// props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId // props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId
lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId; lessionid.value = props.bookobj.levelSecondId? props.bookobj.levelSecondId : props.bookobj.levelFirstId;
/**
* 格式化知识点: 分两种情况
* 1. 语文/英语: 获取学科下的所有知识点(该学科对应无章节与知识点绑定, 故只获取全知识点)
* 2. 其他: 获取当前章节下的所有知识点
*/
let id = props.bookobj.levelSecondId;
if( props.bookobj.node.edustage == '高中' && (props.bookobj.node.edusubject == '语文' || props.bookobj.node.edusubject == '英语') ){
id = props.bookobj.node.rootid;
const res = await listEvaluation({ edusubject: props.bookobj.node.edusubject, edustage: props.bookobj.node.edustage, itemkey: "subject", pageSize: 10 });
id = res.rows[0]?.id;
if (id) {
listKnowlegepointFormat({evalId: id, pageNum: 1, pageSize: 5000,}).then(res => {
//console.log('listKnowlegepointFormat->', res.rows);
curKnowledgePointList.value = updateKnowledgePoint(res.rows);
});
}
}else{
getBindlist({ eid: id }).then(res => {
if (!res.data || res.data.length < 1) {
ElMessage.warning('当前章节下未绑定知识点,暂不更新该试题知识点!');
curKnowledgePointList.value = [];
}
else {
curKnowledgePointList.value = res.data;
}
})
}
}) })
@ -1680,7 +1759,20 @@ defineExpose({
cropperFormItemCallBack, cropperFormItemCallBack,
}) })
</script> </script>
<style lang="scss">
.el-cascader-panel {
li[aria-haspopup="true"] {
.el-checkbox {
display: none;
}
}
}
</style>
<style scoped lang="scss"> <style scoped lang="scss">
.questForm-item-cover{ .questForm-item-cover{
position: relative; position: relative;
@ -1698,6 +1790,4 @@ defineExpose({
z-index: 9999; z-index: 9999;
} }
} }
</style> </style>

View File

@ -20,6 +20,7 @@
<el-col :span="10"> <el-col :span="10">
<el-form-item label="知识点" label-width="70"> <el-form-item label="知识点" label-width="70">
<el-cascader <el-cascader
disabled
v-model="entpCourseWorkQueryParams.point" v-model="entpCourseWorkQueryParams.point"
clearable clearable
style="width: 100%" style="width: 100%"
@ -118,11 +119,18 @@ import { useGetHomework } from '@/hooks/useGetHomework'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import {throttle,debounce } from '@/utils/comm' import {throttle,debounce } from '@/utils/comm'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useClassTaskStore from '@/store/modules/classTask'
// emit // emit
const emit = defineEmits(['addQuiz']) const emit = defineEmits(['addQuiz'])
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const userStore = useUserStore().user const userStore = useUserStore().user
const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const props = defineProps({ const props = defineProps({
bookobj: { bookobj: {
type: Object, type: Object,
@ -130,41 +138,11 @@ const props = defineProps({
}, },
}) })
const entpCourseWorkTypeList = ref([
{value: 0, label: "不限"},
{value: 1, label: "单选题"},
{value: 2, label: "填空题"},
{value: 3, label: "多选题"},
{value: 4, label: "判断题"},
{value: 5, label: "主观题"},
{value: 6, label: "复合题"},
]); // -
const entpCourseWorkGroupList = ref([{
Key: -1,
Value: '不限',
}, {
Key: 1,
Value: '真题',
}, {
Key: 0,
Value: '非真题',
}]); // -
const entpCourseWorkPointList = ref([ const entpCourseWorkPointList = ref([
{label: '不限', value: []}, {label: '不限', value: []},
]); // - ]); // -
const knowledgePointProps = ref({value: 'thirdId', label: 'title'}); const knowledgePointProps = ref({value: 'thirdId', label: 'title'});
//const knowledgePointProps = ref({value: 'thirdId', label: 'knowTitle'}); //const knowledgePointProps = ref({value: 'thirdId', label: 'knowTitle'});
const entpCourseWorkYearList =ref([
{label: '不限', value: '-1'},
{label: '2024', value: '2024'},
{label: '2023', value: '2023'},
{label: '2022', value: '2022'},
{label: '2021', value: '2021'},
{label: '2020', value: '2020'},
]); // -
// //
@ -342,7 +320,7 @@ const handleQueryFromEntpCourseWork= async (queryType) => {
} else if (clueres.rows[i].cluetag == 'mapview') { } else if (clueres.rows[i].cluetag == 'mapview') {
clueres.rows[i].worktype = '学科定位'; clueres.rows[i].worktype = '学科定位';
} }
console.log("clueres.rows[i].childlist",clueres.rows[i].childlist); //console.log("clueres.rows[i].childlist",clueres.rows[i].childlist);
if (clueres.rows[i].childlist != '') { if (clueres.rows[i].childlist != '') {
clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']'); clueres.rows[i].childArray = JSON.parse('['+clueres.rows[i].childlist+']');
for (var j=0; j<clueres.rows[i].childArray.length; j++) { for (var j=0; j<clueres.rows[i].childArray.length; j++) {

View File

@ -58,6 +58,11 @@ import { useGetSubject } from '@/hooks/useGetSubject'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import useUserStore from '@/store/modules/user'
import useClassTaskStore from '@/store/modules/classTask'
const userStore = useUserStore()
const classTaskStore = useClassTaskStore();
const router = useRouter() const router = useRouter()
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
const chartDom = ref(null); const chartDom = ref(null);
@ -206,11 +211,14 @@ ipcRenderer.on('minWinResize', debounce((e, data) =>{
}, 100)) }, 100))
onMounted(async ()=>{ onMounted(async ()=>{
await useGetSubject() await useGetSubject()
// DOM // DOM
await nextTick() await nextTick()
chartInstance = echarts.init(chartDom.value) chartInstance = echarts.init(chartDom.value)
//
await classTaskStore.initJYInfo(userStore.user);
const option = { const option = {
tooltip: { tooltip: {

View File

@ -187,7 +187,7 @@ const getWorkType = async (data) => {
if (selName === curName) { if (selName === curName) {
return; return;
} }
const jyCT = await JYApiListCT(proxy, selName); const jyCT = await JYApiListCT(selName);
if (jyCT.length == 0) { if (jyCT.length == 0) {
ElMessage.error('获取题型失败!'); ElMessage.error('获取题型失败!');
return; return;

View File

@ -60,6 +60,7 @@ import { sessionStore } from '@/utils/store'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { dataSetJson } from '@/utils/comm.js' import { dataSetJson } from '@/utils/comm.js'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import emitter from '@/utils/mitt';
const userInfo = useUserStore().user const userInfo = useUserStore().user
const textarea = ref('') const textarea = ref('')
@ -119,7 +120,7 @@ const getCompletion = async (val) => {
type: 'robot', type: 'robot',
msg: answer, msg: answer,
}) })
} finally { } finally {
loaded.value = false loaded.value = false
} }
@ -132,6 +133,8 @@ const saveAdjust = (item) =>{
} }
const curFile = reactive({}) const curFile = reactive({})
const dataset_id = ref('') const dataset_id = ref('')
const fileList = ref([]) const fileList = ref([])
@ -144,7 +147,9 @@ const getList = () =>{
Object.assign(curFile, fileList.value[0]) Object.assign(curFile, fileList.value[0])
}) })
} }
emitter.on('curFile', (item) =>{
changeFile(item)
})
const changeFile = (val) =>{ const changeFile = (val) =>{
Object.assign(curFile, val); Object.assign(curFile, val);
@ -154,8 +159,8 @@ const changeFile = (val) =>{
onMounted(() => { onMounted(() => {
let data = sessionStore.get('subject.curNode') let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data); Object.assign(curNode, data);
let text = props.modeType == 1 ? '课标': props.modeType == 2 ? '教材' : '考试' let text = props.modeType == 1 ||props.modeType == 2 ? '课标' : '考试'
let jsonKey = `${text}-${data.edustage}-${data.edusubject}` let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey] params.dataset_id = dataSetJson[jsonKey]
dataset_id.value = dataSetJson[jsonKey] dataset_id.value = dataSetJson[jsonKey]
@ -190,7 +195,7 @@ onMounted(() => {
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
flex-direction: column; flex-direction: column;
} }
.chart-item { .chart-item {
border-radius: 5px; border-radius: 5px;
@ -300,4 +305,4 @@ onMounted(() => {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
} }
</style> </style>

View File

@ -8,16 +8,16 @@
</template> </template>
<div class="dialog-content"> <div class="dialog-content">
<div class="flex dialog-top"> <div class="flex dialog-top">
<el-radio-group v-model="radio" @change="changeRadio"> <!-- <el-radio-group v-model="radio" @change="changeRadio">
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio> <el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
</el-radio-group> </el-radio-group> -->
</div> </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' : ''" @click="clickItem(index, item)">
<el-image class="img" :src="item.url" /> <el-image class="img" :src="url" />
<span>{{ item.name }}</span> <el-text truncated>{{ item.fileName }}</el-text>
</li> </li>
</ul> </ul>
</div> </div>
@ -47,6 +47,7 @@ import { sessionStore } from '@/utils/store'
import { dataSetJson } from '@/utils/comm.js' import { dataSetJson } from '@/utils/comm.js'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import emitter from '@/utils/mitt';
const userInfo = useUserStore().user const userInfo = useUserStore().user
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
@ -93,11 +94,7 @@ const changeRadio = () => {
}) })
} }
} }
const activeIndex = ref(-1) const activeIndex = ref(0)
const clickItem = (index) => {
activeIndex.value = index
}
const dataset_id = ref('') const dataset_id = ref('')
@ -109,7 +106,7 @@ const onSuccess = async (response) =>{
dataset_id: dataset_id.value dataset_id: dataset_id.value
} }
const res = await completion(data) const res = await completion(data)
console.log(res)
if(res.data.code != 200) return if(res.data.code != 200) return
let docData = { let docData = {
fileUrl: response.url, fileUrl: response.url,
@ -123,19 +120,30 @@ const onSuccess = async (response) =>{
} }
const { msg } = await addDoc(docData) const { msg } = await addDoc(docData)
ElMessage.success(msg) ElMessage.success(msg)
getList()
} }
const curNode = reactive({}) const curNode = reactive({})
const fileList = ref([])
const curFile = reactive({})
const getList = () =>{ const getList = () =>{
docList({ docList({
userId: userInfo.userId, userId: userInfo.userId,
dataset_id: dataset_id.value dataset_id: dataset_id.value
}).then( res =>{ }).then( res =>{
console.log(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)
}
onMounted(() =>{ onMounted(() =>{
let data = sessionStore.get('subject.curNode') let data = sessionStore.get('subject.curNode')
@ -174,6 +182,7 @@ onMounted(() =>{
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;

View File

@ -257,7 +257,7 @@ const removeItem = async (item) => {
isAdd.value = false isAdd.value = false
Object.assign(keywordItem, item) Object.assign(keywordItem, item)
isEditKeyWord.value = true isEditKeyWord.value = true
} }
// const { msg } = await removeChildTemp(item.id) // const { msg } = await removeChildTemp(item.id)
@ -269,7 +269,7 @@ const removeItem = async (item) => {
onMounted(() => { onMounted(() => {
let data = sessionStore.get('subject.curNode') let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data); Object.assign(curNode, data);
let text = props.modeType == 1 ? '课标' : props.modeType == 2 ? '教材' : '考试' let text = props.modeType == 1 || props.modeType == 2? '课标' : '考试'
let jsonKey = `${text}-${data.edustage}-${data.edusubject}` let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey] params.dataset_id = dataSetJson[jsonKey]

View File

@ -0,0 +1,378 @@
<template>
<div class="ai-container">
<el-steps style="max-width:100% " :active="activeStep" align-center>
<el-step title="生成大纲" />
<el-step title="选择模板" />
<el-step title="制作PPT" />
</el-steps>
<div class="card-box">
<el-card class="card2" v-if="activeStep === 0">
<div class="paragraphs">
{{ outputText }}
</div>
<el-button style="margin-bottom: 5px;" type="primary" @click="addMessage">从新生成</el-button>
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 1">下一步</el-button>
</el-card>
<el-card v-if="activeStep === 1">
<div style="padding-bottom: 10px">ppt模板选择</div>
<div class="themes">
<div v-for="item in backGroundList" :key="item.key" :style="{
padding: '20px',
paddingRight: '30px',
paddingLeft: '30px',
margin: '10px',
backgroundColor: getBackgroundColor(item.key),
borderRadius: '10px',
borderBlock: '10px solid #e6e6e6'
}" @click="chooseBackground(item.key)" @mouseenter="changeCursor('pointer')" @mouseleave="changeCursor('default')">
{{ item.name }}
<br />
<img style="width: 150px; height: auto" :src="item.thumbnail" alt="" />
</div>
</div>
<el-row class="el-row">
<el-col :span="6" class="el-col">
<div class="grid-content-1">
<div>自动配图</div>
<el-switch v-model="outlineData.is_figure" />
</div>
</el-col>
<el-col :span="6" class="el-col">
<div class="grid-content-2">
<div>PPT作者名</div>
<el-input v-model="outlineData.author" style="width: 50%" />
</div>
</el-col>
</el-row>
<div>
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 0">上一步</el-button>
<el-button style="margin-bottom: 5px;" type="primary" @click="outlineCreatePPT()">生成PPT</el-button>
</div>
</el-card>
<el-card v-if="activeStep === 2">
<el-progress :percentage="percentage" type="circle" ></el-progress>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { creatAIPPT } from '@/utils/talkFile'
import { ElMessage } from 'element-plus'
import {
getBackGround,
createPPT,
getProgress,
} from "@/utils/ppt-request.js";
import CryptoJS from "crypto-js"
import { getSignature } from "@/utils/index.js";
let appId = "01ec9aa3";
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
let apikey = "39d05b269fa229f431a56c21794a8ea5"
let timestamp = Math.floor(Date.now() / 1000);
let signature = getSignature(appId, secret, timestamp);
const { ipcRenderer } = window.electron || {}
const outputText = ref(""); //
const stagingData = ref([]); //
const stagOutputText = ref(""); //
let extractedParts = ref([]) //
let firstArray = ref([]); //
let secondArray = ref([]); //
const backGroundList = ref([]);
const inputTheme = ref("高中语文《沁园春雪》的授课课件"); //
const inputRequire = ref("") //
const activeStep = ref(0); //
const combined = ref('') // ppt
const treeData = ref([]);
const status = ref("init");
const percentage = ref(0);
const getBackground = () => {
treeData.value = [];
getBackGround().then((res) => {
console.log(res);
backGroundList.value = res;
});
};
const getBackgroundColor = (key) => {
return outlineData.value.theme === key ? '#83e2b6' : '#f5f5f5';
};
const outlineData = ref({
query: '', // 8000
theme: 'auto', // ppt
author: 'AIX平台',
is_card_note: false, // ppt
is_cover_img: false, //
is_figure: false, //
}
)
const emit = defineEmits(['addSuccess'])
const props = defineProps({
dataList: {
type: Array,
default: () => []
}
})
//
function updateStagingData(role, newData) {
stagingData.value.push({ role: role, content: newData });
}
//ppt
const outlineCreatePPT = () => {
const newOutlineData = { ...outlineData.value, };
newOutlineData.query = outputText.value;
createPPT(newOutlineData).then((res) => {
console.log(res, "正在生成中");
activeStep.value = 2
const checkProgress = () => {
getProgress(res.sid).then((response) => {
percentage.value = response.process;
if (response && response.pptUrl && response.pptUrl.length > 4) {
// window.location.href = response.data.pptUrl;
//URLURL
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFiledf28bf990a4c40ffb7477ed4b65392c27232357022409613439/%E3%80%8A%E9%9D%99%E5%A5%B3%E3%80%8B%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BB%E4%B8%8E%E7%A0%94%E7%A9%B6.pptx"
emit('addSuccess',res)
ElMessage.success("生成成功");
} else {
const sleepTime = 2000;
let remainingTime = sleepTime;
const intervalId = setInterval(() => {
remainingTime -= 100;
if (remainingTime <= 0) {
clearInterval(intervalId);
checkProgress();
}
}, 100);
}
});
};
checkProgress();
})
};
//
const addMessage = () => {
const themeValue = inputTheme.value;
const requireValue = inputRequire.value;
firstArray.value = []
secondArray.value = []
extractedParts.value = []
stagOutputText.value = ''
const combinedString = `请帮我生成一个ppt大纲主题为${themeValue}。具体内容要求为:${requireValue}。注意用三个等级大纲展示如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
updateStagingData("user", combinedString);
connectWebSocket(stagingData.value);
};
let ttsWS
function connectWebSocket(data) {
outputText.value = ""; //
status.value = "ttsing";
return getWebsocketUrl().then((url) => {
ttsWS = new WebSocket(url);
ttsWS.onopen = () => {
webSocketSend(ttsWS, data);
};
ttsWS.onmessage = (e) => {
result1(e.data);
};
ttsWS.onerror = (e) => {
status.value = "error";
console.log("WebSocket error:", e);
};
ttsWS.onclose = () => {
status.value = "init";
};
});
}
function getWebsocketUrl() {
return new Promise((resolve, reject) => {
var apiKey = apikey;
var apiSecret = secret;
var url = "wss://spark-api.xf-yun.com/v4.0/chat";
var host = "spark-api.xf-yun.com";
var date = new Date().toGMTString();
var algorithm = "hmac-sha256";
var headers = "host date request-line";
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v4.0/chat HTTP/1.1`;
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
var authorization = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(authorizationOrigin)
);
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
console.log(url);
resolve(url);
});
}
function webSocketSend(ws, data) {
const params = {
header: {
app_id: appId,
},
parameter: {
chat: {
domain: "4.0Ultra",
temperature: 0.5,
max_tokens: 1024,
},
},
payload: {
message: {
text: data,
},
},
};
ws.send(JSON.stringify(params));
}
function result1(resultData) {
let jsonData = JSON.parse(resultData);
outputText.value += jsonData.payload.choices.text[0].content;
const div = document.querySelector('.paragraphs');
if (div) {
div.scrollTop = div.scrollHeight;
}
if (jsonData.payload && jsonData.payload.usage) {
startExtraction() //
console.log(firstArray.value, secondArray.value)
activeStep.value = 2
updateStagingData("assistant", outputText.value) //
}
if (jsonData.header.code !== 0) {
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
}
}
const chooseBackground = (data) => {
outlineData.value.theme = data
}
const changeCursor = (cursorStyle) => {
document.documentElement.style.cursor = cursorStyle;
};
onMounted(() => {
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx"
// creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => {
// emit('addSuccess',res)
// })
// connectWebSocket("");
props.dataList.filter(item => {
inputRequire.value += item.answer
})
getBackground();
});
</script>
<style scoped>
.ai-container {
width: 100%;
background-color: #f5f7f6;
padding: 20px
}
.card-box {
margin-top: 20px;
}
.card1 {
padding: 0;
width: 100%;
}
.paragraphs {
white-space: pre-wrap;
text-align: left;
max-height: 60vh;
overflow-y: auto;
border: 1px solid #409EFF;
padding: 10px;
margin: 5px
}
.themes {
display: flex;
flex-wrap: wrap;
height: 250px;
overflow-y: auto;
}
.outline {
white-space: pre-wrap;
text-align: left;
border: 1px solid #409EFF;
padding: 10px;
outline-style: none;
/* margin: 5px */
}
.outline-row {
display: flex;
}
.outline-row>.el-col {
display: flex;
flex-direction: column
}
.outline-row>.el-col>div,
.outline-row>.el-col>div>.el-input {
flex: 1;
display: flex;
align-items: center;
padding: 3px;
}
.item-with-dash {
margin-left: 100px
}
.item-with-dash::after {
content: "";
border-bottom: 1px dashed #000;
flex-grow: 1;
margin-left: 4px;
}
.grid-content-1 {
border-radius: 4px;
background-color: #c2dbf3;
}
.grid-content-2 {
border-radius: 4px;
background-color: #f5f5f5;
}
.el-row {
padding: 20px
}
:deep(.el-card__body){
padding: 10px 15px;
}
</style>

View File

@ -0,0 +1,391 @@
<template>
<div class="ai-container">
<el-steps style="max-width:100% " :active="activeStep" align-center>
<el-step title="生成大纲" />
<el-step title="选择模板" />
<el-step title="制作PPT" />
</el-steps>
<div class="card-box">
<el-card class="card2" v-if="activeStep === 0">
<div class="paragraphs">
{{ outputText }}
</div>
<el-button style="margin-bottom: 5px;" type="primary" @click="addMessage">从新生成</el-button>
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 1">下一步</el-button>
</el-card>
<el-card v-if="activeStep === 1">
<div style="padding-bottom: 10px">ppt模板选择</div>
<div class="themes">
<div v-for="item in backGroundList" :key="item.templateIndexId" :style="{
padding: '5px',
paddingRight: '5px',
paddingLeft: '5px',
margin: '5px',
backgroundColor: getBackgroundColor(item.templateIndexId),
borderRadius: '10px',
borderBlock: '10px solid #e6e6e6'
}" @click="chooseBackground(item.templateIndexId)" @mouseenter="changeCursor('pointer')" @mouseleave="changeCursor('default')">
{{ item.name }}
<img style="width: 150px; height: auto" :src="getBackGroundImg(item.detailImage)" alt="" />
</div>
</div>
<el-row class="el-row">
<!-- <el-col :span="6" class="el-col">
<div class="grid-content-1">
<div>演讲备注</div>
<el-switch v-model="outlineData.is_card_note" />
</div>
</el-col>-->
<!-- <el-col :span="6" class="el-col">
<div class="grid-content-2">
<div>生成封面</div>
<el-switch v-model="outlineData.is_cover_img" />
</div>
</el-col>-->
<el-col :span="6" class="el-col">
<div class="grid-content-1">
<div>自动配图</div>
<el-switch v-model="outlineData.isFigure" />
</div>
</el-col>
<el-col :span="6" class="el-col">
<div class="grid-content-2">
<div>PPT作者名</div>
<el-input v-model="outlineData.author" style="width: 50%" />
</div>
</el-col>
</el-row>
<div>
<el-button style="margin-bottom: 5px;" type="primary" @click="activeStep = 0">上一步</el-button>
<el-button style="margin-bottom: 5px;" type="primary" @click="outlineCreatePPT()">生成PPT</el-button>
</div>
</el-card>
<el-card v-if="activeStep === 2">
<el-progress :percentage="30" type="circle" v-if="percentage === 30"></el-progress>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ElMessage } from 'element-plus'
import {
getBackGroundV2,
createPPTV2,
getProgressV2,
} from "@/utils/ppt-request.js";
import CryptoJS from "crypto-js"
import { getSignature } from "@/utils/index.js";
let appId = "01ec9aa3";
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
let apikey = "39d05b269fa229f431a56c21794a8ea5"
let timestamp = Math.floor(Date.now() / 1000);
let signature = getSignature(appId, secret, timestamp);
const { ipcRenderer } = window.electron || {}
const outputText = ref(""); //
const stagingData = ref([]); //
const stagOutputText = ref(""); //
let extractedParts = ref([]) //
let firstArray = ref([]); //
let secondArray = ref([]); //
const backGroundList = ref([]);
const inputTheme = ref("高中语文《沁园春雪》的授课课件"); //
const inputRequire = ref("") //
const activeStep = ref(0); //
const combined = ref('') // ppt
const treeData = ref([]);
const status = ref("init");
const percentage = ref(0);
const getBackgrounds = () => {
treeData.value = [];
getBackGroundV2().then((res) => {
console.log(res);
backGroundList.value = res.records;
});
};
const getBackGroundImg = (imgUrlStr) => {
return JSON.parse(imgUrlStr).titleCoverImage
};
const outlineData = ref({
query: '', // 8000
// templateId: 'auto', // ppt
author: 'AIX平台',
isFigure: false, //
}
)
const emit = defineEmits(['addSuccess'])
const props = defineProps({
dataList: {
type: Array,
default: () => []
}
})
//
function updateStagingData(role, newData) {
stagingData.value.push({ role: role, content: newData });
}
//ppt
const outlineCreatePPT = () => {
const newOutlineData = { ...outlineData.value, };
newOutlineData.query = outputText.value;
createPPTV2(newOutlineData).then((res) => {
console.log(res, "正在生成中");
activeStep.value = 2
const checkProgress = () => {
getProgressV2(res.sid).then((response) => {
percentage.value = response.process;
if (response && response.pptUrl && response.pptUrl.length > 4) {
console.log('PPT',response)
// window.location.href = response.data.pptUrl;
//URLURL
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFiledf28bf990a4c40ffb7477ed4b65392c27232357022409613439/%E3%80%8A%E9%9D%99%E5%A5%B3%E3%80%8B%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BB%E4%B8%8E%E7%A0%94%E7%A9%B6.pptx"
emit('addSuccess',res)
ElMessage.success("生成成功");
} else {
const sleepTime = 2000;
let remainingTime = sleepTime;
const intervalId = setInterval(() => {
remainingTime -= 100;
if (remainingTime <= 0) {
clearInterval(intervalId);
checkProgress();
}
}, 100);
}
});
};
checkProgress();
})
};
//
const addMessage = () => {
const themeValue = inputTheme.value;
const requireValue = inputRequire.value;
firstArray.value = []
secondArray.value = []
extractedParts.value = []
stagOutputText.value = ''
const combinedString = `请帮我生成一个ppt大纲主题为${themeValue}。具体内容要求为:${requireValue}。注意用三个等级大纲展示如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
updateStagingData("user", combinedString);
connectWebSocket(stagingData.value);
// activeStep.value = 3
};
let ttsWS
function connectWebSocket(data) {
outputText.value = ""; //
status.value = "ttsing";
return getWebsocketUrl().then((url) => {
ttsWS = new WebSocket(url);
ttsWS.onopen = () => {
webSocketSend(ttsWS, data);
};
ttsWS.onmessage = (e) => {
result1(e.data);
};
ttsWS.onerror = (e) => {
status.value = "error";
console.log("WebSocket error:", e);
};
ttsWS.onclose = () => {
status.value = "init";
};
});
}
const getBackgroundColor = (key) => {
return outlineData.value.templateId === key ? '#83e2b6' : '#f5f5f5';
};
function getWebsocketUrl() {
return new Promise((resolve, reject) => {
var apiKey = apikey;
var apiSecret = secret;
var url = "wss://spark-api.xf-yun.com/v4.0/chat";
var host = "spark-api.xf-yun.com";
var date = new Date().toGMTString();
var algorithm = "hmac-sha256";
var headers = "host date request-line";
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v4.0/chat HTTP/1.1`;
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
var authorization = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(authorizationOrigin)
);
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
console.log(url);
resolve(url);
});
}
function webSocketSend(ws, data) {
const params = {
header: {
app_id: appId,
},
parameter: {
chat: {
domain: "4.0Ultra",
temperature: 0.5,
max_tokens: 1024,
},
},
payload: {
message: {
text: data,
},
},
};
ws.send(JSON.stringify(params));
}
function result1(resultData) {
let jsonData = JSON.parse(resultData);
console.log(jsonData)
outputText.value += jsonData.payload.choices.text[0].content;
const div = document.querySelector('.paragraphs');
if (div) {
div.scrollTop = div.scrollHeight;
}
if (jsonData.payload && jsonData.payload.usage) {
updateStagingData("assistant", outputText.value) //
}
if (jsonData.header.code !== 0) {
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
}
}
const chooseBackground = (data) => {
outlineData.value.templateId = data
}
const changeCursor = (cursorStyle) => {
document.documentElement.style.cursor = cursorStyle;
};
onMounted(() => {
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx"
// creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => {
// emit('addSuccess',res)
// })
// connectWebSocket("init");
props.dataList.filter(item => {
inputRequire.value += item.answer
})
getBackgrounds();
// addMessage()
});
</script>
<style scoped>
.ai-container {
width: 100%;
background-color: #f5f7f6;
padding: 20px
}
.card-box {
margin-top: 20px;
}
.card1 {
padding: 0;
width: 100%;
}
.paragraphs {
white-space: pre-wrap;
text-align: left;
max-height: 60vh;
overflow-y: auto;
border: 1px solid #409EFF;
padding: 10px;
margin: 5px
}
.themes {
display: flex;
flex-wrap: wrap;
height: 250px;
overflow-y: auto;
}
.outline {
white-space: pre-wrap;
text-align: left;
border: 1px solid #409EFF;
padding: 10px;
outline-style: none;
/* margin: 5px */
}
.outline-row {
display: flex;
}
.outline-row>.el-col {
display: flex;
flex-direction: column
}
.outline-row>.el-col>div,
.outline-row>.el-col>div>.el-input {
flex: 1;
display: flex;
align-items: center;
padding: 3px;
}
.item-with-dash {
margin-left: 100px
}
.item-with-dash::after {
content: "";
border-bottom: 1px dashed #000;
flex-grow: 1;
margin-left: 4px;
}
.grid-content-1 {
border-radius: 4px;
background-color: #c2dbf3;
}
.grid-content-2 {
border-radius: 4px;
background-color: #f5f5f5;
}
.el-row {
padding: 20px
}
:deep(.el-card__body){
padding: 10px 15px;
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<div>
<el-dialog class="ppt-dialog" v-model="model" :show-close="false" width="800" destroy-on-close :top="'3vh'">
<template #header="{ close, titleId, titleClass }">
<div class="dialog-header">
<h4 :id="titleId" :class="titleClass">生成PPT(试验版)</h4>
<i class="iconfont icon-guanbi" @click="close"></i>
</div>
</template>
<AiPptist @add-success="addAiPPT" :dataList="dataList"/>
</el-dialog>
</div>
</template>
<script setup>
import AiPptist from './ai-pptist.vue';
const model = defineModel()
const emit = defineEmits(['addSuccess'])
const props = defineProps({
dataList: {
type: Array,
default: () => []
}
})
const addAiPPT = (data) => {
emit('addSuccess', data)
}
</script>
<style lang="scss" scoped>
:deep(.ppt-dialog){
-webkit-app-region: no-drag;
}
.dialog-header{
display: flex;
justify-content: space-between;
align-items: center;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
</style>

View File

@ -42,6 +42,7 @@ import useUserStore from '@/store/modules/user'
import {ArrowDown} from '@element-plus/icons-vue' import {ArrowDown} from '@element-plus/icons-vue'
import { onMounted,ref } from 'vue'; import { onMounted,ref } from 'vue';
import { listEvaluation } from '@/api/subject/index' import { listEvaluation } from '@/api/subject/index'
import {sessionStore} from '@/utils/store'
const userStore = useUserStore().user const userStore = useUserStore().user
const subjectList = ref([]) const subjectList = ref([])
// //
@ -58,22 +59,27 @@ const getSubject = () => {
// //
const handleUserEduStage = (item) => { const handleUserEduStage = (item) => {
userStore.edustage = item userStore.edustage = item
sessionStore.set('edustageSelf',item)
if(item === '幼儿园'){ if(item === '幼儿园'){
// //
userStore.edusubject = '语文' userStore.edusubject = '语文'
sessionStore.set('edusubjectSelf','语文')
} }
else if(item === '高中' && userStore.edusubject === "道德与法治"){ else if(item === '高中' && userStore.edusubject === "道德与法治"){
// //
userStore.edusubject = '政治' userStore.edusubject = '政治'
sessionStore.set('edusubjectSelf','政治')
} }
else if(item != '高中' && userStore.edusubject === "政治"){ else if(item != '高中' && userStore.edusubject === "政治"){
// //
userStore.edusubject = '道德与法治' userStore.edusubject = '道德与法治'
sessionStore.set('edusubjectSelf','道德与法治')
} }
} }
// //
const handleUserEduSubject = (item) => { const handleUserEduSubject = (item) => {
userStore.edusubject = item; userStore.edusubject = item;
sessionStore.set('edusubjectSelf',item)
} }
onMounted(() => { onMounted(() => {
getSubject() getSubject()

View File

@ -40,6 +40,9 @@ import {listClassmain} from '@/api/classManage/index'
// //
import ClassList from './components/classList.vue' import ClassList from './components/classList.vue'
import useClassTaskStore from '@/store/modules/classTask'
const classTaskStore = useClassTaskStore();
// //
const classList = ref([]) const classList = ref([])
// //
@ -162,11 +165,12 @@ setTimeout(() => {
function submit() { function submit() {
proxy.$refs.userRef.validate((valid) => { proxy.$refs.userRef.validate((valid) => {
if (valid) { if (valid) {
userStore.user.avatar = userStore.user.avatar userStore.user.edusubject = sessionStore.get('edusubjectSelf') ? sessionStore.get('edusubjectSelf') : userStore.user.edusubject
userStore.user.edustage = sessionStore.get('edustageSelf') ? sessionStore.get('edustageSelf') : userStore.user.edustage
updateUserInfo(userStore.user).then((response) => { updateUserInfo(userStore.user).then((response) => {
if(response.code == 200){ if(response.code == 200){
userStore.login({username:userStore.user.userName,password:userStore.user.plainpwd}).then(() => { userStore.login({username:userStore.user.userName,password:userStore.user.plainpwd}).then(() => {
userStore.getInfo().then(res => { userStore.getInfo().then(async res => {
if(res.code === 200){ if(res.code === 200){
let sessionSubject = { let sessionSubject = {
bookList: null, bookList: null,
@ -177,6 +181,10 @@ function submit() {
} }
sessionStore.set( 'subject', sessionSubject) sessionStore.set( 'subject', sessionSubject)
ElMessage.success('修改成功') ElMessage.success('修改成功')
console.log('userStore更新后', res);
await classTaskStore.initJYInfo(userStore.user);
//console.log('classTaskStore->', classTaskStore);
}else{ }else{
ElMessage.error(response.msg) ElMessage.error(response.msg)
} }

View File

@ -32,7 +32,15 @@
</el-row> --> </el-row> -->
<el-row class="resoure-btns"> <el-row class="resoure-btns">
<el-col :span="24" class="query-row flex"> <el-col :span="24" class="query-row flex">
<div class="flex row-left"> <el-select v-model="sourceStore.query.fileSuffix" @change="sourceStore.changeSuffix" <div class="flex row-left">
<!-- 第三方资源筛选-->
<el-select v-if="isThird" v-model="sourceStore.thirdQuery.type" @change="sourceStore.thirdChangeType"
style="width: 110px">
<el-option v-for="item in coursewareTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
<el-select v-else v-model="sourceStore.query.fileSuffix" @change="sourceStore.changeSuffix"
style="width: 110px"> style="width: 110px">
<el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label" <el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label"
:value="item.value" /> :value="item.value" />
@ -48,7 +56,6 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>

View File

@ -7,18 +7,22 @@
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<el-button type="primary" link @click="onSelect(item)">选择模式</el-button> <el-button type="primary" link @click="onSelect(item)">选择模式</el-button>
</div> </div>
<el-scrollbar> <div class="content-list">
<div class="item-list flex"> <div class="item-list flex">
<el-card class="item-card" shadow="never" v-for="el in item.child" :key="el.id"> <el-card class="item-card" shadow="never" v-for="el in item.child" :key="el.id">
<p class="card-name">{{ el.name }}</p> <p class="card-name">
<div class="card-text"> <el-text line-clamp="1" :title="el.name">
<el-text line-clamp="2"> {{ el.name }}
{{ el.prompt }} </el-text>
</el-text> </p>
</div> <div class="card-text">
</el-card> <el-text line-clamp="4" :title="el.prompt">
</div> {{ el.prompt }}
</el-scrollbar> </el-text>
</div>
</el-card>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -97,30 +101,44 @@ onMounted(() => {
justify-content: space-between; justify-content: space-between;
margin-bottom: 5px; margin-bottom: 5px;
} }
.con-item { .con-item {
margin-bottom: 20px; margin-bottom: 20px;
display: flex;
flex-direction: column
}
.item-list{
margin-bottom: 10px;
} }
.item-card { .item-card {
width: 130px; width: 130px;
font-size: 13px; font-size: 13px;
padding: 10px; padding: 10px;
margin-right: 20px; margin-right: 20px;
flex-shrink: 0;
border-radius: 10px;
:deep(.el-card__body) { :deep(.el-card__body) {
padding: 0 !important; padding: 0 !important;
} }
.card-name { .card-name {
text-align: center; text-align: center;
font-size: 14px;
} }
.card-text { .card-text {
text-align: left; text-align: left;
font-size: 12px;
.el-text{
font-size: 12px !important;
}
} }
} }
} }
} }
.content-list{
overflow-x: auto
}
.content-list::-webkit-scrollbar {
height: 8px;
}
</style> </style>

View File

@ -11,7 +11,7 @@
</div> </div>
<div class="header-right"> <div class="header-right">
<el-button type="primary">生成大纲</el-button> <el-button type="primary">生成大纲</el-button>
<el-button type="danger">生成PPT</el-button> <el-button type="danger" @click="pptDialog = true">生成PPT</el-button>
</div> </div>
</div> </div>
<div class="right-con flex"> <div class="right-con flex">
@ -49,29 +49,65 @@
</div> </div>
<EditDialog v-model="isEdit" :item="curItem" /> <EditDialog v-model="isEdit" :item="curItem" />
<AdjustDialog v-model="isAdjust" :item="curItem" /> <AdjustDialog v-model="isAdjust" :item="curItem" />
<PptDialog @add-success="addAiPPT" :dataList="resultList" v-model="pptDialog"/>
</template> </template>
<script setup> <script setup>
import { ref, onUnmounted, reactive } from 'vue' import { ref, onMounted, onUnmounted, reactive } from 'vue'
import { sessionStore } from '@/utils/store'
import emitter from '@/utils/mitt' import emitter from '@/utils/mitt'
import EditDialog from './edit-dialog.vue' import EditDialog from './edit-dialog.vue'
import AdjustDialog from './adjust-dialog.vue' import AdjustDialog from './adjust-dialog.vue'
import { completion } from '@/api/mode/index.js' import { completion, tempResult } from '@/api/mode/index.js'
import { dataSetJson } from '@/utils/comm.js'
import PptDialog from '@/views/prepare/container/pptist-dialog.vue'
const pptDialog = ref(false)
const resultList = ref([]) const resultList = ref([])
emitter.on('changeMode', (item) => { emitter.on('changeMode', (item) => {
console.log(item, 'item')
resultList.value = item.child resultList.value = item.child
conversation() // conversation()
getTempResult(item.id)
}) })
//
const getTempResult = (id) => {
tempResult({ mainModelId: id }).then(res => {
console.log(res, 2000)
let rows = res.rows
if (rows.length > 0) {
resultList.value.forEach(item => {
rows.forEach(el => {
if (item.id == el.modelId) {
item.answer = el.content
item.reultId = el.id
}
})
})
}
})
}
const params = reactive(
{
prompt: '',
dataset_id: ''
}
)
const addAiPPT = (res) => {
//TODO resPPT
console.log(res)
}
const conversation = async () => { const conversation = async () => {
for (let item of resultList.value) { for (let item of resultList.value) {
item.loading = true item.loading = true
try { try {
const { data } = await completion({ params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}课标对${curNode.itemtitle}进行教学分析`
dataset_id: 'cee3062a9fcf11efa6910242ac140006', const { data } = await completion(params)
prompt: item.prompt
})
item.answer = data.answer item.answer = data.answer
} finally { } finally {
item.loading = false item.loading = false
@ -89,10 +125,8 @@ const curItem = reactive({})
const againResult = async (index, item) => { const againResult = async (index, item) => {
try { try {
resultList.value[index].loading = true resultList.value[index].loading = true
const { data } = await completion({ params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}课标对${curNode.itemtitle}进行教学分析`
dataset_id: 'cee3062a9fcf11efa6910242ac140006', const { data } = await completion(params)
prompt: item.prompt
})
resultList.value[index].answer = data.answer resultList.value[index].answer = data.answer
} finally { } finally {
resultList.value[index].loading = false resultList.value[index].loading = false
@ -101,12 +135,12 @@ const againResult = async (index, item) => {
// //
const isAdjust = ref(false) const isAdjust = ref(false)
const onAdjust = (index, item) => { const onAdjust = (index, item) => {
curIndex.value = index curIndex.value = index
Object.assign(curItem, item) Object.assign(curItem, item)
isAdjust.value = true isAdjust.value = true
} }
emitter.on('changeAdjust', (item) =>{ emitter.on('changeAdjust', (item) => {
resultList.value[curIndex.value].answer = item resultList.value[curIndex.value].answer = item
}) })
@ -122,6 +156,17 @@ emitter.on('changeResult', (item) => {
}) })
const curNode = reactive({})
onMounted(() => {
let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
let jsonKey = `课标-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey]
})
// //
onUnmounted(() => { onUnmounted(() => {
emitter.off('changeMode') emitter.off('changeMode')
@ -204,4 +249,4 @@ onUnmounted(() => {
} }
} }
} }
</style> </style>

View File

@ -7,7 +7,10 @@
"noEmit": true, "noEmit": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"../*": ["./src/*"] "../*": ["./src/*"],
"@/*": [
"./src/renderer/src/*"
]
} }
} }
} }