add
This commit is contained in:
commit
295f14e9f4
69
.eslintrc-auto-import.json
Normal file
69
.eslintrc-auto-import.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"globals": {
|
||||||
|
"Component": true,
|
||||||
|
"ComponentPublicInstance": true,
|
||||||
|
"ComputedRef": true,
|
||||||
|
"EffectScope": true,
|
||||||
|
"ExtractDefaultPropTypes": true,
|
||||||
|
"ExtractPropTypes": true,
|
||||||
|
"ExtractPublicPropTypes": true,
|
||||||
|
"InjectionKey": true,
|
||||||
|
"PropType": true,
|
||||||
|
"Ref": true,
|
||||||
|
"VNode": true,
|
||||||
|
"WritableComputedRef": true,
|
||||||
|
"computed": true,
|
||||||
|
"createApp": true,
|
||||||
|
"customRef": true,
|
||||||
|
"defineAsyncComponent": true,
|
||||||
|
"defineComponent": true,
|
||||||
|
"effectScope": true,
|
||||||
|
"getCurrentInstance": true,
|
||||||
|
"getCurrentScope": true,
|
||||||
|
"h": true,
|
||||||
|
"inject": true,
|
||||||
|
"isProxy": true,
|
||||||
|
"isReactive": true,
|
||||||
|
"isReadonly": true,
|
||||||
|
"isRef": true,
|
||||||
|
"markRaw": true,
|
||||||
|
"nextTick": true,
|
||||||
|
"onActivated": true,
|
||||||
|
"onBeforeMount": true,
|
||||||
|
"onBeforeUnmount": true,
|
||||||
|
"onBeforeUpdate": true,
|
||||||
|
"onDeactivated": true,
|
||||||
|
"onErrorCaptured": true,
|
||||||
|
"onMounted": true,
|
||||||
|
"onRenderTracked": true,
|
||||||
|
"onRenderTriggered": true,
|
||||||
|
"onScopeDispose": true,
|
||||||
|
"onServerPrefetch": true,
|
||||||
|
"onUnmounted": true,
|
||||||
|
"onUpdated": true,
|
||||||
|
"provide": true,
|
||||||
|
"reactive": true,
|
||||||
|
"readonly": true,
|
||||||
|
"ref": true,
|
||||||
|
"resolveComponent": true,
|
||||||
|
"shallowReactive": true,
|
||||||
|
"shallowReadonly": true,
|
||||||
|
"shallowRef": true,
|
||||||
|
"toRaw": true,
|
||||||
|
"toRef": true,
|
||||||
|
"toRefs": true,
|
||||||
|
"toValue": true,
|
||||||
|
"triggerRef": true,
|
||||||
|
"unref": true,
|
||||||
|
"useAttrs": true,
|
||||||
|
"useCssModule": true,
|
||||||
|
"useCssVars": true,
|
||||||
|
"useRoute": true,
|
||||||
|
"useRouter": true,
|
||||||
|
"useSlots": true,
|
||||||
|
"watch": true,
|
||||||
|
"watchEffect": true,
|
||||||
|
"watchPostEffect": true,
|
||||||
|
"watchSyncEffect": true
|
||||||
|
}
|
||||||
|
}
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
wwwroot/*.js
|
||||||
|
node_modules
|
||||||
|
typings
|
||||||
|
dist
|
||||||
|
.DS_Store
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
build
|
||||||
|
yarn.lock
|
2171
api/swagger.yaml
Normal file
2171
api/swagger.yaml
Normal file
File diff suppressed because it is too large
Load Diff
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||||
|
<title>amber-wails</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="./src/main.ts" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
50
package.json
Normal file
50
package.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"gen": "openapi-generator-cli generate -i ./api/swagger.yaml -g typescript-axios -o ./src/api"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"highlight.js": "^11.10.0",
|
||||||
|
"js-base64": "^3.7.7",
|
||||||
|
"lottie-web": "^5.12.2",
|
||||||
|
"pinia": "^2.2.2",
|
||||||
|
"pinia-plugin-persistedstate": "^4.0.0",
|
||||||
|
"vooks": "^0.2.12",
|
||||||
|
"vue": "^3.2.37",
|
||||||
|
"vue-router": "^4.0.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/types": "^7.18.10",
|
||||||
|
"@types/node": "^22.5.4",
|
||||||
|
"@vicons/ionicons5": "^0.12.0",
|
||||||
|
"@vicons/material": "^0.12.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
|
"@vue/eslint-config-prettier": "^9.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-vue": "^9.26.0",
|
||||||
|
"naive-ui": "^2.39.0",
|
||||||
|
"postcss": "^8.4.45",
|
||||||
|
"prettier": "^3.3.2",
|
||||||
|
"tailwindcss": "^3.4.10",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"unplugin-auto-import": "^0.18.2",
|
||||||
|
"unplugin-vue-components": "^0.27.4",
|
||||||
|
"unplugin-vue-router": "^0.10.7",
|
||||||
|
"vfonts": "^0.0.3",
|
||||||
|
"vite": "^5.3.1",
|
||||||
|
"vite-plugin-pages": "^0.32.3",
|
||||||
|
"vite-plugin-vue-layouts": "^0.11.0",
|
||||||
|
"vue": "^3.4.30",
|
||||||
|
"vue-tsc": "^2.0.22"
|
||||||
|
}
|
||||||
|
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
117
src/App.vue
Normal file
117
src/App.vue
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<!--<script setup lang="ts">-->
|
||||||
|
<!--import DefaultLayout from './layouts/DefaultLayout.vue'-->
|
||||||
|
|
||||||
|
<!--// const ipcHandle = () => window.electron.ipcRenderer.send('ping')-->
|
||||||
|
<!--</script>-->
|
||||||
|
|
||||||
|
<!--<template>-->
|
||||||
|
<!-- <DefaultLayout />-->
|
||||||
|
<!--</template>-->
|
||||||
|
<template>
|
||||||
|
<n-config-provider
|
||||||
|
:date-locale="dateZhCN"
|
||||||
|
:hljs="hljs"
|
||||||
|
:locale="zhCN"
|
||||||
|
:theme="theme"
|
||||||
|
:theme-overrides="themeOverrides"
|
||||||
|
preflight-style-disabled
|
||||||
|
>
|
||||||
|
<n-global-style />
|
||||||
|
<n-loading-bar-provider>
|
||||||
|
<n-message-provider>
|
||||||
|
<n-notification-provider :max="3">
|
||||||
|
<n-dialog-provider>
|
||||||
|
<!-- <!– 加载动画 –>-->
|
||||||
|
<!-- <transition name="fade">-->
|
||||||
|
|
||||||
|
<!-- <div v-show="load_step === 1">-->
|
||||||
|
<!-- <div class="flex h-screen">-->
|
||||||
|
<!-- <div class="m-auto text-center">-->
|
||||||
|
<!-- <Lottie v-if="osThemeRef === 'dark'" :loop="false" name="lae-jump"/>-->
|
||||||
|
<!-- <Lottie v-else :loop="false" name="lae-jump-black"/>-->
|
||||||
|
<!-- <n-h3>莱云</n-h3>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </transition>-->
|
||||||
|
|
||||||
|
<!-- 页面 -->
|
||||||
|
<!-- <transition name="fade">
|
||||||
|
<div v-if="true">
|
||||||
|
<DefaultLayout />
|
||||||
|
</div>
|
||||||
|
</transition> -->
|
||||||
|
<DefaultLayout />
|
||||||
|
</n-dialog-provider>
|
||||||
|
</n-notification-provider>
|
||||||
|
</n-message-provider>
|
||||||
|
</n-loading-bar-provider>
|
||||||
|
</n-config-provider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import hljs from 'highlight.js/lib/core'
|
||||||
|
import ini from 'highlight.js/lib/languages/ini'
|
||||||
|
|
||||||
|
import {
|
||||||
|
darkTheme,
|
||||||
|
dateZhCN,
|
||||||
|
NConfigProvider,
|
||||||
|
NDialogProvider,
|
||||||
|
NGlobalStyle,
|
||||||
|
NLoadingBarProvider,
|
||||||
|
NMessageProvider,
|
||||||
|
NNotificationProvider,
|
||||||
|
useOsTheme,
|
||||||
|
zhCN
|
||||||
|
} from 'naive-ui'
|
||||||
|
// import Lottie from "./components/Lottie.vue";
|
||||||
|
import DefaultLayout from './layouts/DefaultLayout.vue'
|
||||||
|
import { useUserStore } from './stores/user'
|
||||||
|
|
||||||
|
const osThemeRef = useOsTheme()
|
||||||
|
const theme = computed(() => (osThemeRef.value === 'dark' ? darkTheme : null))
|
||||||
|
|
||||||
|
// const load_step = ref(1)
|
||||||
|
//
|
||||||
|
// if (process.env.NODE_ENV === 'production') {
|
||||||
|
// window.onload = () => {
|
||||||
|
// setTimeout(() => {
|
||||||
|
// load_step.value = 1
|
||||||
|
// setTimeout(() => {
|
||||||
|
// load_step.value = 2
|
||||||
|
// }, 500)
|
||||||
|
// }, 250)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// load_step.value = 2
|
||||||
|
// }
|
||||||
|
|
||||||
|
hljs.registerLanguage('ini', ini)
|
||||||
|
|
||||||
|
// 主题调整
|
||||||
|
/**
|
||||||
|
* js 文件下使用这个做类型提示
|
||||||
|
* @type import('naive-ui').GlobalThemeOverrides
|
||||||
|
*/
|
||||||
|
// const themeOverrides = {
|
||||||
|
// common: {
|
||||||
|
// primaryColor: '#ec4b2d',
|
||||||
|
// primaryColorHover: '#ec4b2d',
|
||||||
|
// },
|
||||||
|
// Button: {
|
||||||
|
// textColor: '#ec4b2d'
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
const themeOverrides = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// UserStore
|
||||||
|
const userStore = useUserStore();
|
||||||
|
userStore.setupTimer();
|
||||||
|
|
||||||
|
</script>
|
4
src/api/.gitignore
vendored
Normal file
4
src/api/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
wwwroot/*.js
|
||||||
|
node_modules
|
||||||
|
typings
|
||||||
|
dist
|
1
src/api/.npmignore
Normal file
1
src/api/.npmignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
|
23
src/api/.openapi-generator-ignore
Normal file
23
src/api/.openapi-generator-ignore
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# OpenAPI Generator Ignore
|
||||||
|
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||||
|
|
||||||
|
# Use this file to prevent files from being overwritten by the generator.
|
||||||
|
# The patterns follow closely to .gitignore or .dockerignore.
|
||||||
|
|
||||||
|
# As an example, the C# client generator defines ApiClient.cs.
|
||||||
|
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||||
|
#ApiClient.cs
|
||||||
|
|
||||||
|
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||||
|
#foo/*/qux
|
||||||
|
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||||
|
#foo/**/qux
|
||||||
|
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can also negate patterns with an exclamation (!).
|
||||||
|
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||||
|
#docs/*.md
|
||||||
|
# Then explicitly reverse the ignore rule for a single file:
|
||||||
|
#!docs/README.md
|
9
src/api/.openapi-generator/FILES
Normal file
9
src/api/.openapi-generator/FILES
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.gitignore
|
||||||
|
.npmignore
|
||||||
|
.openapi-generator-ignore
|
||||||
|
api.ts
|
||||||
|
base.ts
|
||||||
|
common.ts
|
||||||
|
configuration.ts
|
||||||
|
git_push.sh
|
||||||
|
index.ts
|
1
src/api/.openapi-generator/VERSION
Normal file
1
src/api/.openapi-generator/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
7.8.0
|
5854
src/api/api.ts
Normal file
5854
src/api/api.ts
Normal file
File diff suppressed because it is too large
Load Diff
86
src/api/base.ts
Normal file
86
src/api/base.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* Leaflow Amber
|
||||||
|
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 1.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import type { Configuration } from './configuration';
|
||||||
|
// Some imports not used depending on template conditions
|
||||||
|
// @ts-ignore
|
||||||
|
import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
|
||||||
|
import globalAxios from 'axios';
|
||||||
|
|
||||||
|
export const BASE_PATH = "http://localhost".replace(/\/+$/, "");
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const COLLECTION_FORMATS = {
|
||||||
|
csv: ",",
|
||||||
|
ssv: " ",
|
||||||
|
tsv: "\t",
|
||||||
|
pipes: "|",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface RequestArgs
|
||||||
|
*/
|
||||||
|
export interface RequestArgs {
|
||||||
|
url: string;
|
||||||
|
options: RawAxiosRequestConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class BaseAPI
|
||||||
|
*/
|
||||||
|
export class BaseAPI {
|
||||||
|
protected configuration: Configuration | undefined;
|
||||||
|
|
||||||
|
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
|
||||||
|
if (configuration) {
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.basePath = configuration.basePath ?? basePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class RequiredError
|
||||||
|
* @extends {Error}
|
||||||
|
*/
|
||||||
|
export class RequiredError extends Error {
|
||||||
|
constructor(public field: string, msg?: string) {
|
||||||
|
super(msg);
|
||||||
|
this.name = "RequiredError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServerMap {
|
||||||
|
[key: string]: {
|
||||||
|
url: string,
|
||||||
|
description: string,
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const operationServerMap: ServerMap = {
|
||||||
|
}
|
150
src/api/common.ts
Normal file
150
src/api/common.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* Leaflow Amber
|
||||||
|
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 1.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import type { Configuration } from "./configuration";
|
||||||
|
import type { RequestArgs } from "./base";
|
||||||
|
import type { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
|
import { RequiredError } from "./base";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const DUMMY_BASE_URL = 'https://example.com'
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
|
||||||
|
if (paramValue === null || paramValue === undefined) {
|
||||||
|
throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
|
||||||
|
if (configuration && configuration.apiKey) {
|
||||||
|
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
|
||||||
|
? await configuration.apiKey(keyParamName)
|
||||||
|
: await configuration.apiKey;
|
||||||
|
object[keyParamName] = localVarApiKeyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
|
||||||
|
if (configuration && (configuration.username || configuration.password)) {
|
||||||
|
object["auth"] = { username: configuration.username, password: configuration.password };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
|
||||||
|
if (configuration && configuration.accessToken) {
|
||||||
|
const accessToken = typeof configuration.accessToken === 'function'
|
||||||
|
? await configuration.accessToken()
|
||||||
|
: await configuration.accessToken;
|
||||||
|
object["Authorization"] = "Bearer " + accessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
|
||||||
|
if (configuration && configuration.accessToken) {
|
||||||
|
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
|
||||||
|
? await configuration.accessToken(name, scopes)
|
||||||
|
: await configuration.accessToken;
|
||||||
|
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
|
||||||
|
if (parameter == null) return;
|
||||||
|
if (typeof parameter === "object") {
|
||||||
|
if (Array.isArray(parameter)) {
|
||||||
|
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Object.keys(parameter).forEach(currentKey =>
|
||||||
|
setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (urlSearchParams.has(key)) {
|
||||||
|
urlSearchParams.append(key, parameter);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
urlSearchParams.set(key, parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const setSearchParams = function (url: URL, ...objects: any[]) {
|
||||||
|
const searchParams = new URLSearchParams(url.search);
|
||||||
|
setFlattenedQueryParams(searchParams, objects);
|
||||||
|
url.search = searchParams.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
|
||||||
|
const nonString = typeof value !== 'string';
|
||||||
|
const needsSerialization = nonString && configuration && configuration.isJsonMime
|
||||||
|
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
|
||||||
|
: nonString;
|
||||||
|
return needsSerialization
|
||||||
|
? JSON.stringify(value !== undefined ? value : {})
|
||||||
|
: (value || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const toPathString = function (url: URL) {
|
||||||
|
return url.pathname + url.search + url.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
*/
|
||||||
|
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
|
||||||
|
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||||
|
const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url};
|
||||||
|
return axios.request<T, R>(axiosRequestArgs);
|
||||||
|
};
|
||||||
|
}
|
110
src/api/configuration.ts
Normal file
110
src/api/configuration.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* Leaflow Amber
|
||||||
|
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 1.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export interface ConfigurationParameters {
|
||||||
|
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||||
|
basePath?: string;
|
||||||
|
serverIndex?: number;
|
||||||
|
baseOptions?: any;
|
||||||
|
formDataCtor?: new () => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Configuration {
|
||||||
|
/**
|
||||||
|
* parameter for apiKey security
|
||||||
|
* @param name security name
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||||
|
/**
|
||||||
|
* parameter for basic security
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
username?: string;
|
||||||
|
/**
|
||||||
|
* parameter for basic security
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
password?: string;
|
||||||
|
/**
|
||||||
|
* parameter for oauth2 security
|
||||||
|
* @param name security name
|
||||||
|
* @param scopes oauth2 scope
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||||
|
/**
|
||||||
|
* override base path
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
basePath?: string;
|
||||||
|
/**
|
||||||
|
* override server index
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
serverIndex?: number;
|
||||||
|
/**
|
||||||
|
* base options for axios calls
|
||||||
|
*
|
||||||
|
* @type {any}
|
||||||
|
* @memberof Configuration
|
||||||
|
*/
|
||||||
|
baseOptions?: any;
|
||||||
|
/**
|
||||||
|
* The FormData constructor that will be used to create multipart form data
|
||||||
|
* requests. You can inject this here so that execution environments that
|
||||||
|
* do not support the FormData class can still run the generated client.
|
||||||
|
*
|
||||||
|
* @type {new () => FormData}
|
||||||
|
*/
|
||||||
|
formDataCtor?: new () => any;
|
||||||
|
|
||||||
|
constructor(param: ConfigurationParameters = {}) {
|
||||||
|
this.apiKey = param.apiKey;
|
||||||
|
this.username = param.username;
|
||||||
|
this.password = param.password;
|
||||||
|
this.accessToken = param.accessToken;
|
||||||
|
this.basePath = param.basePath;
|
||||||
|
this.serverIndex = param.serverIndex;
|
||||||
|
this.baseOptions = param.baseOptions;
|
||||||
|
this.formDataCtor = param.formDataCtor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given MIME is a JSON MIME.
|
||||||
|
* JSON MIME examples:
|
||||||
|
* application/json
|
||||||
|
* application/json; charset=UTF8
|
||||||
|
* APPLICATION/JSON
|
||||||
|
* application/vnd.company+json
|
||||||
|
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
||||||
|
* @return True if the given MIME is JSON, false otherwise.
|
||||||
|
*/
|
||||||
|
public isJsonMime(mime: string): boolean {
|
||||||
|
const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
|
||||||
|
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
|
||||||
|
}
|
||||||
|
}
|
57
src/api/git_push.sh
Normal file
57
src/api/git_push.sh
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
|
||||||
|
#
|
||||||
|
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
|
||||||
|
|
||||||
|
git_user_id=$1
|
||||||
|
git_repo_id=$2
|
||||||
|
release_note=$3
|
||||||
|
git_host=$4
|
||||||
|
|
||||||
|
if [ "$git_host" = "" ]; then
|
||||||
|
git_host="github.com"
|
||||||
|
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$git_user_id" = "" ]; then
|
||||||
|
git_user_id="GIT_USER_ID"
|
||||||
|
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$git_repo_id" = "" ]; then
|
||||||
|
git_repo_id="GIT_REPO_ID"
|
||||||
|
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$release_note" = "" ]; then
|
||||||
|
release_note="Minor update"
|
||||||
|
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize the local directory as a Git repository
|
||||||
|
git init
|
||||||
|
|
||||||
|
# Adds the files in the local repository and stages them for commit.
|
||||||
|
git add .
|
||||||
|
|
||||||
|
# Commits the tracked changes and prepares them to be pushed to a remote repository.
|
||||||
|
git commit -m "$release_note"
|
||||||
|
|
||||||
|
# Sets the new remote
|
||||||
|
git_remote=$(git remote)
|
||||||
|
if [ "$git_remote" = "" ]; then # git remote not defined
|
||||||
|
|
||||||
|
if [ "$GIT_TOKEN" = "" ]; then
|
||||||
|
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
|
||||||
|
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
|
||||||
|
else
|
||||||
|
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
git pull origin master
|
||||||
|
|
||||||
|
# Pushes (Forces) the changes in the local repository up to the remote repository
|
||||||
|
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
|
||||||
|
git push origin master 2>&1 | grep -v 'To https'
|
18
src/api/index.ts
Normal file
18
src/api/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* Leaflow Amber
|
||||||
|
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 1.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export * from "./api";
|
||||||
|
export * from "./configuration";
|
||||||
|
|
93
src/assets/fonts/OFL.txt
Normal file
93
src/assets/fonts/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com),
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
src/assets/fonts/nunito-v16-latin-regular.woff2
Normal file
BIN
src/assets/fonts/nunito-v16-latin-regular.woff2
Normal file
Binary file not shown.
BIN
src/assets/images/logo-universal.png
Normal file
BIN
src/assets/images/logo-universal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
129
src/auto-imports.d.ts
vendored
Normal file
129
src/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const toValue: typeof import('vue')['toValue']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useRoute: typeof import('vue-router/auto')['useRoute']
|
||||||
|
const useRouter: typeof import('vue-router/auto')['useRouter']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||||
|
import('vue')
|
||||||
|
}
|
||||||
|
// for vue template auto import
|
||||||
|
import { UnwrapRef } from 'vue'
|
||||||
|
declare module 'vue' {
|
||||||
|
interface GlobalComponents {}
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||||
|
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||||
|
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||||
|
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||||
|
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||||
|
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||||
|
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||||
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||||
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||||
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
|
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||||
|
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||||
|
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||||
|
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||||
|
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||||
|
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||||
|
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||||
|
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||||
|
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||||
|
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||||
|
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||||
|
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||||
|
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||||
|
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||||
|
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||||
|
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||||
|
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||||
|
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||||
|
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||||
|
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||||
|
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||||
|
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||||
|
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||||
|
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||||
|
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||||
|
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||||
|
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||||
|
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||||
|
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||||
|
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||||
|
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||||
|
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||||
|
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||||
|
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||||
|
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||||
|
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||||
|
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||||
|
readonly useRoute: UnwrapRef<typeof import('vue-router/auto')['useRoute']>
|
||||||
|
readonly useRouter: UnwrapRef<typeof import('vue-router/auto')['useRouter']>
|
||||||
|
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||||
|
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||||
|
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||||
|
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||||
|
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||||
|
}
|
||||||
|
}
|
15
src/components.d.ts
vendored
Normal file
15
src/components.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
Container: typeof import('./components/Container.vue')['default']
|
||||||
|
Menu: typeof import('./components/Menu.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
}
|
||||||
|
}
|
22
src/components/Container.vue
Normal file
22
src/components/Container.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import router from '../plugins/router';
|
||||||
|
|
||||||
|
const currentRoute = computed(() => router.currentRoute.value.name)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- <Header style="min-height: var(--header-height)"></Header> -->
|
||||||
|
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition mode="out-in" name="fade">
|
||||||
|
<div>
|
||||||
|
<component :is="Component" />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
|
||||||
|
<!-- <router-view></router-view> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
57
src/components/Menu.vue
Normal file
57
src/components/Menu.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<n-menu
|
||||||
|
:value="currentRoute"
|
||||||
|
:collapsed="collapsed"
|
||||||
|
:collapsed-width="64"
|
||||||
|
:collapsed-icon-size="22"
|
||||||
|
:options="menuOptions"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { NIcon, NMenu } from 'naive-ui'
|
||||||
|
import { Home as HomeIcon } from '@vicons/ionicons5'
|
||||||
|
import type { MenuOption } from 'naive-ui'
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const currentRoute: any = computed(() => route.name)
|
||||||
|
|
||||||
|
const collapsed = ref(false)
|
||||||
|
const menuOptions: MenuOption[] = [
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
to: {
|
||||||
|
name: '/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ default: () => 'Amber' }
|
||||||
|
),
|
||||||
|
key: '/',
|
||||||
|
icon: renderIcon(HomeIcon)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
to: {
|
||||||
|
name: '/auth/login'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ default: () => 'Login' }
|
||||||
|
),
|
||||||
|
key: '/auth/login',
|
||||||
|
icon: renderIcon(HomeIcon)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
function renderIcon(icon: Component) {
|
||||||
|
return () => h(NIcon, null, { default: () => h(icon) })
|
||||||
|
}
|
||||||
|
</script>
|
25
src/config/config.ts
Normal file
25
src/config/config.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const config = {
|
||||||
|
app_name: "Amber",
|
||||||
|
oauth_discovery_url:
|
||||||
|
"https://auth.leaflow.cn/.well-known/openid-configuration",
|
||||||
|
oauth_client_id: "90020",
|
||||||
|
oauth_callback_url: "amber-desktop://auth_callback",
|
||||||
|
oauth_storage_key: "code_verifier",
|
||||||
|
oauth_scope: "openid profile",
|
||||||
|
backend: "http://localhost:8080",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore ...
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
config.backend = "https://amber-api.leaflow.cn";
|
||||||
|
config.oauth_callback_url = "https://amber.leaflow.cn/auth/callback";
|
||||||
|
config.oauth_client_id = "16";
|
||||||
|
}
|
||||||
|
|
||||||
|
config.backend = "https://amber-api.leaflow.cn";
|
||||||
|
|
||||||
|
// config.backend = "https://amber-api.leaflow.cn";
|
||||||
|
|
||||||
|
// console.log("api endpoint: " + config.backend);
|
||||||
|
|
||||||
|
export default config;
|
8
src/env.d.ts
vendored
Normal file
8
src/env.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
45
src/layouts/DefaultLayout.vue
Normal file
45
src/layouts/DefaultLayout.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Container from "../components/Container.vue";
|
||||||
|
import { NLayout, NLayoutContent, NLayoutSider } from "naive-ui";
|
||||||
|
import { useIsMobile } from "../utils/composables.js";
|
||||||
|
import Menu from "../components/Menu.vue";
|
||||||
|
import { useUserStore } from "../stores/user";
|
||||||
|
import Guest from "../pages/guest/index.vue";
|
||||||
|
import router from "../plugins/router";
|
||||||
|
const currentRoute = computed(() => router.currentRoute.value.name);
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// import Header from './Header.vue'
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
// const isTablet = useIsTablet()
|
||||||
|
const menuCollapsed = ref({
|
||||||
|
left: false,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-layout :position="isMobile ? 'static' : 'absolute'" :has-sider="true">
|
||||||
|
<n-layout-sider
|
||||||
|
v-if="userStore.logined"
|
||||||
|
:collapsed-width="0"
|
||||||
|
:native-scrollbar="false"
|
||||||
|
:show-collapsed-content="false"
|
||||||
|
:width="240"
|
||||||
|
bordered
|
||||||
|
collapse-mode="width"
|
||||||
|
show-trigger="arrow-circle"
|
||||||
|
class="select-none"
|
||||||
|
@collapse="menuCollapsed.left = true"
|
||||||
|
@expand="menuCollapsed.left = false"
|
||||||
|
>
|
||||||
|
<Menu></Menu>
|
||||||
|
</n-layout-sider>
|
||||||
|
<n-layout-content>
|
||||||
|
<!-- <Guest v-if="!userStore.logined && currentRoute != '/auth/login'" />
|
||||||
|
<Container v-else /> -->
|
||||||
|
<Guest v-if="!userStore.logined && !currentRoute?.startsWith('/auth')" />
|
||||||
|
<Container v-else />
|
||||||
|
</n-layout-content>
|
||||||
|
</n-layout>
|
||||||
|
</template>
|
23
src/layouts/Header.vue
Normal file
23
src/layouts/Header.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<n-layout>
|
||||||
|
<n-layout-header bordered class="layout-header header-height">
|
||||||
|
<n-grid cols="2" class="header-height">
|
||||||
|
<n-grid-item class="flex items-center justify-start mr-1.5">
|
||||||
|
<div>left</div>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item class="flex items-center justify-end mr-1.5">
|
||||||
|
<div>right</div>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</n-layout-header>
|
||||||
|
</n-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.header-height {
|
||||||
|
min-height: var(--header-height);
|
||||||
|
}
|
||||||
|
</style>
|
32
src/main.ts
Normal file
32
src/main.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const meta = document.createElement("meta");
|
||||||
|
meta.name = "naive-ui-style";
|
||||||
|
document.head.appendChild(meta);
|
||||||
|
|
||||||
|
|
||||||
|
import "./style.css";
|
||||||
|
|
||||||
|
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 router from "./plugins/router";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const pinia = createPinia();
|
||||||
|
pinia.use(piniaPluginPersistedstate);
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
app.use(pinia);
|
||||||
|
app.use(naive);
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
app.mount("#app");
|
52
src/pages/auth/callback.vue
Normal file
52
src/pages/auth/callback.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>请稍等片刻</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import axios from "axios";
|
||||||
|
import { useUserStore } from "../../stores/user";
|
||||||
|
import router from "../../plugins/router";
|
||||||
|
import config from "../../config/config";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
axios.get(config.oauth_discovery_url).then((discovery) => {
|
||||||
|
const localCodeVerifier = localStorage.getItem(config.oauth_storage_key);
|
||||||
|
|
||||||
|
const code: any = router.currentRoute.value.query.code;
|
||||||
|
|
||||||
|
// 从当前页面请求中获取 code
|
||||||
|
const q = new URLSearchParams({
|
||||||
|
client_id: config.oauth_client_id,
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
redirect_uri: config.oauth_callback_url,
|
||||||
|
code_verifier: localCodeVerifier || "",
|
||||||
|
code: code ?? "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const tokenEndpoint = discovery.data.token_endpoint;
|
||||||
|
axios
|
||||||
|
.post(tokenEndpoint, q)
|
||||||
|
.then((r) => {
|
||||||
|
userStore.access_token = r.data.access_token;
|
||||||
|
userStore.refresh_token = r.data.refresh_token;
|
||||||
|
|
||||||
|
userStore.login(
|
||||||
|
r.data.id_token,
|
||||||
|
r.data.access_token,
|
||||||
|
r.data.refresh_token,
|
||||||
|
r.data.expires_in,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
alert("登录失败");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 跳转到 /
|
||||||
|
router.push("/");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
5
src/pages/auth/continue.vue
Normal file
5
src/pages/auth/continue.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>请在新窗口中继续登录</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
59
src/pages/auth/login.vue
Normal file
59
src/pages/auth/login.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>请稍后...</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script async setup lang="ts">
|
||||||
|
import config from "../../config/config";
|
||||||
|
import axios from "axios";
|
||||||
|
import router from "../../plugins/router";
|
||||||
|
|
||||||
|
function generateRandomString(length: number) {
|
||||||
|
let text = "";
|
||||||
|
const possible =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateCodeChallenge(codeVerifier: string) {
|
||||||
|
const digest = await crypto.subtle.digest(
|
||||||
|
"SHA-256",
|
||||||
|
new TextEncoder().encode(codeVerifier),
|
||||||
|
);
|
||||||
|
|
||||||
|
return btoa(String.fromCharCode(...new Uint8Array(digest)))
|
||||||
|
.replace(/=/g, "")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function go() {
|
||||||
|
const codeVerifier = generateRandomString(128);
|
||||||
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
||||||
|
localStorage.setItem(config.oauth_storage_key, codeVerifier);
|
||||||
|
|
||||||
|
const query = new URLSearchParams({
|
||||||
|
client_id: config.oauth_client_id,
|
||||||
|
redirect_uri: config.oauth_callback_url,
|
||||||
|
response_type: "code",
|
||||||
|
scope: config.oauth_scope,
|
||||||
|
code_challenge: codeChallenge,
|
||||||
|
code_challenge_method: "S256",
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
const discovery = await axios.get(config.oauth_discovery_url);
|
||||||
|
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = discovery.data.authorization_endpoint + '?' + query
|
||||||
|
a.click()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
go();
|
||||||
|
</script>
|
24
src/pages/guest/index.vue
Normal file
24
src/pages/guest/index.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex items-center align-center justify-center h-full">
|
||||||
|
<div class="text-center">
|
||||||
|
|
||||||
|
<div class="mt-5 !ml-2">
|
||||||
|
<n-h1> 满身星光,不负众望 </n-h1>
|
||||||
|
<n-p> </n-p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<n-button v-if="!userStore.logined" type="primary" @click="login"> 登录 </n-button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import router from "../../plugins/router";
|
||||||
|
import { useUserStore } from "../../stores/user";
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const login = () => {
|
||||||
|
router.push("/auth/login");
|
||||||
|
};
|
||||||
|
</script>
|
46
src/pages/index.vue
Normal file
46
src/pages/index.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useMessage } from "naive-ui";
|
||||||
|
import { useUserStore } from "../stores/user";
|
||||||
|
const message = useMessage();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
label: "滨海湾金沙,新加坡",
|
||||||
|
key: "marina bay sands",
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "布朗酒店,伦敦",
|
||||||
|
key: "brown's hotel, london",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "亚特兰蒂斯巴哈马,拿骚",
|
||||||
|
key: "atlantis nahamas, nassau",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "比佛利山庄酒店,洛杉矶",
|
||||||
|
key: "the beverly hills hotel, los angeles",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
function handleSelect(key: string | number) {
|
||||||
|
message.info(String(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
userStore.logout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-link to="auth/login">go</router-link>
|
||||||
|
<n-dropdown trigger="hover" :options="options" @select="handleSelect">
|
||||||
|
<n-button>找个地方休息111</n-button>
|
||||||
|
</n-dropdown>
|
||||||
|
|
||||||
|
<n-button @click="logout()">退出登录</n-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
36
src/plugins/api.ts
Normal file
36
src/plugins/api.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// import {
|
||||||
|
// AssistantApi,
|
||||||
|
// ChatApi,
|
||||||
|
// ChatMessageApi,
|
||||||
|
// ChatPublicApi,
|
||||||
|
// Configuration,
|
||||||
|
// PingApi,
|
||||||
|
// ToolApi,
|
||||||
|
// } from "@/api";
|
||||||
|
// 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) => {
|
||||||
|
// console.log(mutation);
|
||||||
|
// conf.apiKey = "Bearer " + state.id_token;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const api = {
|
||||||
|
// Chat: new ChatApi(conf),
|
||||||
|
// Assistant: new AssistantApi(conf),
|
||||||
|
// Ping: new PingApi(conf),
|
||||||
|
// Tool: new ToolApi(conf),
|
||||||
|
// ChatMessage: new ChatMessageApi(conf),
|
||||||
|
// ChatPublic: new ChatPublicApi(conf),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// export { api, conf };
|
5
src/plugins/axios.ts
Normal file
5
src/plugins/axios.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
// axios.defaults.adapter = window.axiosHttpAdapter
|
||||||
|
|
||||||
|
export default axios
|
10
src/plugins/router.ts
Normal file
10
src/plugins/router.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
import { routes } from 'vue-router/auto-routes'
|
||||||
|
import { setupLayouts } from 'virtual:generated-layouts'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes: setupLayouts(routes)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
31
src/stores/app.ts
Normal file
31
src/stores/app.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Utilities
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
export const useAppStore = defineStore("app", {
|
||||||
|
persist: false,
|
||||||
|
state: () => ({
|
||||||
|
navigation_drawer: 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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
102
src/stores/user.ts
Normal file
102
src/stores/user.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import axios from "axios";
|
||||||
|
import config from "../config/config";
|
||||||
|
import { Base64 } from "js-base64";
|
||||||
|
|
||||||
|
let timer: any = null;
|
||||||
|
|
||||||
|
export const useUserStore = defineStore("user", {
|
||||||
|
persist: true,
|
||||||
|
state: () => ({
|
||||||
|
logined: false,
|
||||||
|
id_token: "",
|
||||||
|
refresh_token: "",
|
||||||
|
access_token: "",
|
||||||
|
expired_at: 0,
|
||||||
|
user: {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
|
||||||
|
this.refresh_token = refreshToken;
|
||||||
|
this.access_token = accessToken;
|
||||||
|
|
||||||
|
this.id_token = idToken;
|
||||||
|
this.user.email = idTokenPayload.email;
|
||||||
|
this.user.name = idTokenPayload.name;
|
||||||
|
this.user.id = idTokenPayload.sub;
|
||||||
|
this.logined = true;
|
||||||
|
},
|
||||||
|
checkAndRefresh() {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
this.user = {
|
||||||
|
id: 0,
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
this.id_token = "";
|
||||||
|
this.logined = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
7
src/style.css
Normal file
7
src/style.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
body {
|
||||||
|
--header-height: 32px;
|
||||||
|
}
|
27
src/typed-router.d.ts
vendored
Normal file
27
src/typed-router.d.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️
|
||||||
|
// It's recommended to commit this file.
|
||||||
|
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
|
||||||
|
|
||||||
|
declare module 'vue-router/auto-routes' {
|
||||||
|
import type {
|
||||||
|
RouteRecordInfo,
|
||||||
|
ParamValue,
|
||||||
|
ParamValueOneOrMore,
|
||||||
|
ParamValueZeroOrMore,
|
||||||
|
ParamValueZeroOrOne,
|
||||||
|
} from 'vue-router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route name map generated by unplugin-vue-router
|
||||||
|
*/
|
||||||
|
export interface RouteNamedMap {
|
||||||
|
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||||
|
'/auth/callback': RouteRecordInfo<'/auth/callback', '/auth/callback', Record<never, never>, Record<never, never>>,
|
||||||
|
'/auth/continue': RouteRecordInfo<'/auth/continue', '/auth/continue', Record<never, never>, Record<never, never>>,
|
||||||
|
'/auth/login': RouteRecordInfo<'/auth/login', '/auth/login', Record<never, never>, Record<never, never>>,
|
||||||
|
'/guest/': RouteRecordInfo<'/guest/', '/guest', Record<never, never>, Record<never, never>>,
|
||||||
|
}
|
||||||
|
}
|
45
src/utils/composables.ts
Normal file
45
src/utils/composables.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { inject, provide, reactive, toRef, watchEffect } from 'vue'
|
||||||
|
import { useBreakpoint, useMemo } from 'vooks'
|
||||||
|
|
||||||
|
export function useIsMobile() {
|
||||||
|
const breakpointRef = useBreakpoint()
|
||||||
|
return useMemo(() => {
|
||||||
|
return breakpointRef.value === 'xs'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useIsTablet() {
|
||||||
|
const breakpointRef = useBreakpoint()
|
||||||
|
return useMemo(() => {
|
||||||
|
return breakpointRef.value === 's'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useIsSmallDesktop() {
|
||||||
|
const breakpointRef = useBreakpoint()
|
||||||
|
return useMemo(() => {
|
||||||
|
return breakpointRef.value === 'm'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const i18n = function (data: any) {
|
||||||
|
const localeReactive = inject('i18n', null)
|
||||||
|
return {
|
||||||
|
// @ts-ignore ...
|
||||||
|
locale: toRef(localeReactive, 'locale'),
|
||||||
|
t(key: any) {
|
||||||
|
// @ts-ignore ...
|
||||||
|
const { locale } = localeReactive
|
||||||
|
return data[locale][key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i18n.provide = function (localeRef: any) {
|
||||||
|
const localeReactive = reactive({})
|
||||||
|
watchEffect(() => {
|
||||||
|
// @ts-ignore ...
|
||||||
|
localeReactive.locale = localeRef.value
|
||||||
|
})
|
||||||
|
provide('i18n', localeReactive)
|
||||||
|
}
|
7
src/vite-env.d.ts
vendored
Normal file
7
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type {DefineComponent} from 'vue'
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
8
tailwind.config.js
Normal file
8
tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
33
tsconfig.json
Normal file
33
tsconfig.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": [
|
||||||
|
"ESNext",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": [
|
||||||
|
"unplugin-vue-router/client"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue"
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
tsconfig.node.json
Normal file
11
tsconfig.node.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"vite.config.ts"
|
||||||
|
]
|
||||||
|
}
|
57
vite.config.ts
Normal file
57
vite.config.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { defineConfig } from "vite"
|
||||||
|
import vue from "@vitejs/plugin-vue"
|
||||||
|
import VueRouter from "unplugin-vue-router/vite"
|
||||||
|
import Layouts from "vite-plugin-vue-layouts"
|
||||||
|
import Components from "unplugin-vue-components/vite"
|
||||||
|
import AutoImport from "unplugin-auto-import/vite"
|
||||||
|
// import { resolve } from "path";
|
||||||
|
// const rootPath = new URL(".", import.meta.url).pathname;
|
||||||
|
|
||||||
|
import { fileURLToPath, URL } from "node:url"
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
},
|
||||||
|
extensions: [".js", ".json", ".jsx", ".mjs", ".ts", ".tsx", ".vue"],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
VueRouter({
|
||||||
|
dts: "src/typed-router.d.ts",
|
||||||
|
routesFolder: [
|
||||||
|
{
|
||||||
|
src: "src/pages",
|
||||||
|
path: "",
|
||||||
|
// override globals
|
||||||
|
exclude: (excluded) => excluded,
|
||||||
|
filePatterns: (filePatterns) => filePatterns,
|
||||||
|
extensions: (extensions) => extensions,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
vue(),
|
||||||
|
Layouts(),
|
||||||
|
AutoImport({
|
||||||
|
imports: [
|
||||||
|
"vue",
|
||||||
|
{
|
||||||
|
"vue-router/auto": ["useRoute", "useRouter"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dts: "src/auto-imports.d.ts",
|
||||||
|
eslintrc: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
vueTemplate: true,
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
dts: "src/components.d.ts",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
server: {
|
||||||
|
host: true,
|
||||||
|
port: 5173,
|
||||||
|
strictPort: true,
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user