Compare commits

...

23 Commits

Author SHA1 Message Date
白了个白 b5176e41b3 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-11-12 15:30:30 +08:00
yangws c6b65102bd Merge pull request 'fix:修改格式;' (#28) from yangws_ws into main
Reviewed-on: #28
2024-11-12 14:49:19 +08:00
小杨 2513215a52 fix:修改格式; 2024-11-12 14:47:47 +08:00
zhangxuelin 488ad420d7 Merge pull request 'zxl' (#27) from zxl into main
Reviewed-on: #27
2024-11-12 14:34:17 +08:00
zhangxuelin 5b7007a2fc Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zxl 2024-11-12 14:32:47 +08:00
zhangxuelin 23470cc188 修改认证图标颜色 2024-11-12 14:32:24 +08:00
yangws 82b1258019 Merge pull request 'fix:修改格式传值;' (#26) from yangws_ws into main
Reviewed-on: #26
2024-11-12 11:05:13 +08:00
小杨 52da91f8ec fix:修改格式传值; 2024-11-12 11:04:49 +08:00
yangws 58bc348eed Merge pull request 'fix:不改变原数据;' (#25) from yangws_ws into main
Reviewed-on: #25
2024-11-12 10:46:25 +08:00
小杨 3ade7f7a35 fix:不改变原数据; 2024-11-12 10:46:01 +08:00
yangws 68a6050ba7 Merge pull request 'fix:添加章节组件;' (#24) from yangws_ws into main
Reviewed-on: #24
2024-11-12 09:55:54 +08:00
小杨 866abbcd7e fix:添加章节组件; 2024-11-12 09:55:25 +08:00
yangws b3b4f29d5d Merge pull request 'add:章节节点选择;' (#23) from yangws_ws into main
Reviewed-on: #23
2024-11-12 09:49:34 +08:00
小杨 5dc9e4e20d add:章节节点选择; 2024-11-12 09:49:09 +08:00
zhangxuelin 21cf2c9cfa Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into zxl 2024-11-12 09:31:02 +08:00
zhangxuelin 9031fca09c no message 2024-11-12 09:30:45 +08:00
lyc 5a1b95ea5d Merge pull request 'lyc-dev' (#22) from lyc-dev into main 2024-11-11 17:28:10 +08:00
lyc 0ec8ec7480 路由冲突 2024-11-11 17:28:49 +08:00
baigl 2c1e9923c5 Merge pull request 'baigl' (#21) from baigl into main
Reviewed-on: #21
2024-11-11 17:27:09 +08:00
lyc c3ef8d881e 模板 2024-11-11 17:16:06 +08:00
lyc 756d7fdaa1 模板 2024-11-07 14:14:46 +08:00
lyc d05e50218d 模板 2024-11-06 17:33:32 +08:00
lyc 71e6b74dd4 调整路由 2024-11-05 00:39:06 +08:00
25 changed files with 1484 additions and 192 deletions

View File

@ -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()],

View File

@ -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
})
}

View File

@ -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

View File

@ -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": "消息",

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -1,81 +1,147 @@
<template>
<div class="read-container">
<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-col :span="24">
<div class="template-item template-item-result">
<div class="result-item-header">
<i class="iconfont icon-xiaoxi"></i>
语言建构与运用是指学生在丰富的语言实践中通过主动的积累梳理和整合逐步掌握祖国语言文字特点及其运用规律形成个体言语经验......
</div>
<div class="result-icon-btn flex pl-25">
<div class="flex">
<span><i class="iconfont icon-fuzhi"></i>复制</span>
<span><i class="iconfont icon-bianji-gangbi"></i>写想法</span>
</div>
<div>
<span><i class="iconfont icon-tianjia"></i>加入备课篮</span>
<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>
<div class="line"></div>
<div class="other-msg pl-25">
<div class="other-user flex">
<i class="iconfont icon-touxiang"></i>
重庆市酉阳县二中 - 李丽
</el-col>
</el-row>
<!-- <el-row>
<el-col :span="24">
<div class="template-item template-item-result">
<div class="result-item-header">
<i class="iconfont icon-xiaoxi"></i>
语言建构与运用是指学生在丰富的语言实践中通过主动的积累梳理和整合逐步掌握祖国语言文字特点及其运用规律形成个体言语经验......
</div>
<div class="result-icon-btn flex pl-25">
<div class="flex">
<span><i class="iconfont icon-fuzhi"></i>复制</span>
<span><i class="iconfont icon-bianji-gangbi"></i>写想法</span>
</div>
<div>
<span><i class="iconfont icon-tianjia"></i>加入备课篮</span>
</div>
</div>
<div class="line"></div>
<div class="other-msg pl-25">
<div class="other-user flex">
<i class="iconfont icon-touxiang"></i>
重庆市酉阳县二中 - 李丽
</div>
<div class="other-text flex">北师大王宁老师说语言建构有两方面的含义一是指出于表达思想的目的二是指在个人言语经验的基础上逐步建构起自己的言语体系</div>
</div>
<div class="other-text flex">北师大王宁老师说语言建构有两方面的含义一是指出于表达思想的目的二是指在个人言语经验的基础上逐步建构起自己的言语体系</div>
</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
</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;
}
}
}
}
}

View File

@ -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 {

View File

@ -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>

View File

@ -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'
},
{

View File

@ -170,7 +170,7 @@ const getregisterinfo=()=>{
//
const closed=()=>{
if (ruleFormRef.value) ruleFormRef.value.resetFields()
router.push("/home")
router.push("/")
}
onMounted(async () => {
getUser()

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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: {

View File

@ -183,7 +183,7 @@ const submitForm = async (formEl) => {
//
const closed=()=>{
if (ruleFormRef.value) ruleFormRef.value.resetFields()
router.push("/home")
router.push("/")
}
onMounted(() => {
getUser()