Merge branch 'feature/dsw' of https://git.nianxx.cn/duanshuwen/zn-ai into feature/lishaohua
This commit is contained in:
@@ -16,7 +16,6 @@ export function authOauth2TokenUsingPost({
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Basic Y3VzdG9tUEM6Y3VzdG9tUEM=',
|
||||
},
|
||||
data: body,
|
||||
...(options || {}),
|
||||
|
||||
@@ -21,17 +21,107 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { menus } from '@constant/menus'
|
||||
import { useRouter } from "vue-router"
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { menus, type MenuItem } from '@constant/menus'
|
||||
|
||||
const router = useRouter()
|
||||
const currentId = ref(1)
|
||||
const tabMap = new Map<number, string>()
|
||||
const cleanupListeners: (() => void)[] = []
|
||||
|
||||
const handleClick = async (item: any) => {
|
||||
const getHtmlPath = (menuUrl: string) => {
|
||||
const cleanUrl = menuUrl.startsWith('/') ? menuUrl.slice(1) : menuUrl
|
||||
let filename = ''
|
||||
switch (cleanUrl) {
|
||||
case 'home':
|
||||
filename = 'home.html'
|
||||
break
|
||||
case 'knowledge':
|
||||
filename = 'knowledge.html'
|
||||
break
|
||||
case 'task':
|
||||
filename = 'task.html'
|
||||
break
|
||||
case 'setting':
|
||||
filename = 'setting.html'
|
||||
break
|
||||
default:
|
||||
filename = 'home.html'
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
return `/html/${filename}`
|
||||
}
|
||||
return filename
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (window.api && window.api.tabs) {
|
||||
const cleanupClosed = window.api.tabs.on('tab-closed', (payload: any) => {
|
||||
const { tabId } = payload
|
||||
for (const [menuId, id] of tabMap.entries()) {
|
||||
if (id === tabId) {
|
||||
tabMap.delete(menuId)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
if (cleanupClosed) cleanupListeners.push(cleanupClosed)
|
||||
|
||||
const cleanupCreated = window.api.tabs.on('tab-created', (tab: any) => {
|
||||
for (const menu of menus) {
|
||||
const targetHtml = getHtmlPath(menu.url)
|
||||
if (tab.url.includes(targetHtml)) {
|
||||
tabMap.set(menu.id, tab.id)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
if (cleanupCreated) cleanupListeners.push(cleanupCreated)
|
||||
|
||||
try {
|
||||
const tabs = await window.api.tabs.list()
|
||||
if (tabs && tabs.length > 0) {
|
||||
for (const tab of tabs) {
|
||||
for (const menu of menus) {
|
||||
const targetHtml = getHtmlPath(menu.url)
|
||||
if (tab.url.includes(targetHtml)) {
|
||||
tabMap.set(menu.id, tab.id)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to sync tabs', e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanupListeners.forEach(fn => fn())
|
||||
})
|
||||
|
||||
const handleClick = async (item: MenuItem) => {
|
||||
console.log("🚀 ~ handleClick ~ item:", item)
|
||||
currentId.value = item.id
|
||||
router.push(item.url);
|
||||
|
||||
const existingTabId = tabMap.get(item.id)
|
||||
if (existingTabId) {
|
||||
await window.api.tabs.switch(existingTabId)
|
||||
} else {
|
||||
const htmlFile = getHtmlPath(item.url)
|
||||
const targetUrl = new URL(htmlFile, window.location.href).href
|
||||
|
||||
try {
|
||||
const tabInfo = await window.api.tabs.create(targetUrl)
|
||||
if (tabInfo && tabInfo.id) {
|
||||
tabMap.set(item.id, tabInfo.id)
|
||||
await window.api.tabs.switch(tabInfo.id)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to create tab', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export const menus: MenuItem[] = [
|
||||
icon: RiApps2AiLine,
|
||||
color: '#525866',
|
||||
activeColor: '#2B7FFF',
|
||||
url: '/stock',
|
||||
url: '/task',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
|
||||
@@ -19,10 +19,12 @@ import 'element-plus/dist/index.css'
|
||||
// 引入全局组件
|
||||
import HeaderBar from '@components/HeaderBar/index.vue'
|
||||
import DragRegion from '@components/DragRegion/index.vue'
|
||||
import Layout from '@components/Layout/index.vue'
|
||||
|
||||
const components: Plugin = (app) => {
|
||||
app.component('HeaderBar', HeaderBar);
|
||||
app.component('DragRegion', DragRegion);
|
||||
app.component('Layout', Layout);
|
||||
}
|
||||
|
||||
// 创建 Vue 应用实例
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import router from './router'
|
||||
import { isPathMatch } from '@utils/validate'
|
||||
import { getToken } from '@utils/auth'
|
||||
import { Session } from '@renderer/utils/storage'
|
||||
|
||||
// 白名单
|
||||
const whiteList = ['/login', '/register']
|
||||
@@ -8,7 +8,7 @@ const whiteList = ['/login', '/register']
|
||||
const isWhiteList = (path: string) => whiteList.some(pattern => isPathMatch(pattern, path))
|
||||
|
||||
router.beforeEach((to: any, _from: any, next: any) => {
|
||||
if(getToken()) {
|
||||
if(Session.getToken()) {
|
||||
// has token
|
||||
if (to.path === '/login') {
|
||||
next({path: '/home'})
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { authOauth2TokenUsingPost } from "@renderer/api"
|
||||
import { getToken, setToken, removeToken } from '@utils/auth'
|
||||
import { Session } from '@utils/storage'
|
||||
import { encryption } from '@utils/other'
|
||||
|
||||
export const useUserStore = defineStore('userInfo', {
|
||||
state: () => ({
|
||||
token: getToken(),
|
||||
token: Session.get('token'),
|
||||
}),
|
||||
|
||||
actions: {
|
||||
@@ -15,15 +16,37 @@ export const useUserStore = defineStore('userInfo', {
|
||||
* @param {Object} data - 登录数据
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async login(data: LoginForm) {
|
||||
async login(data: any) {
|
||||
data.grant_type = 'password';
|
||||
data.scope = 'server';
|
||||
|
||||
// const { VITE_OAUTH2_PASSWORD_CLIENT, VITE_PWD_ENC_KEY } = (import.meta as any).env
|
||||
// const basicAuth = 'Basic ' + window.btoa(VITE_OAUTH2_PASSWORD_CLIENT);
|
||||
|
||||
// Session.set('basicAuth', basicAuth);
|
||||
|
||||
// let encPassword = data.password;
|
||||
// 密码加密
|
||||
// if (VITE_PWD_ENC_KEY) {
|
||||
// encPassword = encryption(data.password, VITE_PWD_ENC_KEY);
|
||||
// }
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
authOauth2TokenUsingPost({body: {...data, clientId: ''}})
|
||||
authOauth2TokenUsingPost({
|
||||
body: { clientId: '', ...data },
|
||||
options: {
|
||||
headers: {
|
||||
isToken: true,
|
||||
// Authorization: basicAuth,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Basic Y3VzdG9tUEM6Y3VzdG9tUEM='
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((res: any) => {
|
||||
// 存储token 信息
|
||||
setToken(res.access_token)
|
||||
// 存储token 信息
|
||||
Session.set('token', res.access_token);
|
||||
Session.set('refresh_token', res.refresh_token);
|
||||
resolve(res)
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const TokenKey = 'Nianxx-Token'
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token: string) {
|
||||
return Cookies.set(TokenKey, token)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
}
|
||||
32
src/renderer/utils/other.ts
Normal file
32
src/renderer/utils/other.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as CryptoJS from 'crypto-js';
|
||||
|
||||
/**
|
||||
*加密处理
|
||||
*/
|
||||
export function encryption(src: string, keyWord: string) {
|
||||
const key = CryptoJS.enc.Utf8.parse(keyWord);
|
||||
// 加密
|
||||
var encrypted = CryptoJS.AES.encrypt(src, key, {
|
||||
iv: key,
|
||||
mode: CryptoJS.mode.CFB,
|
||||
padding: CryptoJS.pad.NoPadding,
|
||||
});
|
||||
return encrypted.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
* @param {*} params 参数列表
|
||||
* @returns 明文
|
||||
*/
|
||||
export function decryption(src: string, keyWord: string) {
|
||||
const key = CryptoJS.enc.Utf8.parse(keyWord);
|
||||
// 解密逻辑
|
||||
var decryptd = CryptoJS.AES.decrypt(src, key, {
|
||||
iv: key,
|
||||
mode: CryptoJS.mode.CFB,
|
||||
padding: CryptoJS.pad.NoPadding,
|
||||
});
|
||||
|
||||
return decryptd.toString(CryptoJS.enc.Utf8);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import axios from 'axios'
|
||||
import cache from '@utils/cache'
|
||||
import errorCode from '@constant/errorCode'
|
||||
import { ElNotification , ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { getToken } from '@utils/auth'
|
||||
import { Session } from '@renderer/utils/storage'
|
||||
import { tansParams } from '@utils/tansParams'
|
||||
|
||||
// 获取.env中的服务地址
|
||||
@@ -28,9 +28,12 @@ instance.interceptors.request.use(
|
||||
const isToken = (config.headers || {}).isToken === false
|
||||
// 是否需要防止数据重复提交
|
||||
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
|
||||
if (getToken() && !isToken) {
|
||||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
const token = Session.getToken();
|
||||
|
||||
if (token && !isToken) {
|
||||
config.headers['Authorization'] = `Bearer ${token}` // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
|
||||
// get请求映射params参数
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = config.url + '?' + tansParams(config.params)
|
||||
|
||||
85
src/renderer/utils/storage.ts
Normal file
85
src/renderer/utils/storage.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
/**
|
||||
* window.localStorage 浏览器永久缓存
|
||||
* @method set 设置永久缓存
|
||||
* @method get 获取永久缓存
|
||||
* @method remove 移除永久缓存
|
||||
* @method clear 移除全部永久缓存
|
||||
*/
|
||||
export const Local = {
|
||||
// 查看 v2.4.3版本更新日志
|
||||
setKey(key: string) {
|
||||
// @ts-ignore
|
||||
return `${__NEXT_NAME__}:${key}`;
|
||||
},
|
||||
|
||||
// 设置永久缓存
|
||||
set<T>(key: string, val: T) {
|
||||
window.localStorage.setItem(Local.setKey(key), JSON.stringify(val));
|
||||
},
|
||||
|
||||
// 获取永久缓存
|
||||
get(key: string) {
|
||||
let json = <string>window.localStorage.getItem(Local.setKey(key));
|
||||
return JSON.parse(json);
|
||||
},
|
||||
|
||||
// 移除永久缓存
|
||||
remove(key: string) {
|
||||
window.localStorage.removeItem(Local.setKey(key));
|
||||
},
|
||||
|
||||
// 移除全部永久缓存
|
||||
clear() {
|
||||
window.localStorage.clear();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* window.sessionStorage 浏览器临时缓存
|
||||
* @method set 设置临时缓存
|
||||
* @method get 获取临时缓存
|
||||
* @method remove 移除临时缓存
|
||||
* @method clear 移除全部临时缓存
|
||||
*/
|
||||
export const Session = {
|
||||
// 设置临时缓存
|
||||
set(key: string, val: any) {
|
||||
if (key === 'token' || key === 'refresh_token') {
|
||||
Cookies.set(key, val);
|
||||
}
|
||||
window.sessionStorage.setItem(key, JSON.stringify(val));
|
||||
},
|
||||
|
||||
// 获取临时缓存
|
||||
get(key: string) {
|
||||
if (key === 'token' || key === 'refresh_token') return Cookies.get(key);
|
||||
let json = <string>window.sessionStorage.getItem(key);
|
||||
return JSON.parse(json);
|
||||
},
|
||||
|
||||
// 移除临时缓存
|
||||
remove(key: string) {
|
||||
if (key === 'token' || key === 'refresh_token') return Cookies.remove(key);
|
||||
window.sessionStorage.removeItem(key);
|
||||
},
|
||||
|
||||
// 移除全部临时缓存
|
||||
clear() {
|
||||
Cookies.remove('token');
|
||||
Cookies.remove('refresh_token');
|
||||
Cookies.remove('tenantId');
|
||||
window.sessionStorage.clear();
|
||||
},
|
||||
|
||||
// 获取当前存储的 token
|
||||
getToken() {
|
||||
return this.get('token');
|
||||
},
|
||||
|
||||
// 获取当前的租户
|
||||
getTenant() {
|
||||
return Local.get('tenantId') ? Local.get('tenantId') : 1;
|
||||
},
|
||||
};
|
||||
8
src/renderer/views/home/HomeTab.vue
Normal file
8
src/renderer/views/home/HomeTab.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="bg-white h-full w-full p-[20px]">
|
||||
<h1>首页 Dashboard</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
40
src/renderer/views/home/tab.ts
Normal file
40
src/renderer/views/home/tab.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { createApp, type Plugin } from "vue"
|
||||
import errorHandler from '@utils/errorHandler'
|
||||
|
||||
// 引入 Element Plus 组件库
|
||||
import ElementPlus from 'element-plus'
|
||||
import locale from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
// 引入 i18n 插件
|
||||
import i18n from '@renderer/i18n'
|
||||
|
||||
import Home from './HomeTab.vue'
|
||||
|
||||
// 样式文件隔离
|
||||
import '@renderer/styles/index.css'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
// 引入全局组件
|
||||
import HeaderBar from '@components/HeaderBar/index.vue'
|
||||
import DragRegion from '@components/DragRegion/index.vue'
|
||||
import Layout from '@components/Layout/index.vue'
|
||||
|
||||
const components: Plugin = (app) => {
|
||||
app.component('HeaderBar', HeaderBar);
|
||||
app.component('DragRegion', DragRegion);
|
||||
app.component('Layout', Layout);
|
||||
}
|
||||
|
||||
// 创建 Vue 应用实例
|
||||
const app = createApp(Home);
|
||||
const pinia = createPinia();
|
||||
|
||||
// 使用 Pinia 状态管理
|
||||
app.use(pinia);
|
||||
app.use(ElementPlus, { locale })
|
||||
app.use(components)
|
||||
app.use(i18n)
|
||||
app.use(errorHandler)
|
||||
|
||||
// 挂载应用到 DOM
|
||||
app.mount("#app");
|
||||
38
src/renderer/views/knowledge/index.ts
Normal file
38
src/renderer/views/knowledge/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createApp, type Plugin } from "vue"
|
||||
import errorHandler from '@utils/errorHandler'
|
||||
|
||||
// 引入 Element Plus 组件库
|
||||
import ElementPlus from 'element-plus'
|
||||
import locale from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
// 引入 i18n 插件
|
||||
import i18n from '@renderer/i18n'
|
||||
|
||||
import Knowledge from './index.vue'
|
||||
|
||||
// 样式文件隔离
|
||||
import '@renderer/styles/index.css'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
// 引入全局组件
|
||||
import HeaderBar from '@components/HeaderBar/index.vue'
|
||||
import DragRegion from '@components/DragRegion/index.vue'
|
||||
|
||||
const components: Plugin = (app) => {
|
||||
app.component('HeaderBar', HeaderBar);
|
||||
app.component('DragRegion', DragRegion);
|
||||
}
|
||||
|
||||
// 创建 Vue 应用实例
|
||||
const app = createApp(Knowledge);
|
||||
const pinia = createPinia();
|
||||
|
||||
// 使用 Pinia 状态管理
|
||||
app.use(pinia);
|
||||
app.use(ElementPlus, { locale })
|
||||
app.use(components)
|
||||
app.use(i18n)
|
||||
app.use(errorHandler)
|
||||
|
||||
// 挂载应用到 DOM
|
||||
app.mount("#app");
|
||||
@@ -1,13 +1,5 @@
|
||||
<!--
|
||||
* @Author: kongbeiwu lishaohua-520@qq.com
|
||||
* @Date: 2025-12-21 23:02:06
|
||||
* @LastEditors: kongbeiwu lishaohua-520@qq.com
|
||||
* @LastEditTime: 2025-12-22 01:24:00
|
||||
* @FilePath: /project/zn-ai/src/renderer/views/knowledge/index.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<div class="bg-white box-border w-full h-full rounded-[16px] p-[20px]">
|
||||
<div class="bg-white box-border w-full h-full p-[20px]">
|
||||
<TitleSection title="知识库管理" desc="内容管理" />
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="事件管理" name="first">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="bg-white box-border w-full h-full rounded-[16px] flex">
|
||||
<div class="bg-white box-border w-full h-full flex">
|
||||
<SystemConfig @change=onChange />
|
||||
|
||||
<component :is="currentComponent" />
|
||||
|
||||
@@ -17,12 +17,10 @@ import 'element-plus/dist/index.css'
|
||||
// 引入全局组件
|
||||
import HeaderBar from '@components/HeaderBar/index.vue'
|
||||
import DragRegion from '@components/DragRegion/index.vue'
|
||||
import Layout from '@components/Layout/index.vue'
|
||||
|
||||
const components: Plugin = (app) => {
|
||||
app.component('HeaderBar', HeaderBar);
|
||||
app.component('DragRegion', DragRegion);
|
||||
app.component('Layout', Layout);
|
||||
}
|
||||
|
||||
// 创建 Vue 应用实例
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<div class="bg-gray-100 h-screen flex flex-col">
|
||||
<header-bar>
|
||||
<drag-region class="w-full" />
|
||||
</header-bar>
|
||||
|
||||
<layout>
|
||||
|
||||
</layout>
|
||||
<div class="bg-white h-full flex flex-col p-[20px]">
|
||||
任务中心
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user