Compare commits

..

22 Commits

Author SHA1 Message Date
lyc fae380f9c6 Merge pull request 'edit' (#71) from lyc-dev into main 2024-11-27 11:21:39 +08:00
lyc a68fb21203 edit 2024-11-27 11:21:18 +08:00
朱浩 2c6ccd893b s生成PPT 2024-11-27 11:03:35 +08:00
朱浩 540e5f0179 Merge remote-tracking branch 'origin/main' 2024-11-27 10:59:44 +08:00
朱浩 8a8f90ce16 s生成PPT 2024-11-27 10:58:29 +08:00
朱浩 050f87a84a s生成PPT 2024-11-27 10:58:00 +08:00
yangws c338f34127 Merge pull request 'yws_dev' (#70) from yws_dev into main
Reviewed-on: #70
2024-11-27 10:52:14 +08:00
小杨 5b49eef8e8 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into yws_dev 2024-11-27 10:51:50 +08:00
小杨 1060929532 fix:修改个人中心默认修改; 2024-11-27 10:51:43 +08:00
baigl 448c4d979f Merge pull request 'baigl' (#69) from baigl into main
Reviewed-on: #69
2024-11-27 10:44:11 +08:00
白了个白 222f558807 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-11-27 10:41:03 +08:00
白了个白 ef5dfc8ed1 习题筛选修改 2024-11-27 10:39:21 +08:00
yangws 733855b95e Merge pull request 'fix:个人中心修改学科问题;' (#68) from yws_dev into main
Reviewed-on: #68
2024-11-27 10:30:07 +08:00
小杨 7a6f57a9b7 fix:个人中心修改学科问题; 2024-11-27 10:29:39 +08:00
白了个白 48571086c3 1 2024-11-27 10:12:39 +08:00
白了个白 b967c684d6 1 2024-11-27 10:11:35 +08:00
白了个白 84e8acdef7 Merge branch 'zouyf_dev' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-11-27 10:03:38 +08:00
白了个白 d8c4556393 1 2024-11-27 10:01:53 +08:00
白了个白 42f24041db Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-11-25 11:11:20 +08:00
白了个白 b081000f11 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-11-25 09:41:57 +08:00
白了个白 124b584e38 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk_WS into baigl 2024-11-22 15:12:20 +08:00
白了个白 639178c827 作业布置:查看推送记录bug修复 2024-11-22 10:02:04 +08:00
15 changed files with 953 additions and 184 deletions

View File

@ -16,6 +16,7 @@
<script setup> <script setup>
import { ref, onMounted, nextTick } from 'vue' import { ref, onMounted, nextTick } from 'vue'
import { sessionStore } from '@/utils/store'
import PDF from '@/components/PdfJs/index.vue' import PDF from '@/components/PdfJs/index.vue'
import LeftDialog from './left-dialog.vue' import LeftDialog from './left-dialog.vue'
@ -31,8 +32,8 @@ const onClick = () => {
const pdfUrl = ref('') const pdfUrl = ref('')
onMounted(async () => { onMounted(async () => {
await nextTick() await nextTick()
const { fileurl } = props.curNode let data = sessionStore.get('subject.curBook')
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf') pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + data.fileurl.replace('.txt', '.pdf')
}) })
</script> </script>

View File

@ -82,9 +82,10 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, computed, onUnmounted } from 'vue' import { ref, reactive, onMounted, watch, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { tempSave, completion, modelList, removeChildTemp, tempResult } from '@/api/mode/index' import { tempSave, completion, modelList, removeChildTemp, tempResult } from '@/api/mode/index'
import { sessionStore } from '@/utils/store'
import keywordDialog from './keyword-dialog.vue'; import keywordDialog from './keyword-dialog.vue';
import AdjustDialog from './adjust-dialog.vue' import AdjustDialog from './adjust-dialog.vue'
import EditDialog from './edit-dialog.vue' import EditDialog from './edit-dialog.vue'
@ -187,7 +188,7 @@ const changeTemplate = (val) => {
const removeItem = async (item, isChild) => { const removeItem = async (item, isChild) => {
/** /**
* item: 当前操作的模板 * item: 当前操作的模板
* isChild: 子模板中的移除为 true 否则为false * isChild: 子模板中的移除为 true
*/ */
if (item.ex3 != '1') { if (item.ex3 != '1') {
ElMessageBox.confirm( ElMessageBox.confirm(
@ -213,15 +214,11 @@ const removeItem = async (item, isChild) => {
}) })
} }
else { else {
editKeyWord(item,!isChild) editKeyWord(item,!isChild)
} }
} }
// Ai // Ai
const curIndex = ref(-1) const curIndex = ref(-1)
const isAdjust = ref(false) const isAdjust = ref(false)
@ -240,11 +237,21 @@ const onEdit = (index, item) => {
} }
const modeType = computed(() => { const modeType = ref('课标')
if (props.type == 1) return '课标'; watch(() => props.type, (newVal) => {
if (props.type == 2) return '教材'; if (newVal == 1){
if (props.type == 3) return '考试'; modeType.value = '课标'
}) }
if (newVal == 2){
modeType.value = '教材'
}
if (newVal == 2){
modeType.value = '考试'
}
}, { immediate: false })
// //
const params = reactive( const params = reactive(
{ {
@ -256,7 +263,7 @@ const params = reactive(
const againResult = async (index, item) => { const againResult = async (index, item) => {
try { try {
childTempList.value[index].loading = true childTempList.value[index].loading = true
params.prompt = `按照${item.name}的要求,针对${props.curNode.edustage}${props.curNode.edusubject}${modeType}${props.curNode.itemtitle}进行教学分析` params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${curNode.itemtitle}进行教学分析`
const { data } = await completion(params) const { data } = await completion(params)
let answer = data.answer let answer = data.answer
childTempList.value[index].oldAnswer = answer childTempList.value[index].oldAnswer = answer
@ -271,7 +278,7 @@ const getCompletion = async () => {
for (let item of childTempList.value) { for (let item of childTempList.value) {
try { try {
item.loading = true item.loading = true
params.prompt = `按照${item.name}的要求,针对${props.curNode.edustage}${props.curNode.edusubject}${modeType}${props.curNode.itemtitle}进行教学分析` params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}${modeType.value}${curNode.itemtitle}进行教学分析`
const { data } = await completion(params) const { data } = await completion(params)
let answer = data.answer let answer = data.answer
item.oldAnswer = answer item.oldAnswer = answer
@ -286,7 +293,7 @@ const getCompletion = async () => {
// //
const onSaveTemp = (item) => { const onSaveTemp = (item) => {
const data = { const data = {
mainModelId: props.tempId, mainModelId: curTemplate.id,
modelId: item.id, modelId: item.id,
examDocld: '', examDocld: '',
content: item.oldAnswer content: item.oldAnswer
@ -324,10 +331,12 @@ emitter.on('onGetMain', () => {
}) })
const curNode = reactive({})
onMounted(() => { onMounted(() => {
getTemplateList() getTemplateList()
let jsonKey = `${modeType}-${props.curNode.edustage}-${props.curNode.edusubject}` let data = sessionStore.get('subject.curNode')
Object.assign(curNode, data);
let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
params.dataset_id = dataSetJson[jsonKey] params.dataset_id = dataSetJson[jsonKey]
}) })

View File

@ -6,9 +6,34 @@ import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuesti
const useClassTaskStore = defineStore('classTask',{ const useClassTaskStore = defineStore('classTask',{
state: () => ({ state: () => ({
classListIds: [], classListIds: [],
jyCT: [], entpCourseWorkTypeList: [
jySO: [], {value: 0, label: "不限"},
jyYear: [], {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) {
@ -32,15 +57,12 @@ const useClassTaskStore = defineStore('classTask',{
Promise.all([getJYYear(), getJYSO(education), getJYCT(education)]) Promise.all([getJYYear(), getJYSO(education), getJYCT(education)])
.then(results => { .then(results => {
console.log('更新第三方题源+题型succ:', results); console.log('更新第三方题源+题型succ:', results);
this.jyYear = results[0]; this.entpCourseWorkYearList = results[0];
this.jyCT = results[2]; this.entpCourseWorkTypeList = results[2];
this.jySO = results[1]; this.entpCourseWorkGroupList = results[1];
}) })
.catch(error => { .catch(error => {
console.error('更新第三方题源+题型err:', error); console.error('更新第三方题源+题型err:', error);
this.jyYear = [];
this.jySO = [];
this.jyCT = [];
}); });
}, },
}, },

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

@ -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(name);
if (jyCT.length == 0) {
ElMessage.error('获取题型失败!');
return;
}
entpCourseWorkTypeList.value = jyCT;
//
entpCourseWorkYearList.value = JYApiListOriginYear();
entpCourseWorkGroupList.value = await JYApiListSO(name);
}) })
// const refreshData = () => { // const refreshData = () => {

View File

@ -125,18 +125,25 @@ 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' 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 classTaskStore = useClassTaskStore(); const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const props = defineProps({ const props = defineProps({
bookobj: { bookobj: {
@ -145,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'},
]); // -
// //
@ -221,19 +198,9 @@ const dlgImportSingle = reactive({
}) })
onMounted(() => { onMounted(() => {
if (classTaskStore.jyCT.length>0) {
entpCourseWorkTypeList.value = classTaskStore.jyCT;
}
if (classTaskStore.jySO.length>0) {
entpCourseWorkGroupList.value = classTaskStore.jySO;
}
if (classTaskStore.jyYear.length>0) {
entpCourseWorkYearList.value = classTaskStore.jyYear;
}
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) } });
} }

View File

@ -417,7 +417,11 @@ import useUserStore from '@/store/modules/user'
import useClassTaskStore from '@/store/modules/classTask' import useClassTaskStore from '@/store/modules/classTask'
const userStore = useUserStore().user const userStore = useUserStore().user
const classTaskStore = useClassTaskStore(); const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
// emit // emit
@ -619,15 +623,15 @@ onMounted(() => {
}; };
// //
if (classTaskStore.jyCT.length>0) { if (entpCourseWorkTypeList.length>0) {
const flagDict = ['单选题', '多选题', '判断题', '填空题']; const flagDict = ['单选题', '多选题', '判断题', '填空题'];
fromOptions.type = classTaskStore.jyCT.filter(item => flagDict.includes(item.label)); fromOptions.type = entpCourseWorkTypeList.filter(item => flagDict.includes(item.label));
} }
if (classTaskStore.jySO.length>0) { if (entpCourseWorkGroupList.length>0) {
fromOptions.flag = classTaskStore.jySO; fromOptions.flag = entpCourseWorkGroupList;
} }
// if (classTaskStore.jyYear.length>0) { // if (entpCourseWorkYearList.length>0) {
// yearList.value = classTaskStore.jyYear; // yearList.value = entpCourseWorkYearList;
// } // }
}) })

View File

@ -125,7 +125,11 @@ import useClassTaskStore from '@/store/modules/classTask'
const emit = defineEmits(['addQuiz']) const emit = defineEmits(['addQuiz'])
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const userStore = useUserStore().user const userStore = useUserStore().user
const classTaskStore = useClassTaskStore(); const {
entpCourseWorkTypeList,
entpCourseWorkGroupList,
entpCourseWorkYearList
} = useClassTaskStore();
const props = defineProps({ const props = defineProps({
bookobj: { bookobj: {
@ -134,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'},
]); // -
// //
@ -209,16 +183,6 @@ const workResource = reactive({
}); // }); //
onMounted(() => { onMounted(() => {
if (classTaskStore.jyCT.length>0) {
entpCourseWorkTypeList.value = classTaskStore.jyCT;
}
if (classTaskStore.jySO.length>0) {
entpCourseWorkGroupList.value = classTaskStore.jySO;
}
if (classTaskStore.jyYear.length>0) {
entpCourseWorkYearList.value = classTaskStore.jyYear;
}
debounceQueryData(); // debounceQueryData(); //
}) })

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

@ -59,24 +59,27 @@ const getSubject = () => {
// //
const handleUserEduStage = (item) => { const handleUserEduStage = (item) => {
userStore.edustage = item userStore.edustage = item
sessionStore.set('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('edusubject',item) sessionStore.set('edusubjectSelf',item)
} }
onMounted(() => { onMounted(() => {
getSubject() getSubject()

View File

@ -165,9 +165,8 @@ setTimeout(() => {
function submit() { function submit() {
proxy.$refs.userRef.validate((valid) => { proxy.$refs.userRef.validate((valid) => {
if (valid) { if (valid) {
userStore.user.edusubject = sessionStore.get('edusubject') userStore.user.edusubject = sessionStore.get('edusubjectSelf') ? sessionStore.get('edusubjectSelf') : userStore.user.edusubject
userStore.user.edustage = sessionStore.get('edustage') userStore.user.edustage = sessionStore.get('edustageSelf') ? sessionStore.get('edustageSelf') : userStore.user.edustage
console.log('userStore更新前', userStore.user);
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(() => {

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,6 +49,7 @@
</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>
@ -59,8 +60,9 @@ import EditDialog from './edit-dialog.vue'
import AdjustDialog from './adjust-dialog.vue' import AdjustDialog from './adjust-dialog.vue'
import { completion, tempResult } from '@/api/mode/index.js' import { completion, tempResult } from '@/api/mode/index.js'
import { dataSetJson } from '@/utils/comm.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') console.log(item, 'item')
@ -95,6 +97,11 @@ const params = reactive(
dataset_id: '' 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
@ -242,4 +249,4 @@ onUnmounted(() => {
} }
} }
} }
</style> </style>