fix/non-blocking-route-navigation
qinzhenpen 2 years ago
parent bf7ef9fafa
commit 3200459f34

@ -0,0 +1,5 @@
{
"htmlWhitespaceSensitivity": "ignore",
"printWidth": 1000
}

@ -23,11 +23,14 @@
"element-plus": "2.2.27", "element-plus": "2.2.27",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"highlight.js": "^11.10.0",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"jsencrypt": "3.3.1", "jsencrypt": "3.3.1",
"marked": "^14.0.0",
"md-editor-v3": "^4.18.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "2.0.22", "pinia": "2.0.22",
"vue": "3.2.45", "vue": "^3.2.47",
"vue-cropper": "1.0.3", "vue-cropper": "1.0.3",
"vue-router": "4.1.4" "vue-router": "4.1.4"
}, },

@ -1,10 +1,12 @@
#app { #app {
.main-container { .main-container {
height: 100%; height:calc(100% - 58px);
transition: margin-left .28s; transition: margin-left .28s;
margin-left: $base-sidebar-width; margin-left: $base-sidebar-width;
position: relative; position: relative;
background: url("@/assets/images/背景.png") no-repeat;
background-size: 100% 100%;
} }
.sidebarHide { .sidebarHide {

@ -42,7 +42,7 @@ $--color-warning: #E6A23C;
$--color-danger: #F56C6C; $--color-danger: #F56C6C;
$--color-info: #909399; $--color-info: #909399;
$base-sidebar-width: 200px; $base-sidebar-width: 239px;
// the :export directive is the magic sauce for webpack // the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass

@ -4,9 +4,11 @@
class="app-wrapper" class="app-wrapper"
:style="{ '--current-color': theme }" :style="{ '--current-color': theme }"
> >
<div class="navTitle"> <div class="title" style="height: 58px;background-color: #072048 !important;">
<div class="navTitle">
<span class="nacber-name">天 择 数 字 营 销 实 训 系 统 </span> <span class="nacber-name">天 择 数 字 营 销 实 训 系 统 </span>
</div> </div>
</div>
<div <div
v-if="device === 'mobile' && sidebar.opened" v-if="device === 'mobile' && sidebar.opened"
class="drawer-bg" class="drawer-bg"
@ -85,14 +87,14 @@ function setLayout() {
position: relative; position: relative;
height: 100%; height: 100%;
width: 100%; width: 100%;
background: url("@/assets/images/背景.png");
&.mobile.openSidebar { &.mobile.openSidebar {
position: fixed; position: fixed;
top: 0; top: 0;
} }
.navTitle { .navTitle {
background: url("@/assets/images/top.png"); background: url("@/assets/images/top.png") no-repeat;
background-size: 100% 100%;
height: 58px; height: 58px;
width: 100%; width: 100%;
z-index: 1; z-index: 1;
@ -111,18 +113,12 @@ function setLayout() {
margin-left: 33px; margin-left: 33px;
margin-top: 3px; margin-top: 3px;
letter-spacing: 1px; letter-spacing: 1px;
} }
} }
.sidebar-container { .sidebar-container {
margin-top: 58px; margin-top: 58px;
} }
.main-container { .main-container {
// margin-top: 58px;
padding-top: 70px;
padding-left: 60px;
padding-right: 20px;
.caseData { .caseData {
position: fixed; position: fixed;
right: 2%; right: 2%;

@ -96,6 +96,20 @@ export const constantRoutes = [
}, },
], ],
}, },
// Bi可视化分析
{
path: "",
component: Layout,
redirect: "/bi",
children: [
{
path: "/bi",
component: () => import("@/views/visual-analysis/index.vue"),
name: "bi",
meta: { title: "BI可视化分析", icon: "dashboard", affix: true },
},
],
},
{ {
path: "", path: "",
component: Layout, component: Layout,

@ -5,7 +5,7 @@ const useAppStore = defineStore(
{ {
state: () => ({ state: () => ({
sidebar: { sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, opened: true,
withoutAnimation: false, withoutAnimation: false,
hide: false hide: false
}, },

@ -16,12 +16,10 @@ axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
const service = axios.create({ const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分 // axios中请求配置有baseURL选项,表示请求URL公共部分
// baseURL: import.meta.env.VITE_APP_BASE_API, // baseURL: import.meta.env.VITE_APP_BASE_API,
// baseURL:'http://192.168.2.28:9868/',
baseURL:'http://192.168.2.16:9868/', baseURL:'http://192.168.2.16:9868/',
// 超时 // 超时
timeout: 10000 timeout: 10000
}) })
// request拦截器 // request拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(config => {
// 是否需要设置 token // 是否需要设置 token

@ -2,38 +2,31 @@
<div class="app-containere"> <div class="app-containere">
<el-scrollbar ref="scrollbar" height="530px"> <el-scrollbar ref="scrollbar" height="530px">
<div class="one-AI" v-if="textOne"> <div class="one-AI" v-if="textOne">
<img src="@/assets/images/ai.png" alt="" /> <img src="@/assets/images/ai.png" alt="" />
<div class="AI-item"> <div class="AI-item">
<h2>你好,我是AI助手</h2> <h2>你好,我是AI助手</h2>
<p>作为你的智能伙伴,我既能写文章、想点子,又能陪你聊天、答疑解惑。</p> <p>作为你的智能伙伴,我既能写文章、想点子,又能陪你聊天、答疑解惑。</p>
<p>想知道我还能做什么?点这里快速上手!可以浏览器收藏网站,下次使用更高效哦 ~</p> <p>想知道我还能做什么?点这里快速上手!可以浏览器收藏网站,下次使用更高效哦 ~</p>
<div class="examples"> <div class="examples">
<div style="flex-basis: 100%;"> <div style="flex-basis: 100%">
<p>你可以试着问我:</p> <p>你可以试着问我:</p>
</div> </div>
<div class="example-item" v-for="item in itemData" :key="item" @click="sendExample(item)"> <div class="example-item" v-for="item in itemData" :key="item" @click="sendExample(item)">
<div style="display:flex"> <div style="display: flex">
<img :src="getImg(item.img)" alt=""> <img :src="getImg(item.img)" alt="" />
<span>{{item.title}}</span> <span>{{ item.title }}</span>
</div>
<p>{{item.text}}</p>
</div> </div>
<p>{{ item.text }}</p>
</div> </div>
</div> </div>
</div> </div>
<div </div>
v-for="(message, index) in messages" <div v-for="(message, index) in messages" :key="index" :class="['message', message.type]">
:key="index"
:class="['message', message.type]"
>
<div class="items-end" v-if="message.type === 'ai-main'"> <div class="items-end" v-if="message.type === 'ai-main'">
<img src="@/assets/images/ai.png" alt="" /> <img src="@/assets/images/ai.png" alt="" />
</div> </div>
<div <div :class="['items-text', message.type]" :id="'text-container-' + message.id">
:class="['items-text', message.type]" <MdPreview :editorId="id" :modelValue="message.displayContent" :codeFoldable="false" theme="dark" />
:id="'text-container-' + message.id"
>
<p>{{ message.displayContent }}</p>
</div> </div>
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -41,19 +34,8 @@
<form @submit.prevent="sendText"> <form @submit.prevent="sendText">
<el-scrollbar height="140px"> <el-scrollbar height="140px">
<div class="inputCode" style="display: flex"> <div class="inputCode" style="display: flex">
<el-input <el-input @keydown="handleKeyDown" v-model="input" type="textarea" rows="6" resize="none" placeholder="文件拖拽进来,使用“/”调用指令,可通过shift+回车换行" />
@keydown="handleKeyDown" <el-button class="sendButton" :disabled="input === '' || butFlag" @click="sendText">
v-model="input"
type="textarea"
rows="6"
resize="none"
placeholder="文件拖拽进来,使用“/”调用指令,可通过shift+回车换行"
/>
<el-button
class="sendButton"
:disabled="input === '' || butFlag"
@click="sendText"
>
<img src="@/assets/images/发送.png" /> <img src="@/assets/images/发送.png" />
</el-button> </el-button>
</div> </div>
@ -62,60 +44,66 @@
</div> </div>
</div> </div>
</template> </template>
<script setup>
<script setup> import { MdPreview, MdCatalog } from "md-editor-v3";
import { ref, reactive } from "vue"; import "md-editor-v3/lib/preview.css";
import * as AI from "@/api/AI.js"; import * as AI from "@/api/AI.js";
import {getAssetsFile} from "@/utils/index.js" import { marked } from "marked";
import "highlight.js/lib/common";
import hljs from "highlight.js";
import { getAssetsFile } from "@/utils/index.js";
const input = ref(""); const input = ref("");
const butFlag = ref(false); const butFlag = ref(false);
const messages = reactive([]); const messages = reactive([]);
const scrollbar = ref(null); const scrollbar = ref(null);
const isRequestPending = ref(false); const isRequestPending = ref(false);
const textOne=ref(true) const textOne = ref(true);
const getImg=(img)=>{ const getImg = (img) => {
return getAssetsFile(img) return getAssetsFile(img);
} };
const itemData=[ const itemData = [
{ {
title:'热点文章编写', title: "热点文章编写",
text:'你是一位编辑,请从数学老师王闰秋鼓励支持其发挥数学…', text: "你是一位编辑,请从数学老师王闰秋鼓励支持其发挥数学…",
img:'热点.png' img: "热点.png",
}, },
{ {
title:'暑期实践活动安排', title: "暑期实践活动安排",
text:'仿照这些优秀范例,设计一份初中生2024年暑期社会实践…', text: "仿照这些优秀范例,设计一份初中生2024年暑期社会实践…",
img:'安排.png' img: "安排.png",
}, },
{ {
title:'PPT内容框架', title: "PPT内容框架",
text:'现在你需要制作一份PPT,你需要按照我给出的主题来准备…', text: "现在你需要制作一份PPT,你需要按照我给出的主题来准备…",
img:'框架.png' img: "框架.png",
},{
title:'最新学术论文检索',
text:'帮我检索一下今年6月的人工智能领域的最新论文,记得给…',
img:'检索.png'
},{
title:'期末备考计划',
text:'你是大学期末备考专家,我是一名学【工商管理】的学生…',
img:'科研论文.png'
},{
title:'科普2024世界人工智能大会',
text:'阅读下面这些链接,科普一下:2024世界人工智能大会究…',
img:'新闻01.png'
}, },
] {
const sendExample=(item)=>{ title: "最新学术论文检索",
textOne.value=false text: "帮我检索一下今年6月的人工智能领域的最新论文,记得给…",
input.value=item.text img: "检索.png",
sendText() },
} {
const sendText = async() => { title: "期末备考计划",
if (input.value.trim() === '' || isRequestPending.value) return; text: "你是大学期末备考专家,我是一名学【工商管理】的学生…",
img: "科研论文.png",
},
{
title: "科普2024世界人工智能大会",
text: "阅读下面这些链接,科普一下:2024世界人工智能大会究…",
img: "新闻01.png",
},
];
const sendExample = (item) => {
textOne.value = false;
input.value = item.text;
sendText();
};
const sendText = async () => {
if (input.value.trim() === "" || isRequestPending.value) return;
if (input.value.trim() === "") return; if (input.value.trim() === "") return;
isRequestPending.value = true; isRequestPending.value = true;
let text = input.value; let text = input.value;
textOne.value=false textOne.value = false;
input.value = ""; input.value = "";
// 添加用户消息 // 添加用户消息
@ -134,34 +122,52 @@ const sendText = async() => {
}, },
]; ];
const messageId = Date.now(); const messageId = Date.now();
const newMessage = reactive({ id: messageId, type: 'ai-main', displayContent: '' }); 0;
const newMessage = reactive({ id: messageId, type: "ai-main", displayContent: "" });
messages.push(newMessage); messages.push(newMessage);
const resp=await fetch('http://192.168.2.16:9868/api/bigModule/createArticleByAi',{ const resp = await fetch("http://192.168.2.28:9868/api/bigModule/createArticleByAi", {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify(sendData), body: JSON.stringify(sendData),
}) });
const reader=resp.body.getReader() const reader = resp.body.getReader();
while(1){ while (1) {
const {done,value}=await reader.read() const { done, value } = await reader.read();
if(done){ if (done) {
break break;
} }
const decoder=new TextDecoder() const decoder = new TextDecoder("utf-8");
const tet=decoder.decode(value) const tet = decoder.decode(value, { stream: true });
newMessage.displayContent+=tet
await typeText(newMessage, tet);
} }
isRequestPending.value = false; isRequestPending.value = false;
nextTick(() => { nextTick(() => {
setTimeout(() => { setTimeout(() => {
const scrollContainer = scrollbar.value?.$el.querySelector('.el-scrollbar__wrap'); const scrollContainer = scrollbar.value?.$el.querySelector(".el-scrollbar__wrap");
if (scrollContainer) { if (scrollContainer) {
scrollContainer.scrollTop = scrollContainer.scrollHeight; scrollContainer.scrollTop = scrollContainer.scrollHeight;
} }
}, 10); }, 10);
}); });
};
const typeText = (message, text) => {
return new Promise((resolve) => {
let index = 0;
function type() {
if (index < text.length) {
const nextChar = text[index];
message.displayContent += nextChar;
index++;
setTimeout(type, 30); // 调整打字速度
} else {
resolve();
}
}
type();
});
}; };
const handleKeyDown = (event) => { const handleKeyDown = (event) => {
if (event.key === "Enter" && !event.shiftKey) { if (event.key === "Enter" && !event.shiftKey) {
@ -170,12 +176,12 @@ const handleKeyDown = (event) => {
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.app-containere { .app-containere {
margin: 10px 4px 10px 10px; margin: 10px 4px 10px 10px;
box-shadow: 0px 0px 13px 0px #0452c6; border: 1px solid #0452c6;
border-radius: 10px; background-color: rgba(0, 0, 0, 0.5);
padding: 25px; padding: 25px;
} }
.message { .message {
@ -247,50 +253,69 @@ const handleKeyDown = (event) => {
margin-right: 15px; margin-right: 15px;
} }
} }
.one-AI{ .one-AI {
display: flex; display: flex;
width: 100%; width: 100%;
img{ img {
width: 31px; width: 31px;
height: 31px; height: 31px;
} }
.AI-item{ .AI-item {
width: calc(100% - 200px); width: calc(100% - 200px);
margin-left: 10px; margin-left: 10px;
background: #011031; background: #011031;
border-radius: 10px; border-radius: 10px;
border: 1px solid #003E7C; border: 1px solid #003e7c;
padding: 10px 15px; padding: 10px 15px;
color:#FFFFFF; color: #ffffff;
p{ p {
font-size: 12px; font-size: 12px;
} }
.examples{ .examples {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 60px; margin-top: 60px;
width: calc(100% - 300px); width: calc(100% - 300px);
.example-item{ .example-item {
margin: 0 25px 15px 0; margin: 0 25px 15px 0;
cursor: pointer; cursor: pointer;
width: 349px; width: 349px;
height: 72px; height: 72px;
background: #002C5B; background: #002c5b;
border-radius: 10px; border-radius: 10px;
padding: 10px; padding: 10px;
img{ img {
width: 20px; width: 20px;
height: 20px; height: 20px;
} }
span{ span {
margin-left: 5px; margin-left: 5px;
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 16px;
color: #FFFFFF; color: #ffffff;
} }
} }
} }
} }
} }
.md-editor-dark,
.md-editor-modal-container[data-theme="dark"] {
--md-color: #ffffff;
--md-bk-color: transparent;
--md-border-color: transparent;
--md-border-color-hover: transparent;
--md-border-color-focus: transparent;
--md-border-color-active: transparent;
--md-border-color-disabled: transparent;
--md-border-color-focus-inner: transparent;
--md-border-color-hover-inner: transparent;
--md-border-color-active-inner: transparent;
--md-border-color-disabled-inner: transparent;
--md-border-color-focus-outer: transparent;
--md-border-color-hover-outer: transparent;
--md-border-color-active-outer: transparent;
}
.md-editor-preview hr {
border-color: #ffffff;
}
</style> </style>

@ -1,5 +1,5 @@
<template> <template>
<div class="app-top"> <div class="app-top app-container">
<el-button v-for="(item,index) in imgData" :key="index" :class="item.img" @click="clickIndx(index)">{{item.text}}</el-button> <el-button v-for="(item,index) in imgData" :key="index" :class="item.img" @click="clickIndx(index)">{{item.text}}</el-button>
</div> </div>
<component :is="page"></component> <component :is="page"></component>

@ -0,0 +1,19 @@
<!--
* @Author: qinzhenpen qzp1807@126.com
* @Date: 2024-08-16 09:16:26
* @LastEditors: qinzhenpen qzp1807@126.com
* @LastEditTime: 2024-08-16 09:18:05
* @FilePath: \vue3\src\views\visual-analysis\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div>
<iframe src="http://118.31.7.2:96" width="100%" height="900" scrolling="no" frameborder="0"></iframe>
</div>
</template>
<script setup>
</script>
<style lang='scss' scoped>
</style>
Loading…
Cancel
Save