forked from Leaf/amber-ui
改进
This commit is contained in:
parent
a0c325cd97
commit
92da9181e1
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
out
|
||||||
|
.gitignore
|
17
.eslintrc.cjs
Normal file
17
.eslintrc.cjs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'@electron-toolkit',
|
||||||
|
'@electron-toolkit/eslint-config-ts/eslint-recommended',
|
||||||
|
'@vue/eslint-config-typescript/recommended',
|
||||||
|
'@vue/eslint-config-prettier'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/multi-word-component-names': 'off'
|
||||||
|
}
|
||||||
|
}
|
7
openapitools.json
Normal file
7
openapitools.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||||
|
"spaces": 2,
|
||||||
|
"generator-cli": {
|
||||||
|
"version": "7.8.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
.gitignore
|
.gitignore
|
||||||
.npmignore
|
.npmignore
|
||||||
.openapi-generator-ignore
|
|
||||||
api.ts
|
api.ts
|
||||||
base.ts
|
base.ts
|
||||||
common.ts
|
common.ts
|
||||||
|
4
src/components.d.ts
vendored
4
src/components.d.ts
vendored
@ -7,14 +7,14 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
AssistantMenu: typeof import('./components/AssistantMenu.vue')['default']
|
||||||
Chat: typeof import('./components/chat/chat.vue')['default']
|
Chat: typeof import('./components/chat/chat.vue')['default']
|
||||||
ChatMenu: typeof import('./components/ChatMenu.vue')['default']
|
ChatMenu: typeof import('./components/ChatMenu.vue')['default']
|
||||||
Container: typeof import('./components/Container.vue')['default']
|
Container: typeof import('./components/Container.vue')['default']
|
||||||
copy: typeof import('./components/Menu copy.vue')['default']
|
copy: typeof import('./components/AssistantMenu.vue')['default']
|
||||||
Menu: typeof import('./components/Menu.vue')['default']
|
Menu: typeof import('./components/Menu.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
Test: typeof import('./components/chat/test.vue')['default']
|
|
||||||
UserMenu: typeof import('./components/UserMenu.vue')['default']
|
UserMenu: typeof import('./components/UserMenu.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
src/components/AssistantMenu.vue
Normal file
41
src/components/AssistantMenu.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<n-list hoverable clickable>
|
||||||
|
<template #header>
|
||||||
|
<div>
|
||||||
|
<span class="text-xl">切换助理</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n-list-item v-for="a in assistantStore.assistants" :key="a.id">
|
||||||
|
<n-thing>
|
||||||
|
{{ a.name }}
|
||||||
|
</n-thing>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { useUserStore } from "../stores/user";
|
||||||
|
import { updateAll } from "../plugins/update/update";
|
||||||
|
import { useAssistantStore } from "../stores/assistants";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const currentRoute: any = computed(() => route.name);
|
||||||
|
const collapsed = ref(false);
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const assistantStore = useAssistantStore();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => userStore.logined,
|
||||||
|
(newValue, oldValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
function update() {
|
||||||
|
updateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
</script>
|
@ -8,8 +8,10 @@
|
|||||||
/> -->
|
/> -->
|
||||||
<n-list hoverable clickable>
|
<n-list hoverable clickable>
|
||||||
<template #header>
|
<template #header>
|
||||||
<!-- <n-icon :component="ChatboxOutline" /> -->
|
<div class="text-xl">
|
||||||
<span>对话列表</span>
|
<n-icon :component="ChatboxOutline" class="align-middle" />
|
||||||
|
<span class="font-xl ml-2">对话列表</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<n-list-item v-for="i in 10" :key="i">
|
<n-list-item v-for="i in 10" :key="i">
|
||||||
<n-thing> 对话 </n-thing>
|
<n-thing> 对话 </n-thing>
|
||||||
|
@ -5,7 +5,7 @@ import { useIsMobile } from "../utils/composables.js";
|
|||||||
import Menu from "../components/Menu.vue";
|
import Menu from "../components/Menu.vue";
|
||||||
import { useUserStore } from "../stores/user";
|
import { useUserStore } from "../stores/user";
|
||||||
import Guest from "../pages/guest/index.vue";
|
import Guest from "../pages/guest/index.vue";
|
||||||
import router from "../plugins/router";
|
import router from "../router";
|
||||||
const currentRoute = computed(() => router.currentRoute.value.name);
|
const currentRoute = computed(() => router.currentRoute.value.name);
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
@ -17,10 +17,28 @@
|
|||||||
</template>
|
</template>
|
||||||
<Menu></Menu>
|
<Menu></Menu>
|
||||||
</n-popover>
|
</n-popover>
|
||||||
|
|
||||||
|
<!-- 更新状态 -->
|
||||||
|
<div v-show="appStore.updating">正在更新数据</div>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
|
|
||||||
<n-grid-item class="flex items-center justify-end mr-1.5">
|
<n-grid-item class="flex items-center justify-end mr-1.5">
|
||||||
<!-- 右侧 -->
|
<!-- 右侧 -->
|
||||||
|
<!-- 助理选择 -->
|
||||||
|
<n-popover
|
||||||
|
:placement="userPlacement"
|
||||||
|
class="w-full"
|
||||||
|
trigger="click"
|
||||||
|
style="padding: 0"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<n-icon class="text-2xl mr-4">
|
||||||
|
<PersonOutline />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<AssistantMenu class="select-none" />
|
||||||
|
</n-popover>
|
||||||
|
<!-- 用户弹出 -->
|
||||||
<n-popover
|
<n-popover
|
||||||
:placement="userPlacement"
|
:placement="userPlacement"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@ -45,16 +63,18 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import UserMenu from "../components/UserMenu.vue";
|
import UserMenu from "../components/UserMenu.vue";
|
||||||
|
import AssistantMenu from "../components/AssistantMenu.vue";
|
||||||
|
import { useAppStore } from "../stores/app";
|
||||||
import { useUserStore } from "../stores/user";
|
import { useUserStore } from "../stores/user";
|
||||||
import { useIsMobile, useIsTablet } from "../utils/composables";
|
import { useIsMobile, useIsTablet } from "../utils/composables";
|
||||||
import { MenuOutline } from "@vicons/ionicons5";
|
import { MenuOutline, PersonOutline } from "@vicons/ionicons5";
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const isTablet = useIsTablet();
|
const isTablet = useIsTablet();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const userPlacement = ref("bottom");
|
const userPlacement = ref("bottom");
|
||||||
if (isMobile.value) {
|
if (isMobile.value) {
|
||||||
userPlacement.value = "bottom";
|
userPlacement.value = "bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
21
src/main.ts
21
src/main.ts
@ -1,30 +1,23 @@
|
|||||||
|
console.log("load")
|
||||||
const meta = document.createElement("meta");
|
const meta = document.createElement("meta");
|
||||||
meta.name = "naive-ui-style";
|
meta.name = "naive-ui-style";
|
||||||
document.head.appendChild(meta);
|
document.head.appendChild(meta);
|
||||||
|
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
import { registerPlugins } from "./plugins";
|
||||||
|
import router from "./router";
|
||||||
|
|
||||||
import { createApp } from "vue";
|
|
||||||
import { createPinia } from "pinia";
|
|
||||||
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
|
||||||
import naive from "naive-ui";
|
|
||||||
// 通用字体
|
|
||||||
import "vfonts/Lato.css";
|
|
||||||
// 等宽字体
|
|
||||||
import "vfonts/FiraCode.css";
|
|
||||||
|
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./plugins/router";
|
import { createApp } from "vue";
|
||||||
|
|
||||||
const pinia = createPinia();
|
|
||||||
pinia.use(piniaPluginPersistedstate);
|
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.use(pinia);
|
|
||||||
app.use(naive);
|
registerPlugins(app);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore ...
|
// @ts-ignore ...
|
||||||
if (process.env.NODE_ENV === "production") {
|
if (process.env.NODE_ENV === "production") {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useUserStore } from "../../stores/user";
|
import { useUserStore } from "../../stores/user";
|
||||||
import router from "../../plugins/router";
|
import router from "../../router";
|
||||||
import config from "../../config/config";
|
import config from "../../config/config";
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
import config from "../../config/config";
|
import config from "../../config/config";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useUserStore } from "../../stores/user";
|
import { useUserStore } from "../../stores/user";
|
||||||
import router from "../../plugins/router";
|
import router from "../../router";
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
function generateRandomString(length: number) {
|
function generateRandomString(length: number) {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import router from "../../plugins/router";
|
import router from "../../router";
|
||||||
import { useUserStore } from "../../stores/user";
|
import { useUserStore } from "../../stores/user";
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
:text="text"
|
:text="text"
|
||||||
@input="handleInput"
|
@input="handleInput"
|
||||||
height="500px"
|
height="500px"
|
||||||
|
|
||||||
></v-md-preview>
|
></v-md-preview>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,20 +23,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
||||||
|
|
||||||
|
import VMdPreview from "@kangc/v-md-editor/lib/preview";
|
||||||
import VMdPreview from '@kangc/v-md-editor/lib/preview';
|
import "@kangc/v-md-editor/lib/style/preview.css";
|
||||||
import '@kangc/v-md-editor/lib/style/preview.css';
|
import githubTheme from "@kangc/v-md-editor/lib/theme/github.js";
|
||||||
import githubTheme from '@kangc/v-md-editor/lib/theme/github.js';
|
import "@kangc/v-md-editor/lib/theme/style/github.css";
|
||||||
import '@kangc/v-md-editor/lib/theme/style/github.css';
|
|
||||||
|
|
||||||
// highlightjs
|
// highlightjs
|
||||||
import hljs from 'highlight.js';
|
import hljs from "highlight.js";
|
||||||
|
|
||||||
VMdPreview.use(githubTheme, {
|
VMdPreview.use(githubTheme, {
|
||||||
Hljs: hljs,
|
Hljs: hljs,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const text = ref("text");
|
const text = ref("text");
|
||||||
const textLength = 10; // 每次生成的文本长度
|
const textLength = 10; // 每次生成的文本长度
|
||||||
const textParts = ref<string[]>([]);
|
const textParts = ref<string[]>([]);
|
||||||
@ -59,10 +56,10 @@ onMounted(() => {
|
|||||||
updateTextParts();
|
updateTextParts();
|
||||||
|
|
||||||
// 随机生成字符串到 text.value
|
// 随机生成字符串到 text.value
|
||||||
// setInterval(() => {
|
// setInterval(() => {
|
||||||
// const randomString = Math.random().toString(36).substring(2, 2 + textLength);
|
// const randomString = Math.random().toString(36).substring(2, 2 + textLength);
|
||||||
// text.value += randomString;
|
// text.value += randomString;
|
||||||
// }, 1000);
|
// }, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -1,36 +1,57 @@
|
|||||||
// import {
|
import {
|
||||||
// AssistantApi,
|
AssistantApi,
|
||||||
// ChatApi,
|
ChatApi,
|
||||||
// ChatMessageApi,
|
ChatMessageApi,
|
||||||
// ChatPublicApi,
|
ChatPublicApi,
|
||||||
// Configuration,
|
Configuration,
|
||||||
// PingApi,
|
PingApi,
|
||||||
// ToolApi,
|
ToolApi,
|
||||||
// } from "@/api";
|
} from "../api";
|
||||||
// import config from "@/config/config";
|
import config from "../config/config";
|
||||||
// import { useUserStore } from "@/stores/user";
|
|
||||||
//
|
|
||||||
// const userStore = useUserStore();
|
|
||||||
//
|
|
||||||
// const conf = new Configuration();
|
|
||||||
//
|
|
||||||
// conf.basePath = config.backend;
|
|
||||||
// conf.apiKey = () => {
|
|
||||||
// return "Bearer " + userStore.id_token;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// userStore.$subscribe((mutation, state) => {
|
import { useUserStore } from "../stores/user";
|
||||||
// console.log(mutation);
|
import axios from "./axios";
|
||||||
// conf.apiKey = "Bearer " + state.id_token;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const api = {
|
// 定义 Api 类型
|
||||||
// Chat: new ChatApi(conf),
|
interface Api {
|
||||||
// Assistant: new AssistantApi(conf),
|
Chat: ChatApi;
|
||||||
// Ping: new PingApi(conf),
|
Assistant: AssistantApi;
|
||||||
// Tool: new ToolApi(conf),
|
Ping: PingApi;
|
||||||
// ChatMessage: new ChatMessageApi(conf),
|
Tool: ToolApi;
|
||||||
// ChatPublic: new ChatPublicApi(conf),
|
ChatMessage: ChatMessageApi;
|
||||||
// };
|
ChatPublic: ChatPublicApi;
|
||||||
//
|
}
|
||||||
// export { api, conf };
|
|
||||||
|
let api: Api | null = null; // 使用联合类型来表示初始状态可能是 null
|
||||||
|
|
||||||
|
const getApi = () => {
|
||||||
|
if (api) {
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const conf = new Configuration();
|
||||||
|
|
||||||
|
conf.basePath = config.backend;
|
||||||
|
conf.apiKey = () => {
|
||||||
|
return "Bearer " + userStore.id_token;
|
||||||
|
};
|
||||||
|
|
||||||
|
userStore.$subscribe((mutation, state) => {
|
||||||
|
conf.apiKey = "Bearer " + state.id_token;
|
||||||
|
});
|
||||||
|
|
||||||
|
api = {
|
||||||
|
Chat: new ChatApi(conf, undefined, axios),
|
||||||
|
Assistant: new AssistantApi(conf, undefined, axios),
|
||||||
|
Ping: new PingApi(conf, undefined, axios),
|
||||||
|
Tool: new ToolApi(conf, undefined, axios),
|
||||||
|
ChatMessage: new ChatMessageApi(conf, undefined, axios),
|
||||||
|
ChatPublic: new ChatPublicApi(conf, undefined, axios),
|
||||||
|
};
|
||||||
|
|
||||||
|
return api;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getApi;
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import { request, response } from "./httpInterceptors";
|
||||||
|
|
||||||
|
axios.interceptors.request.use(request.onFulfilled, request.onRejected)
|
||||||
|
axios.interceptors.response.use(response.onFulfilled, response.onRejected)
|
||||||
|
|
||||||
// axios.defaults.adapter = window.axiosHttpAdapter
|
// axios.defaults.adapter = window.axiosHttpAdapter
|
||||||
|
|
||||||
export default axios
|
export default axios
|
||||||
|
71
src/plugins/httpInterceptors.ts
Normal file
71
src/plugins/httpInterceptors.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { useUserStore } from "../stores/user";
|
||||||
|
import { h, computed } from "vue";
|
||||||
|
import { createDiscreteApi, darkTheme, lightTheme, useOsTheme } from "naive-ui";
|
||||||
|
import type { ConfigProviderProps } from "naive-ui";
|
||||||
|
|
||||||
|
const osThemeRef = useOsTheme();
|
||||||
|
|
||||||
|
const configProviderPropsRef = computed<ConfigProviderProps>(() => ({
|
||||||
|
theme: osThemeRef.value === "light" ? lightTheme : darkTheme,
|
||||||
|
}));
|
||||||
|
const { dialog, loadingBar } = createDiscreteApi(
|
||||||
|
["message", "dialog", "notification", "loadingBar", "modal"],
|
||||||
|
{
|
||||||
|
configProviderProps: configProviderPropsRef,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
onFulfilled: (config: any) => {
|
||||||
|
if (config.headers === undefined) {
|
||||||
|
config.headers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingBar.start();
|
||||||
|
|
||||||
|
return Promise.resolve(config);
|
||||||
|
},
|
||||||
|
onRejected: (error: any) => {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
// loadingBar.error()
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
onFulfilled: (res: any) => {
|
||||||
|
// if 20x
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
loadingBar.finish();
|
||||||
|
} else if (res.status >= 400 && res.status < 600) {
|
||||||
|
loadingBar.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(res);
|
||||||
|
},
|
||||||
|
onRejected: (error: any) => {
|
||||||
|
loadingBar.error();
|
||||||
|
|
||||||
|
console.error("axios error", error);
|
||||||
|
|
||||||
|
let data = [];
|
||||||
|
|
||||||
|
if (error.response.data.data) {
|
||||||
|
data = error.response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response.data.message) {
|
||||||
|
data = error.response.data.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response.data.error) {
|
||||||
|
data = error.response.data.error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export { request, response };
|
13
src/plugins/index.ts
Normal file
13
src/plugins/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import pinia from "../stores";
|
||||||
|
import naive from "naive-ui";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { App } from "vue";
|
||||||
|
// 通用字体
|
||||||
|
import "vfonts/Lato.css";
|
||||||
|
// 等宽字体
|
||||||
|
import "vfonts/FiraCode.css";
|
||||||
|
|
||||||
|
export function registerPlugins(app: App) {
|
||||||
|
app.use(naive).use(pinia);
|
||||||
|
}
|
14
src/plugins/update/update.ts
Normal file
14
src/plugins/update/update.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { useAssistantStore } from "../../stores/assistants";
|
||||||
|
import api from "../../plugins/api";
|
||||||
|
import { useUserStore } from "../../stores/user";
|
||||||
|
|
||||||
|
const updateAll = async () => {
|
||||||
|
// 更新所有的数据
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const assistantStore = useAssistantStore();
|
||||||
|
|
||||||
|
const assistantList = await api().Assistant.apiV1AssistantsGet();
|
||||||
|
assistantStore.assistants = assistantList.data.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { updateAll };
|
@ -4,28 +4,6 @@ import { defineStore } from "pinia";
|
|||||||
export const useAppStore = defineStore("app", {
|
export const useAppStore = defineStore("app", {
|
||||||
persist: false,
|
persist: false,
|
||||||
state: () => ({
|
state: () => ({
|
||||||
navigation_drawer: false,
|
updating: false,
|
||||||
navigation_items: [
|
|
||||||
{
|
|
||||||
icon: "mdi-home",
|
|
||||||
text: "主页",
|
|
||||||
to: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "mdi-assistant",
|
|
||||||
text: "助理",
|
|
||||||
to: "/assistants",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "mdi-tools",
|
|
||||||
text: "工具",
|
|
||||||
to: "/tools",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: "mdi-key",
|
|
||||||
text: "令牌",
|
|
||||||
to: "/tokens",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
10
src/stores/assistants.ts
Normal file
10
src/stores/assistants.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Utilities
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { EntityAssistant } from "../api";
|
||||||
|
|
||||||
|
export const useAssistantStore = defineStore("assistant", {
|
||||||
|
persist: false,
|
||||||
|
state: () => ({
|
||||||
|
assistants: <EntityAssistant[] | undefined> [],
|
||||||
|
}),
|
||||||
|
});
|
9
src/stores/chat.ts
Normal file
9
src/stores/chat.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { EntityChat } from "../api";
|
||||||
|
|
||||||
|
export const useChatStore = defineStore("chats", {
|
||||||
|
persist: false,
|
||||||
|
state: () => ({
|
||||||
|
chats: <EntityChat[] | undefined>[],
|
||||||
|
}),
|
||||||
|
});
|
15
src/stores/index.ts
Normal file
15
src/stores/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Utilities
|
||||||
|
import { createPinia, setActivePinia } from "pinia";
|
||||||
|
|
||||||
|
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||||
|
|
||||||
|
const pinia = createPinia();
|
||||||
|
|
||||||
|
pinia.use(piniaPluginPersistedstate);
|
||||||
|
|
||||||
|
// console
|
||||||
|
|
||||||
|
// 设置当前活跃的 Pinia 实例
|
||||||
|
// setActivePinia(pinia);
|
||||||
|
|
||||||
|
export default pinia;
|
@ -4,93 +4,101 @@ import config from "../config/config";
|
|||||||
import { Base64 } from "js-base64";
|
import { Base64 } from "js-base64";
|
||||||
|
|
||||||
let timer: any = null;
|
let timer: any = null;
|
||||||
|
export const useUserStore = () => {
|
||||||
|
const innerStore = defineStore("user", {
|
||||||
|
persist: true,
|
||||||
|
state: () => ({
|
||||||
|
logined: false,
|
||||||
|
id_token: "",
|
||||||
|
refresh_token: "",
|
||||||
|
access_token: "",
|
||||||
|
expired_at: 0,
|
||||||
|
user: {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
avatar: "",
|
||||||
|
},
|
||||||
|
timer: 0,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
login(
|
||||||
|
idToken: string,
|
||||||
|
accessToken: string,
|
||||||
|
refreshToken: string,
|
||||||
|
expiredAt: number
|
||||||
|
) {
|
||||||
|
const idTokenParts = idToken.split(".");
|
||||||
|
|
||||||
export const useUserStore = defineStore("user", {
|
const idTokenPayload = JSON.parse(Base64.decode(idTokenParts[1]));
|
||||||
persist: true,
|
|
||||||
state: () => ({
|
|
||||||
logined: false,
|
|
||||||
id_token: "",
|
|
||||||
refresh_token: "",
|
|
||||||
access_token: "",
|
|
||||||
expired_at: 0,
|
|
||||||
user: {
|
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
email: "",
|
|
||||||
avatar: "",
|
|
||||||
},
|
|
||||||
timer: 0,
|
|
||||||
}),
|
|
||||||
actions: {
|
|
||||||
login(
|
|
||||||
idToken: string,
|
|
||||||
accessToken: string,
|
|
||||||
refreshToken: string,
|
|
||||||
expiredAt: number
|
|
||||||
) {
|
|
||||||
const idTokenParts = idToken.split(".");
|
|
||||||
|
|
||||||
const idTokenPayload = JSON.parse(Base64.decode(idTokenParts[1]));
|
expiredAt = Date.now() + expiredAt * 1000;
|
||||||
|
this.expired_at = expiredAt;
|
||||||
|
|
||||||
expiredAt = Date.now() + expiredAt * 1000;
|
this.refresh_token = refreshToken;
|
||||||
this.expired_at = expiredAt;
|
this.access_token = accessToken;
|
||||||
|
|
||||||
this.refresh_token = refreshToken;
|
this.id_token = idToken;
|
||||||
this.access_token = accessToken;
|
this.user = { ...idTokenPayload };
|
||||||
|
this.logined = true;
|
||||||
this.id_token = idToken;
|
},
|
||||||
this.user = { ...idTokenPayload };
|
checkAndRefresh() {
|
||||||
this.logined = true;
|
if (this.logined) {
|
||||||
},
|
if (this.expired_at - Date.now() < 60000) {
|
||||||
checkAndRefresh() {
|
this.refresh();
|
||||||
if (this.logined) {
|
|
||||||
if (this.expired_at - Date.now() < 60000) {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setupTimer() {
|
|
||||||
this.checkAndRefresh();
|
|
||||||
timer = setInterval(() => {
|
|
||||||
this.checkAndRefresh();
|
|
||||||
}, 10 * 1000);
|
|
||||||
},
|
|
||||||
async refresh() {
|
|
||||||
const discovery = await axios.get(config.oauth_discovery_url);
|
|
||||||
|
|
||||||
// post /oauth/token to refresh
|
|
||||||
// 构建 form 参数
|
|
||||||
const data = new URLSearchParams();
|
|
||||||
data.set("grant_type", "refresh_token");
|
|
||||||
data.set("refresh_token", this.refresh_token);
|
|
||||||
data.set("client_id", config.oauth_client_id);
|
|
||||||
data.set("scope", config.oauth_scope);
|
|
||||||
|
|
||||||
axios
|
|
||||||
.post(discovery.data.token_endpoint, data)
|
|
||||||
.then((response) => {
|
|
||||||
this.login(
|
|
||||||
response.data.id_token,
|
|
||||||
response.data.access_token,
|
|
||||||
response.data.refresh_token,
|
|
||||||
response.data.expires_in
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
// if 401
|
|
||||||
if (error.response.status === 401) {
|
|
||||||
console.log("Refresh token failed");
|
|
||||||
}
|
}
|
||||||
// logout
|
}
|
||||||
this.logout();
|
},
|
||||||
clearInterval(timer);
|
setupTimer() {
|
||||||
});
|
this.checkAndRefresh();
|
||||||
|
timer = setInterval(() => {
|
||||||
|
this.checkAndRefresh();
|
||||||
|
}, 10 * 1000);
|
||||||
|
},
|
||||||
|
async refresh() {
|
||||||
|
const discovery = await axios.get(config.oauth_discovery_url);
|
||||||
|
|
||||||
|
// post /oauth/token to refresh
|
||||||
|
// 构建 form 参数
|
||||||
|
const data = new URLSearchParams();
|
||||||
|
data.set("grant_type", "refresh_token");
|
||||||
|
data.set("refresh_token", this.refresh_token);
|
||||||
|
data.set("client_id", config.oauth_client_id);
|
||||||
|
data.set("scope", config.oauth_scope);
|
||||||
|
|
||||||
|
axios
|
||||||
|
.post(discovery.data.token_endpoint, data)
|
||||||
|
.then((response) => {
|
||||||
|
this.login(
|
||||||
|
response.data.id_token,
|
||||||
|
response.data.access_token,
|
||||||
|
response.data.refresh_token,
|
||||||
|
response.data.expires_in
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// if 401
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
console.log("Refresh token failed");
|
||||||
|
}
|
||||||
|
// logout
|
||||||
|
this.logout();
|
||||||
|
clearInterval(timer);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
this.$reset();
|
||||||
|
this.user = this.$state.user;
|
||||||
|
this.id_token = this.$state.id_token;
|
||||||
|
this.logined = this.$state.logined;
|
||||||
|
},
|
||||||
|
getIdToken() {
|
||||||
|
return this.id_token;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
logout() {
|
});
|
||||||
this.$reset();
|
|
||||||
this.user = this.$state.user;
|
console.log("storage")
|
||||||
this.id_token = this.$state.id_token;
|
|
||||||
this.logined = this.$state.logined;
|
return innerStore()
|
||||||
},
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
|
Loading…
Reference in New Issue
Block a user