Compare commits
23 Commits
36b6d1cc05
...
b5176e41b3
Author | SHA1 | Date |
---|---|---|
白了个白 | b5176e41b3 | |
yangws | c6b65102bd | |
小杨 | 2513215a52 | |
zhangxuelin | 488ad420d7 | |
zhangxuelin | 5b7007a2fc | |
zhangxuelin | 23470cc188 | |
yangws | 82b1258019 | |
小杨 | 52da91f8ec | |
yangws | 58bc348eed | |
小杨 | 3ade7f7a35 | |
yangws | 68a6050ba7 | |
小杨 | 866abbcd7e | |
yangws | b3b4f29d5d | |
小杨 | 5dc9e4e20d | |
zhangxuelin | 21cf2c9cfa | |
zhangxuelin | 9031fca09c | |
lyc | 5a1b95ea5d | |
lyc | 0ec8ec7480 | |
baigl | 2c1e9923c5 | |
lyc | c3ef8d881e | |
lyc | 756d7fdaa1 | |
lyc | d05e50218d | |
lyc | 71e6b74dd4 |
|
@ -48,6 +48,11 @@ export default defineConfig({
|
|||
changeOrigin: true, // 改变请求的起源
|
||||
rewrite: (path) => path.replace(/^\/parth/, '') // 重写路径
|
||||
},
|
||||
'/v1': {
|
||||
target: 'https://ai.ysaix.com:7864',
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^/v1': '' }
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [vue(), WindiCSS()],
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import request from '@/utils/request'
|
||||
import axios from 'axios'
|
||||
|
||||
// 查询模板列表
|
||||
export function modelList(params) {
|
||||
return request({
|
||||
url: '/education/llmModel/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function conversation(data) {
|
||||
return axios({
|
||||
url: '/v1/api/new_conversation',
|
||||
method: 'get',
|
||||
headers: {
|
||||
isToken: true,
|
||||
'Authorization':'Bearer ragflow-IwNzMxMTIyOGY0ZTExZWZiOGE2MDI0Mm',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': '*/*'
|
||||
},
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 进行课标研读对话
|
||||
export function completion(data) {
|
||||
return axios({
|
||||
url: '/v1/api/completion',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Authorization':'Bearer ragflow-IwNzMxMTIyOGY0ZTExZWZiOGE2MDI0Mm',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': '*/*'
|
||||
},
|
||||
data: data
|
||||
})
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4723712 */
|
||||
src: url('iconfont.woff2?t=1730448425319') format('woff2'),
|
||||
url('iconfont.woff?t=1730448425319') format('woff'),
|
||||
url('iconfont.ttf?t=1730448425319') format('truetype');
|
||||
src: url('iconfont.woff2?t=1731315402630') format('woff2'),
|
||||
url('iconfont.woff?t=1731315402630') format('woff'),
|
||||
url('iconfont.ttf?t=1731315402630') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
@ -13,6 +13,26 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-ai1:before {
|
||||
content: "\e70a";
|
||||
}
|
||||
|
||||
.icon-duihua:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.icon-bianji1:before {
|
||||
content: "\e678";
|
||||
}
|
||||
|
||||
.icon-a-ziyuan91:before {
|
||||
content: "\e611";
|
||||
}
|
||||
|
||||
.icon-ai:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.icon-xiaoxi:before {
|
||||
content: "\e677";
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,41 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "41844021",
|
||||
"name": "ai",
|
||||
"font_class": "ai1",
|
||||
"unicode": "e70a",
|
||||
"unicode_decimal": 59146
|
||||
},
|
||||
{
|
||||
"icon_id": "2286510",
|
||||
"name": "对话",
|
||||
"font_class": "duihua",
|
||||
"unicode": "e60d",
|
||||
"unicode_decimal": 58893
|
||||
},
|
||||
{
|
||||
"icon_id": "4093249",
|
||||
"name": "编辑",
|
||||
"font_class": "bianji1",
|
||||
"unicode": "e678",
|
||||
"unicode_decimal": 59000
|
||||
},
|
||||
{
|
||||
"icon_id": "39732311",
|
||||
"name": "AI分析",
|
||||
"font_class": "a-ziyuan91",
|
||||
"unicode": "e611",
|
||||
"unicode_decimal": 58897
|
||||
},
|
||||
{
|
||||
"icon_id": "41784801",
|
||||
"name": "ai",
|
||||
"font_class": "ai",
|
||||
"unicode": "e626",
|
||||
"unicode_decimal": 58918
|
||||
},
|
||||
{
|
||||
"icon_id": "2158298",
|
||||
"name": "消息",
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<el-dialog v-model="isDialog" :show-close="false" width="900">
|
||||
<template #header>
|
||||
<div class="custom-header flex">
|
||||
<span>选择{{ title }}</span>
|
||||
<i class="iconfont icon-guanbi" @click="isDialog = false"></i>
|
||||
</div>
|
||||
</template>
|
||||
<div class="dialog-content">
|
||||
<div class="flex">
|
||||
<el-radio-group v-model="radio" @change="changeRadio">
|
||||
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="content-list">
|
||||
<ul>
|
||||
<li v-for="(item, index) in list" :class="activeIndex == index ? 'li-active' : ''" @click="clickItem(index)">
|
||||
<el-image class="img" :src="item.url" />
|
||||
<span>{{ item.name }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="isDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="isDialog = false">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35'
|
||||
|
||||
const isDialog = defineModel()
|
||||
|
||||
const props = defineProps({
|
||||
model: {
|
||||
type: [String, Number],
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
const title = computed(() => {
|
||||
if (props.model == 1) return '课标';
|
||||
if (props.model == 2) return '教材';
|
||||
if (props.model == 3) return '考试';
|
||||
})
|
||||
|
||||
const radio = ref(1)
|
||||
const radioList = ref([
|
||||
{ label: '浏览研读', value: 1 },
|
||||
{ label: '跨学科研读', value: 2 },
|
||||
{ label: '跨学段研读', value: 3 },
|
||||
{ label: '课标修订研读', value: 4 },
|
||||
{ label: '自由研读', value: 5 },
|
||||
])
|
||||
const list = ref([
|
||||
{
|
||||
name: '高中语文课程标准',
|
||||
url
|
||||
}
|
||||
])
|
||||
const changeRadio = () => {
|
||||
list.value = []
|
||||
for (let i = 0; i < Math.floor(Math.random() * 5) + 1; i++) {
|
||||
list.value.push({
|
||||
name: '高中语文课程标准',
|
||||
url
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const activeIndex = ref(-1)
|
||||
|
||||
const clickItem = (index) => {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-header {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.icon-guanbi {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding-top: 10px;
|
||||
.content-list {
|
||||
padding-top: 10px;
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 13px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.img {
|
||||
width: 100px;
|
||||
height: 130px;
|
||||
border: solid #ccc 1px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #E0EAFF;
|
||||
}
|
||||
}
|
||||
|
||||
.li-active {
|
||||
background: #E0EAFF;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<div class="container-header flex">
|
||||
<div class="header-left flex">
|
||||
<el-button link @click="showDialog = true">
|
||||
高中语文课程标准<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>
|
||||
<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>
|
||||
<Dialog v-model="showDialog" :model="model" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } 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'
|
||||
|
||||
const props = defineProps({
|
||||
model: {
|
||||
type: [String, Number],
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['changeTemp', 'onRead'])
|
||||
|
||||
const showDialog = ref(false)
|
||||
|
||||
const aiRead = () => {
|
||||
emit('onRead')
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 当前模板名称
|
||||
const curTemplate = reactive({ name: '', id: '' })
|
||||
// 模板列表
|
||||
const templateList = ref([])
|
||||
// 获取模板列表
|
||||
const getTemplateList = () => {
|
||||
modelList({ model: 1, type: 1 }).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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
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>
|
|
@ -0,0 +1,27 @@
|
|||
<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')
|
||||
console.log('pdfUrl.value', pdfUrl.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-pdf{
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,356 @@
|
|||
<template>
|
||||
<div class="read-container">
|
||||
|
||||
<el-scrollbar height="100%">
|
||||
<div class="template-list">
|
||||
<el-row v-for="item in childTempList">
|
||||
<el-col :span="24">
|
||||
<div class="template-item" v-loading="item.loading">
|
||||
<div class="item-header"><span class="blue">#</span>{{ item.name }}</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>
|
||||
<i class="iconfont icon-ai1"></i>
|
||||
重新研读
|
||||
</el-button>
|
||||
<el-button type="primary" link>
|
||||
<i class="iconfont icon-duihua"></i>
|
||||
AI对话调整
|
||||
</el-button>
|
||||
<el-button type="primary" link>
|
||||
<i class="iconfont icon-bianji1"></i>
|
||||
手动编辑结果
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { conversation, completion } from '@/api/mode/index'
|
||||
import { modelList } from '@/api/mode/index'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const props = defineProps({
|
||||
curTemp: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
tempId: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
model: {
|
||||
type: [String, Number],
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
|
||||
// 获取子模板
|
||||
const tempLoading = ref(false)
|
||||
const childTempList = ref([])
|
||||
const getChildTemplate = () => {
|
||||
tempLoading.value = true
|
||||
modelList({ model: props.model, type: 2, parentId: props.tempId }).then(res => {
|
||||
childTempList.value = res.rows
|
||||
console.log('res.rows=====>', res.rows)
|
||||
|
||||
}).finally(() => {
|
||||
tempLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => props.tempId, (newVal) => {
|
||||
if (newVal) {
|
||||
getChildTemplate()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
// 获取会话ID
|
||||
const params = reactive(
|
||||
{
|
||||
"conversation_id": "",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": ""
|
||||
}
|
||||
],
|
||||
"quote": false,
|
||||
"stream": false
|
||||
}
|
||||
)
|
||||
const isAiDeal = ref(false)
|
||||
const curNode = reactive({})
|
||||
const getConversation = async () => {
|
||||
|
||||
const { user: { userId } } = userStore
|
||||
const result = await conversation({ user_id: String(userId) })
|
||||
params.conversation_id = result.data.data.id
|
||||
|
||||
|
||||
getCompletion()
|
||||
}
|
||||
// 大模型对话
|
||||
const resultList = ref([])
|
||||
const getCompletion = async () => {
|
||||
console.log('params=====>', params)
|
||||
|
||||
for (let item of childTempList.value) {
|
||||
try {
|
||||
item.loading = true
|
||||
params.messages[0].content = `根据${curNode.edustage}语文课标,提炼出${item.name}`
|
||||
const res = await completion(params)
|
||||
console.log('对话结果===》', res)
|
||||
let answer = res.data.data.answer
|
||||
item.answer = getResult(answer);
|
||||
|
||||
} finally {
|
||||
item.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
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let data = sessionStore.get('subject.curNode')
|
||||
Object.assign(curNode, data);
|
||||
|
||||
})
|
||||
|
||||
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;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.pl-25 {
|
||||
padding-left: 25px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div class="page-template flex">
|
||||
<!--头部-->
|
||||
<Header @changeTemp="changeTemp" @onRead="onRead"/>
|
||||
<el-row :gutter="20" class="tempalte-main">
|
||||
<el-col :span="12">
|
||||
<!--左侧pdf-->
|
||||
<Pdf />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<!--右侧模板研读-->
|
||||
<Result ref="resultRef" :tempId="tempId"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import Header from './container/header.vue'
|
||||
import Pdf from './container/pdf.vue'
|
||||
import Result from './container/result.vue'
|
||||
|
||||
const resultRef = ref()
|
||||
const tempId = ref('')
|
||||
const changeTemp = (id) =>{
|
||||
tempId.value = id
|
||||
}
|
||||
|
||||
const onRead = () =>{
|
||||
resultRef.value.getConversation()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-template {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
.tempalte-main {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -12,7 +12,7 @@
|
|||
<div class="head-aside">
|
||||
<ul >
|
||||
<li :class="computedregistertype==1 || computedregistertype==3?'auth-li':'auth-li pointer-events'" @click="onUserTo('/schoolCertification')" >
|
||||
<i class="iconfont icon-renzheng-"></i>
|
||||
<i class="iconfont icon-renzheng-" :style="computedregistertype==4?'color:green;':''"></i>
|
||||
<span class="mlr-5" v-if="computedregistertype!=4">学校认证</span>
|
||||
<span class="mlr-5" v-else>{{ userStore.DeptInfo.register.schoolName }}</span>
|
||||
<span class="gray" v-if="computedregistertype!=4">未认证</span>
|
||||
|
@ -85,13 +85,13 @@ const headerMenus = [
|
|||
name: '教学工作台',
|
||||
id: 2,
|
||||
icon: 'icon-gongzuotai',
|
||||
path: '/home'
|
||||
path: '/desktop'
|
||||
},
|
||||
{
|
||||
name: '资源中心',
|
||||
id: 3,
|
||||
icon: 'icon-kechengziyuan1',
|
||||
path: '/resource'
|
||||
path: '/resource/index'
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -100,13 +100,13 @@ const sideBottomMenu = [
|
|||
name: '算力',
|
||||
id: 4,
|
||||
icon: 'icon-yanhouke-shengyinyichang',
|
||||
path: '/hashrate'
|
||||
path: '/hashrate/index'
|
||||
},
|
||||
{
|
||||
name: '设置',
|
||||
id: 5,
|
||||
icon: 'icon-set',
|
||||
path: '/setting'
|
||||
path: '/setting/index'
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -118,7 +118,6 @@ const computedregistertype = computed(() => {
|
|||
}
|
||||
// 加入成功
|
||||
if(type==3 && userStore.DeptInfo.register.auditStatus==1){
|
||||
console.log(1111)
|
||||
return 4
|
||||
}
|
||||
if(type==4 && userStore.DeptInfo.register.auditStatus==1){
|
||||
|
@ -310,4 +309,8 @@ onMounted(() => {
|
|||
.pointer-events{
|
||||
pointer-events: none;
|
||||
}
|
||||
.icon-renzheng-{
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
</style>
|
|
@ -58,23 +58,40 @@ export const constantRoutes = [
|
|||
},
|
||||
]
|
||||
},
|
||||
|
||||
...toolRouters
|
||||
]
|
||||
|
||||
const dynamicRoutes = [
|
||||
{
|
||||
path: '/',
|
||||
component: Layout,
|
||||
redirect: '/home',
|
||||
redirect: '/desktop',
|
||||
meta: { title: '教学工作台' },
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
path: 'desktop',
|
||||
component: () => import('@/views/desktop/index.vue'),
|
||||
name: 'desktop',
|
||||
meta: { title: '教学工作台' }
|
||||
},
|
||||
{
|
||||
path: '/resource',
|
||||
component: () => import('@/views/resource/index.vue'),
|
||||
name: 'resource',
|
||||
meta: { title: '资源库' }
|
||||
path: 'standardanalysis',
|
||||
component: () => import('@/views/teach/standardAnalysis/index.vue'),
|
||||
name: 'standardanalysis',
|
||||
meta: { title: '课标分析', showBread: true }
|
||||
},
|
||||
{
|
||||
path: 'textbookAnalysis',
|
||||
component: () => import('@/views/textbookAnalysis/index.vue'),
|
||||
name: 'textbookAnalysis',
|
||||
meta: { title: '教材分析', showBread: true }
|
||||
},
|
||||
{
|
||||
path: 'examReport',
|
||||
component: () => import('@/views/examReport/index.vue'),
|
||||
name: 'examReport',
|
||||
meta: { title: '考试分析', showBread: true }
|
||||
},
|
||||
{
|
||||
path: 'prepare',
|
||||
|
@ -82,24 +99,33 @@ export const constantRoutes = [
|
|||
name: 'prepare',
|
||||
meta: { title: '教学实践', showBread: true }
|
||||
},
|
||||
{
|
||||
path: 'newClassTask',
|
||||
component: () => import('@/views/classTask/newClassTask.vue'),
|
||||
name: 'newClassCorrect',
|
||||
meta: { title: '作业设计', showBread: true }
|
||||
},
|
||||
{
|
||||
path: 'classTaskAssign',
|
||||
component: () => import('@/views/classTask/classTaskAssign.vue'),
|
||||
name: 'classTaskAssign',
|
||||
meta: { title: '作业布置', showBread: true }
|
||||
},
|
||||
{
|
||||
path: 'classTask',
|
||||
component: () => import('@/views/classTask/classTask.vue'),
|
||||
name: 'classCorrect',
|
||||
meta: { title: '作业批改', showBread: true }
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: '/teach',
|
||||
component: () => import('@/views/teach/index.vue'),
|
||||
name: 'teach',
|
||||
meta: { title: '授课' }
|
||||
},
|
||||
{
|
||||
path: '/standardanalysis',
|
||||
component: () => import('@/views/teach/standardAnalysis/index.vue'),
|
||||
name: 'standardanalysis',
|
||||
meta: { title: '课标分析', showBread: true }
|
||||
},
|
||||
{
|
||||
path: '/textbookAnalysis',
|
||||
component: () => import('@/views/textbookAnalysis/index.vue'),
|
||||
name: 'textbookAnalysis',
|
||||
meta: { title: '教材分析', showBread: true }
|
||||
},
|
||||
|
||||
{
|
||||
path: '/profile',
|
||||
component: () => import('@/views/profile/index.vue'),
|
||||
|
@ -125,42 +151,6 @@ export const constantRoutes = [
|
|||
name: 'class',
|
||||
meta: { title: '班级中心' }
|
||||
},
|
||||
{
|
||||
path: '/classTaskAssign',
|
||||
component: () => import('@/views/classTask/classTaskAssign.vue'),
|
||||
name: 'classTaskAssign',
|
||||
meta: { title: '作业布置', showBread: true }
|
||||
},
|
||||
{
|
||||
path: '/classTask',
|
||||
component: () => import('@/views/classTask/classTask.vue'),
|
||||
name: 'classCorrect',
|
||||
meta: { title: '作业批改', showBread: true }
|
||||
},
|
||||
{
|
||||
path: '/newClassTask',
|
||||
component: () => import('@/views/classTask/newClassTask.vue'),
|
||||
name: 'newClassCorrect',
|
||||
meta: { title: '作业设计', showBread: true }
|
||||
},
|
||||
{
|
||||
path: '/examReport',
|
||||
component: () => import('@/views/examReport/index.vue'),
|
||||
name: 'examReport',
|
||||
meta: { title: '考试分析', showBread: true }
|
||||
},
|
||||
{
|
||||
path: '/hashrate',
|
||||
component: () => import('@/views/hashrate/index.vue'),
|
||||
name: 'hashrate',
|
||||
meta: { title: '算力' }
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
component: () => import('@/views/setting/index.vue'),
|
||||
name: 'setting',
|
||||
meta: { title: '设置' }
|
||||
},
|
||||
{
|
||||
path: '/joinSchool',
|
||||
component: () => import('@/views/joinSchool/index.vue'),
|
||||
|
@ -179,14 +169,50 @@ export const constantRoutes = [
|
|||
name: 'schoolManagement',
|
||||
meta: {title: '学校管理'}
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
...toolRouters
|
||||
{
|
||||
path: '/resource',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/resource/index.vue'),
|
||||
name: 'resource',
|
||||
meta: { title: '资源库' },
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/hashrate',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/hashrate/index.vue'),
|
||||
name: 'hashrate',
|
||||
meta: { title: '算力' },
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/setting/index.vue'),
|
||||
name: 'setting',
|
||||
meta: { title: '设置' },
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(), //hash 模式
|
||||
routes: constantRoutes
|
||||
routes: [...constantRoutes,...dynamicRoutes]
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
|
@ -1,43 +1,22 @@
|
|||
<template>
|
||||
<div class="read-container">
|
||||
<el-scrollbar height="100%">
|
||||
<div class="template-list" v-loading="loading">
|
||||
<el-row v-for="item in 6">
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>{{ item }}</div>
|
||||
<div class="item-text" >
|
||||
<div class="item-icon">
|
||||
<i class="iconfont icon-ai"></i>
|
||||
</div>
|
||||
<div class="item-answer" v-html="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="template-list">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>核心素养与课程目标</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的核心素养与课程目标</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>课程内容相关</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的课程内容要求
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>学业质量要求</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的学业水平要求,包括水平一、水平二、水平三各自的要求描述
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>教学实施建议</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的教学实施建议
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<!-- <el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item template-item-result">
|
||||
<div class="result-item-header">
|
||||
|
@ -63,19 +42,106 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-row> -->
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { conversation, completion } from '@/api/mode/index'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const props = defineProps({
|
||||
curTemp: {
|
||||
type: Array,
|
||||
default: () =>{
|
||||
return []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
// 获取会话ID
|
||||
const params = reactive(
|
||||
{
|
||||
"conversation_id": "",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": ""
|
||||
}
|
||||
],
|
||||
"quote": false,
|
||||
"stream": false
|
||||
}
|
||||
)
|
||||
const curNode = reactive({})
|
||||
const getConversation = async() =>{
|
||||
const { user: { userId } } = userStore
|
||||
const result = await conversation({ user_id: String(userId) })
|
||||
params.conversation_id = result.data.data.id
|
||||
|
||||
|
||||
getCompletion()
|
||||
}
|
||||
// 大模型对话
|
||||
const resultList = ref([])
|
||||
const getCompletion = async() =>{
|
||||
console.log('params=====>',params)
|
||||
|
||||
for (const item of props.curTemp) {
|
||||
try {
|
||||
loading.value = true
|
||||
params.messages[0].content = `根据${curNode.edustage}语文课标,提炼出${item.name}`
|
||||
const res = await completion(params)
|
||||
console.log('对话结果===》', res)
|
||||
let answer = res.data.data.answer
|
||||
answer = getResult(answer);
|
||||
resultList.value.push({
|
||||
title: item.name,
|
||||
answer
|
||||
})
|
||||
} finally{
|
||||
loading.value = 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
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let data = sessionStore.get('subject.curNode')
|
||||
Object.assign(curNode, data);
|
||||
console.log(props.curTemp,'curTemp')
|
||||
// getConversation()
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.read-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
|
||||
height: 100%;
|
||||
.el-dropdown-link {
|
||||
font-weight: bold;
|
||||
|
||||
|
@ -98,6 +164,9 @@
|
|||
}
|
||||
|
||||
}
|
||||
.right-con{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.template-list {
|
||||
|
||||
|
@ -123,10 +192,32 @@
|
|||
|
||||
.item-text {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
color: #409eff;
|
||||
font-size: 13px;
|
||||
padding-left: 20px;
|
||||
text-align: left;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +1,80 @@
|
|||
<template>
|
||||
<div class="read-container">
|
||||
<div class="read-header flex">
|
||||
<el-dropdown>
|
||||
<el-dropdown @command="changeTemplate">
|
||||
<span class="el-dropdown-link">
|
||||
课标研读模板
|
||||
{{ curTemplate.name }}
|
||||
<i class="iconfont icon-xiangxia" </i>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>课标研读模板一</el-dropdown-item>
|
||||
<el-dropdown-item>课标研读模板二</el-dropdown-item>
|
||||
<el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.name
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button text class="add-btn">
|
||||
<i class="iconfont icon-jiahao"></i>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button type="primary" @click="aiRead">一键研读</el-button>
|
||||
</div>
|
||||
<div class="template-list">
|
||||
<el-row>
|
||||
<div class="template-list" v-loading="tempLoading">
|
||||
<el-row v-for="item in childTempList" :key="item.id">
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>核心素养与课标目标</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的核心素养与课程目标</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>课程内容要求</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的课程内容要求
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>学业质量要求</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的学业水平要求,包括水平一、水平二、水平三各自的要求描述
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<div class="template-item">
|
||||
<div class="item-header"><span class="blue">#</span>教学实施建议</div>
|
||||
<div class="item-text">研读课程标准,提取出与本课相关的教学实施建议
|
||||
</div>
|
||||
<div class="item-header"><span class="blue">#</span>{{ item.name }}</div>
|
||||
<div class="item-text">{{ item.prompt }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty v-if="!childTempList.length" description="暂无模板数据" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { modelList} from '@/api/mode/index'
|
||||
|
||||
// 当前模板名称
|
||||
const curTemplate = reactive({ name: '', id: '' })
|
||||
// 模板列表
|
||||
const templateList = ref([])
|
||||
// 获取模板列表
|
||||
const getTemplateList = () => {
|
||||
modelList({ model: 1, type: 1 }).then(res => {
|
||||
templateList.value = res.rows
|
||||
Object.assign(curTemplate, res.rows[0]);
|
||||
getChildTemplate()
|
||||
})
|
||||
}
|
||||
// 模板切换
|
||||
const changeTemplate = (val) => {
|
||||
Object.assign(curTemplate, val);
|
||||
getChildTemplate()
|
||||
}
|
||||
|
||||
// 获取子模板
|
||||
const tempLoading = ref(false)
|
||||
const childTempList = ref([])
|
||||
const getChildTemplate = () => {
|
||||
tempLoading.value = true
|
||||
modelList({ model: 1, type: 2, parentId: curTemplate.id }).then(res => {
|
||||
childTempList.value = res.rows
|
||||
}).finally(() => {
|
||||
tempLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const emit = defineEmits(['changeMenu'])
|
||||
const aiRead = async () => {
|
||||
emit('changeMenu', childTempList.value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
getTemplateList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -78,15 +93,6 @@
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.add-btn {
|
||||
font-size: 13px;
|
||||
|
||||
.icon-jiahao {
|
||||
margin-right: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.template-list {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="page-curriculum flex">
|
||||
<TemplateStudy/>
|
||||
<!-- <div class="page-curriculum flex">
|
||||
<el-row>
|
||||
<el-col :span="12" class="flex">
|
||||
<div class="page-left">
|
||||
|
@ -31,18 +32,19 @@
|
|||
}}</el-button>
|
||||
</div>
|
||||
<div class="right-con">
|
||||
<ReadTemplate v-if="activeMenu == 1" />
|
||||
<ReadTemplate v-if="activeMenu == 1" @changeMenu="changeMenu" />
|
||||
<QuestionAnswer v-if="activeMenu == 2" />
|
||||
<ReadResult v-if="activeMenu == 3" />
|
||||
<ReadResult v-if="activeMenu == 3" :curTemp="curTemp" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import TemplateStudy from '@/components/template-study/index.vue'
|
||||
import ReadTemplate from './container/read-template.vue';
|
||||
import QuestionAnswer from './container/question-answer.vue'
|
||||
import ReadResult from './container/read-result.vue'
|
||||
|
@ -93,6 +95,12 @@ const onClickMenu = (item) => {
|
|||
activeMenu.value = item.value
|
||||
}
|
||||
|
||||
const curTemp = ref([])
|
||||
const changeMenu = (data) =>{
|
||||
activeMenu.value = 3
|
||||
curTemp.value = data
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
|
|
@ -70,19 +70,19 @@ const menuList = [{
|
|||
{
|
||||
name: '课标分析',
|
||||
icon: '#icon-kebiao',
|
||||
path: '/standardanalysis?',
|
||||
path: 'standardanalysis?',
|
||||
id: '1-1'
|
||||
},
|
||||
{
|
||||
name: '教材分析',
|
||||
icon: '#icon-jiaocaixuanze',
|
||||
path: '/textbookAnalysis',
|
||||
path: 'textbookAnalysis',
|
||||
id: '1-2'
|
||||
},
|
||||
{
|
||||
name: '考试分析',
|
||||
icon: '#icon-kaoshi',
|
||||
path: '/examReport',
|
||||
path: 'examReport',
|
||||
id: '1-3'
|
||||
},
|
||||
{
|
||||
|
@ -115,7 +115,7 @@ const menuList = [{
|
|||
icon: '#icon-zuoyesheji',
|
||||
// isOuter: true,
|
||||
// path: '/teaching/classtaskassign?titleName=作业布置&openDialog=newClassTask',
|
||||
path: '/newClassTask',
|
||||
path: 'newClassTask',
|
||||
id: '2-1'
|
||||
},
|
||||
{
|
||||
|
@ -123,13 +123,13 @@ const menuList = [{
|
|||
icon: '#icon-zuoyebuzhi',
|
||||
// isOuter: true,
|
||||
// path: '/teaching/classtaskassign?titleName=作业布置',
|
||||
path: '/classTaskAssign',
|
||||
path: 'classTaskAssign',
|
||||
id: '2-2'
|
||||
},
|
||||
{
|
||||
name: '作业批改',
|
||||
icon: '#icon-zuoyepigai',
|
||||
path: '/classTask',
|
||||
path: 'classTask',
|
||||
id: '2-3'
|
||||
},
|
||||
{
|
||||
|
|
|
@ -170,7 +170,7 @@ const getregisterinfo=()=>{
|
|||
// 关闭
|
||||
const closed=()=>{
|
||||
if (ruleFormRef.value) ruleFormRef.value.resetFields()
|
||||
router.push("/home")
|
||||
router.push("/")
|
||||
}
|
||||
onMounted(async () => {
|
||||
getUser()
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<div>
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="onchange('/model/curriculum')">课标研读</el-button>
|
||||
<el-button type="success" @click="onchange('/model/teaching')">教材研读</el-button>
|
||||
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
|
||||
<!-- <el-button type="success" @click="onchange('/model/teaching')">教材研读</el-button> -->
|
||||
<!-- <el-button type="info" @click="onchange('/model/examination')">考试分析</el-button> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
<template>
|
||||
<div class="book-wrap">
|
||||
<el-scrollbar height="100%">
|
||||
<div class="book-name flex" @click="dialogVisible = true">
|
||||
<span>{{ curBook.data.itemtitle }}</span>
|
||||
<i class="iconfont icon-xiangyou"></i>
|
||||
</div>
|
||||
<div class="book-list" v-loading="treeLoading">
|
||||
<el-tree :data="treeData" accordion :props="defaultProps" node-key="id"
|
||||
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
|
||||
@node-click="handleNodeClick">
|
||||
<template #default="{ node }">
|
||||
<span :title="node.label" class="tree-label">{{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<!--弹窗 选择教材-->
|
||||
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
|
||||
style="border-radius: 10px; padding: 10px 15px;">
|
||||
<template #header>
|
||||
<div class="choose-book-header flex">
|
||||
<span>切换教材</span>
|
||||
<i class="iconfont icon-guanbi" @click="dialogVisible = false"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="textbook-container">
|
||||
<el-scrollbar height="450px">
|
||||
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
|
||||
:key="item.id" @click="changeBook(item)">
|
||||
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
|
||||
<div v-else class="textbook-img">
|
||||
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
|
||||
</div>
|
||||
<span class="book-name">{{ item.itemtitle }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, nextTick, toRaw, reactive } from 'vue';
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { sessionStore } from '@/utils/store'
|
||||
import { useGetSubject } from '@/hooks/useGetSubject'
|
||||
|
||||
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
||||
// 定义要发送的emit事件
|
||||
const emit = defineEmits(['nodeClick', 'changeBook'])
|
||||
let useSubject = null
|
||||
const subjectList = ref([])
|
||||
const dialogVisible = ref(false)
|
||||
// 当前教材下面单元内容数据
|
||||
const treeData = ref([])
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'itemtitle',
|
||||
class: 'textbook-tree'
|
||||
}
|
||||
// 当前选中的教材
|
||||
const curBook = reactive({
|
||||
data: {}
|
||||
})
|
||||
// 当前节点
|
||||
const curNode = reactive({
|
||||
data:{}
|
||||
})
|
||||
const treeLoading = ref(false)
|
||||
// 默认展开的节点
|
||||
const defaultExpandedKeys = ref([])
|
||||
|
||||
//选择教材
|
||||
const changeBook = (data) => {
|
||||
curBook.data = data
|
||||
treeData.value = useSubject.getTreeData(data.id)
|
||||
//切换教材后默认展开第一个并选中
|
||||
nextTick(() =>{
|
||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||
curNode.data = getLastLevelData(treeData.value)[0]
|
||||
handleNodeClick(curNode.data)
|
||||
})
|
||||
// 延迟关闭 视觉上选中
|
||||
setTimeout(() => {
|
||||
dialogVisible.value = false
|
||||
}, 100);
|
||||
}
|
||||
|
||||
const getLastLevelData = (tree) => {
|
||||
let lastLevelData = [];
|
||||
// 递归函数遍历树形结构
|
||||
function traverseTree(nodes) {
|
||||
nodes.forEach((node) => {
|
||||
// 如果当前节点有子节点,继续遍历
|
||||
if (node.children && node.children.length > 0) {
|
||||
traverseTree(node.children);
|
||||
} else {
|
||||
// 如果没有子节点,说明是最后一层的节点
|
||||
lastLevelData.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 调用递归函数开始遍历
|
||||
traverseTree(tree);
|
||||
|
||||
// 返回最后一层的数据
|
||||
return lastLevelData;
|
||||
}
|
||||
// 根据id 拿到父节点数据
|
||||
const findParentByChildId = (treeData, targetNodeId) => {
|
||||
// 递归查找函数
|
||||
// 遍历树中的每个节点
|
||||
for (let node of treeData) {
|
||||
// 检查当前节点的子节点是否包含目标子节点 ID
|
||||
if (node.children && node.children.some(child => child.id === targetNodeId)) {
|
||||
// 如果当前节点的某个子节点的 ID 匹配目标子节点 ID,则当前节点即为父节点
|
||||
return node;
|
||||
}
|
||||
// 如果当前节点没有匹配的子节点,则递归检查当前节点的子节点
|
||||
if (node.children) {
|
||||
let parentNode = findParentByChildId(node.children, targetNodeId);
|
||||
if (parentNode) {
|
||||
return parentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果未找到匹配的父节点,则返回 null 或者适当的默认值
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const handleNodeClick = (data) => {
|
||||
/**
|
||||
* data : 当前节点数据
|
||||
*/
|
||||
let nodeData = cloneDeep(toRaw(data));
|
||||
|
||||
//增加一个label 之前取的label
|
||||
nodeData.label = nodeData.itemtitle
|
||||
// 父级节点 如果当前是一级节点 父级则为null
|
||||
let parent = {
|
||||
id: nodeData.parentid,
|
||||
label: nodeData.parenttitle,
|
||||
itemtitle: nodeData.parenttitle
|
||||
}
|
||||
const parentNode = nodeData.parentid ? parent : null
|
||||
nodeData.parentNode = parentNode
|
||||
let curData = {
|
||||
textBook: {
|
||||
curBookId: curBook.data.id,
|
||||
curBookName: curBook.data.itemtitle,
|
||||
curBookImg: BaseUrl + curBook.data.avartar,
|
||||
curBookPath: curBook.data.fileurl
|
||||
},
|
||||
node: nodeData
|
||||
}
|
||||
// 本地存储:electron-store
|
||||
emit('nodeClick', curData)
|
||||
}
|
||||
onMounted( async () => {
|
||||
treeLoading.value = true
|
||||
try{
|
||||
useSubject = await useGetSubject()
|
||||
subjectList.value = sessionStore.get('subject.bookList')
|
||||
// 当前教材
|
||||
if(sessionStore.get('subject.curBook')){
|
||||
curBook.data = sessionStore.get('subject.curBook')
|
||||
}
|
||||
else{
|
||||
curBook.data = subjectList.value[0]
|
||||
}
|
||||
|
||||
// 章节"树"
|
||||
treeData.value = useSubject.getTreeData(curBook.data.id)
|
||||
|
||||
nextTick(() =>{
|
||||
// 默认展开 选中
|
||||
if(sessionStore.get('subject.curNode')){
|
||||
defaultExpandedKeys.value = sessionStore.get('subject.defaultExpandedKeys')
|
||||
curNode.data = sessionStore.get('subject.curNode')
|
||||
}else{
|
||||
defaultExpandedKeys.value = [treeData.value[0].id]
|
||||
curNode.data = getLastLevelData(treeData.value)[0]
|
||||
}
|
||||
handleNodeClick(curNode.data)
|
||||
})
|
||||
|
||||
} finally{
|
||||
treeLoading.value = false
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.book-wrap {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
.book-name {
|
||||
background-color: #ffffff;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
padding: 0 15px;
|
||||
z-index: 1;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #3b3b3b;
|
||||
cursor: pointer;
|
||||
border-bottom: solid #f4f5f7 1px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.book-list {
|
||||
padding: 45px 10px 0 10px;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.choose-dialog) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.choose-book-header {
|
||||
justify-content: space-between;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
|
||||
.icon-guanbi {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.textbook-container {
|
||||
.textbook-item {
|
||||
padding: 10px 20px;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
.book-name {
|
||||
margin-left: 20px;
|
||||
color: #3b3b3b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f4f7f9;
|
||||
}
|
||||
}
|
||||
|
||||
.active-item {
|
||||
background-color: #f4f7f9;
|
||||
|
||||
.book-name {
|
||||
color: #368fff;
|
||||
font-weight: bold
|
||||
}
|
||||
}
|
||||
|
||||
.textbook-img {
|
||||
width: 55px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tree-node) {
|
||||
.el-tree-node__content {
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: #eaf3ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree-label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
|
||||
background-color: #eaf3ff !important;
|
||||
color: #409EFF
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div style="padding: 10px;">
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
width="350"
|
||||
append-to-body
|
||||
>
|
||||
<div style="display: flex;justify-content: center;">
|
||||
<ChooseTextbook @node-click="nodeClick" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click.stop="save">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineExpose } from 'vue'
|
||||
import ChooseTextbook from './chooseTextbook.vue'
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const getNodeInfo = ref([])
|
||||
|
||||
const openDialog = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const getFullObj = (node) => {
|
||||
const obj = []
|
||||
const recursive = (currentNode) => {
|
||||
// 这里添加节点参数
|
||||
if (currentNode.parentNode) {
|
||||
obj.unshift({id: currentNode.id,title:currentNode.itemtitle})
|
||||
recursive(currentNode.parentNode)
|
||||
} else {
|
||||
obj.unshift({id: currentNode.id,title:currentNode.itemtitle})
|
||||
}
|
||||
}
|
||||
recursive(node)
|
||||
return obj
|
||||
}
|
||||
|
||||
const nodeClick = (data) => {
|
||||
getNodeInfo.value = {
|
||||
textbookId:data.node.rootid,
|
||||
bookList:getFullObj(data.node)
|
||||
}
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
})
|
||||
</script>
|
|
@ -140,6 +140,8 @@
|
|||
<!-- 上课配置 -->
|
||||
<class-start ref="calssRef" @close="closeChange"/>
|
||||
<PptDialog @add-success="addAiPPT" :currentNode="currentNode" :uploadData="uploadData" v-model="pptDialog"/>
|
||||
<!-- 章节弹窗 -->
|
||||
<TreeLog ref="treelogRef"/>
|
||||
<!-- <button @click="test">test</button> -->
|
||||
</template>
|
||||
<script setup>
|
||||
|
@ -235,7 +237,9 @@ export default {
|
|||
isOpenHomework: false,
|
||||
// 当前上课课程
|
||||
activeClass: null,
|
||||
pptDialog: false
|
||||
pptDialog: false,
|
||||
// 打开章节的弹窗
|
||||
treelogRef:null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -183,7 +183,7 @@ const submitForm = async (formEl) => {
|
|||
// 关闭
|
||||
const closed=()=>{
|
||||
if (ruleFormRef.value) ruleFormRef.value.resetFields()
|
||||
router.push("/home")
|
||||
router.push("/")
|
||||
}
|
||||
onMounted(() => {
|
||||
getUser()
|
||||
|
|
Loading…
Reference in New Issue