改进 前端

This commit is contained in:
Twilight 2024-03-28 16:48:13 +08:00
parent b822becbbf
commit 4225ae8629
18 changed files with 324 additions and 159 deletions

View File

@ -10,7 +10,10 @@
},
"dependencies": {
"@mdi/font": "6.2.95",
"axios": "^1.6.8",
"core-js": "^3.34.0",
"keycloak-js": "^24.0.2",
"pinia-plugin-persistedstate": "^3.2.1",
"roboto-fontface": "*",
"vue": "^3.4.21",
"vuetify": "^3.5.8"

View File

@ -1,79 +0,0 @@
<template>
<v-footer height="40" app>
<a
v-for="item in items"
:key="item.title"
:href="item.href"
:title="item.title"
class="d-inline-block mx-2 social-link"
rel="noopener noreferrer"
target="_blank"
>
<v-icon
:icon="item.icon"
:size="item.icon === '$vuetify' ? 24 : 16"
/>
</a>
<div
class="text-caption text-disabled"
style="position: absolute; right: 16px;"
>
&copy; 2016-{{ (new Date()).getFullYear() }} <span class="d-none d-sm-inline-block">Vuetify, LLC</span>
<a
class="text-decoration-none on-surface"
href="https://vuetifyjs.com/about/licensing/"
rel="noopener noreferrer"
target="_blank"
>
MIT License
</a>
</div>
</v-footer>
</template>
<script setup lang="ts">
const items = [
{
title: 'Vuetify Documentation',
icon: `$vuetify`,
href: 'https://vuetifyjs.com/',
},
{
title: 'Vuetify Support',
icon: 'mdi-shield-star-outline',
href: 'https://support.vuetifyjs.com/',
},
{
title: 'Vuetify X',
icon: `svg:M2.04875 3.00002L9.77052 13.3248L1.99998 21.7192H3.74882L10.5519 14.3697L16.0486 21.7192H22L13.8437 10.8137L21.0765 3.00002H19.3277L13.0624 9.76874L8.0001 3.00002H2.04875ZM4.62054 4.28821H7.35461L19.4278 20.4308H16.6937L4.62054 4.28821Z`,
href: 'https://x.com/vuetifyjs',
},
{
title: 'Vuetify GitHub',
icon: `mdi-github`,
href: 'https://github.com/vuetifyjs/vuetify',
},
{
title: 'Vuetify Discord',
icon: `mdi-discord`,
href: 'https://community.vuetifyjs.com/',
},
{
title: 'Vuetify Reddit',
icon: `mdi-reddit`,
href: 'https://reddit.com/r/vuetifyjs',
},
]
</script>
<style scoped lang="sass">
.social-link :deep(.v-icon)
color: rgba(var(--v-theme-on-background), var(--v-disabled-opacity))
text-decoration: none
transition: .2s ease-in-out
&:hover
color: rgba(25, 118, 210, 1)
</style>

View File

@ -1,5 +1,19 @@
<template>
<h1>欢迎使用 资料库</h1>
<h1>欢迎使用 MC 搜索</h1>
<p>目前还在开发中</p>
</template>
<script setup>
// test api
// import {theSpiderApi} from "@/plugins/api";
//
// theSpiderApi.apiSpidersGet({
// params: {
// page: 2
// }
// }).then(r =>{
// console.log(r.data.Total)
// })
</script>

5
src/config/app.ts Normal file
View File

@ -0,0 +1,5 @@
let app = {
name: "MC Search"
}
export default app

13
src/config/config.ts Normal file
View File

@ -0,0 +1,13 @@
let config = {
auth: {
server: "https://kc.ivampiresp.com",
clientId: "dev",
realm: "test",
scopes: "openid email profile",
},
api: {
baseUrl: "http://127.0.0.1:8081",
}
}
export default config

10
src/config/keycloak.ts Normal file
View File

@ -0,0 +1,10 @@
import Keycloak from "keycloak-js";
import config from "@/config/config";
let keycloak = new Keycloak({
url: config.auth.server,
realm: config.auth.realm,
clientId: config.auth.clientId,
})
export default keycloak

View File

@ -33,23 +33,24 @@
<v-app-bar>
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
<v-app-bar-title>{{ configStore.appName }}</v-app-bar-title>
<v-app-bar-title>{{ app.name }}</v-app-bar-title>
</v-app-bar>
<!-- </v-app> -->
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useConfigStore } from "@/stores/config";
import app from "../../config/app";
import {useUserStore} from "@/stores/user";
const userStore = useUserStore()
const configStore = useConfigStore();
const drawer = ref(false);
const items = [
const items = ref([
{ icon: "mdi-home", text: "首页", to: { name: "home" } },
// { icon: "mdi-content-copy", text: "", to: { name: "libraries" } },
// // { icon: "mdi-history", text: "Frequently contacted", to: "/documents" },
// { icon: "mdi-account", text: "", to: { name: "login" } },
];
// { icon: "mdi-history", text: "Frequently contacted", to: "/documents" },
{ icon: "mdi-account", text: userStore.access_token ? "注销" : "登录", to: { name: "login" } },
])
</script>

View File

@ -1,5 +0,0 @@
# Pages
Vue components created in this folder will automatically be converted to navigatable routes.
Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository.

View File

@ -1,7 +0,0 @@
<template>
<HelloWorld />
</template>
<script lang="ts" setup>
//
</script>

View File

@ -1,22 +1,17 @@
import { Configuration } from "../api";
import {Configuration, SpiderApi} from "@/api";
import {useUserStore} from "@/stores/user";
import config from "@/config/config";
// import axios from "axios";
// import router from "@/router";
// import {useUserStore} from "@/store/user";
// import { useConfigStore } from "@/store/config";
//
//
// const userStore = useUserStore()
// const configStore = useConfigStore()
//
// const conf = new Configuration
// conf.basePath = configStore.apiServer
//
// conf.apiKey = "Bearer " + userStore.jwt_token
// const document = new DocumentsApi(conf);
// const library = new LibrariesApi(conf);
//
//
// export {
// document, library, conf
// }
const userStore = useUserStore()
const conf = new Configuration
conf.basePath = config.api.baseUrl
conf.apiKey = "Bearer " + userStore.id_token
const theSpiderApi = new SpiderApi(conf);
export {
theSpiderApi, conf
}

View File

@ -11,9 +11,16 @@ import 'vuetify/styles'
// Composables
import { createVuetify } from 'vuetify'
// lang
import { pl, zhHans } from 'vuetify/locale'
import { md3 } from 'vuetify/blueprints'
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
export default createVuetify({
theme: {
defaultTheme: 'dark',
locale: {
locale: 'zhHans',
fallback: 'sv',
messages: { zhHans, pl },
},
blueprint: md3,
})

View File

@ -1,8 +1,6 @@
// Composables
import { createRouter, createWebHistory } from "vue-router";
// import { useUserStore } from "@/stores/user";
// import { useConfigStore } from "@/stores/config";
import {useUserStore} from "@/stores/user";
const routes = [
{
path: "/",
@ -15,6 +13,17 @@ const routes = [
// which is lazy-loaded when the route is visited.
component: () => import("@/views/Home.vue"),
},
{
path: "/auth/login",
name: "login",
meta: {
auth: false,
},
// route level code-splitting
// this generates a separate chunk (Home-[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("@/views/auth/Login.vue"),
},
];
@ -23,35 +32,29 @@ const router = createRouter({
routes,
});
// router.beforeEach((to, from) => {
//
// // get route name
// console.log(from.name, from.fullPath)
//
// const userStore = useUserStore();
// const configStore = useConfigStore();
//
// // if (to.matched.length === 0) {
// // return router.push({ name: "errors.404" });
// // }
//
// if (to.meta.title) {
// document.title = to.meta.title + " - " + configStore.getAppName();
// } else {
// document.title = configStore.getAppName();
// }
//
// if (to.meta.auth == true) {
// // validate login state
// if (userStore.jwt_token == null) {
// router.push({ name: "login" });
// }
// } else {
// document.title = configStore.getAppName();
// }
//
// return true;
//
// });
router.beforeEach((to: any, from: any) => {
console.log(from.name, from.fullPath)
const userStore = useUserStore();
// if (to.matched.length === 0) {
// return router.push({ name: "errors.404" });
// }
if (to.meta.auth == true) {
// validate login state
if (userStore.access_token == null) {
router.push({name: "login"}).then(r => {
console.log(r)
});
}
}
return true;
});
export default router;

View File

@ -1,4 +1,5 @@
// Utilities
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export default createPinia()
export default createPinia().use(piniaPluginPersistedstate)

33
src/stores/user.ts Normal file
View File

@ -0,0 +1,33 @@
import {defineStore} from 'pinia'
export const useUserStore = defineStore('user', {
persist: true,
state: () => ({
refresh_token: null as null | string,
access_token: null as null | string,
id_token: null as null | string,
expired_at: null as null | Date,
user: null as null | any,
}),
actions: {
setRefreshToken(refresh_token: string) {
this.refresh_token = refresh_token
},
setAccessToken(access_token: string) {
this.access_token = access_token
},
setIdToken(id_token: string) {
this.id_token = id_token
},
setProfile(user: any) {
this.user = user
},
logout() {
this.user = null;
this.expired_at = null;
this.id_token = null;
this.access_token = null;
this.refresh_token = null
}
}
})

View File

@ -18,6 +18,5 @@ declare module 'vue-router/auto-routes' {
* Route name map generated by unplugin-vue-router
*/
export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
}
}

View File

@ -1,7 +1,6 @@
<template>
<div>
<HelloWorld />
</div>
</template>

93
src/views/auth/Login.vue Normal file
View File

@ -0,0 +1,93 @@
<script setup lang="ts">
import {ref} from "vue";
import {useUserStore} from "@/stores/user";
import keycloak from "@/config/keycloak";
import config from "@/config/config";
import router from "@/router";
const userStore = useUserStore();
const status = ref()
if (userStore.access_token) {
status.value = '注销中'
userStore.logout()
}
const logged = ref(false)
const failed = ref(false)
status.value = '请稍后'
keycloak.init({onLoad: 'check-sso', scope: config.auth.scopes}).then(async (auth: boolean) => {
if (!auth) {
status.value = "正在连接"
keycloak.login().then(r => {
console.log(r)
status.value = "跳转到登录页面"
}).catch(r => {
console.log(r)
status.value = "失败,请刷新重试。"
failed.value = true
})
} else {
status.value = "已登录"
logged.value = true
if (keycloak.token != null) {
userStore.setAccessToken(keycloak.token)
}
if (keycloak.refreshToken != null) {
userStore.setRefreshToken(keycloak.refreshToken)
}
userStore.setProfile(keycloak.tokenParsed)
if (keycloak.idToken != null) {
userStore.setIdToken(keycloak.idToken)
}
keycloak.loadUserProfile().then((profile) => {
console.log(profile)
}).catch((e) => {
})
keycloak.loadUserInfo().then((user) => {
console.log(user)
})
await router.push({
name: "home"
})
}
})
</script>
<template>
<div class="d-flex h-screen align-center justify-center">
<div class="text-center">
<div v-if="!failed">
<v-progress-circular v-show="!logged" :indeterminate="!logged"></v-progress-circular>
<v-icon v-show="logged" class="text-success" icon="mdi-check"></v-icon>
</div>
<div v-else>
<v-icon v-show="failed" class="text-danger" icon="mdi-close"></v-icon>
</div>
<div class="mt-3">
{{ status }}
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -723,6 +723,11 @@ ast-walker-scope@^0.6.0:
"@babel/parser" "^7.24.0"
ast-kit "^0.12.1"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
available-typed-arrays@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
@ -730,6 +735,15 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
axios@^1.6.8:
version "1.6.8"
resolved "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -830,6 +844,13 @@ color-name@~1.1.4:
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
computeds@^0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz#215b08a4ba3e08a11ff6eee5d6d8d7166a97ce2e"
@ -933,6 +954,11 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -1393,6 +1419,11 @@ flatted@^3.2.9:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
follow-redirects@^1.15.6:
version "1.15.6"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3:
version "0.3.3"
resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
@ -1400,6 +1431,15 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -1762,6 +1802,11 @@ isexe@^2.0.0:
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
js-sha256@^0.11.0:
version "0.11.0"
resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576"
integrity sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@ -1801,6 +1846,19 @@ jsonc-parser@^3.2.0:
resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==
jwt-decode@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b"
integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
keycloak-js@^24.0.2:
version "24.0.2"
resolved "https://registry.npmjs.org/keycloak-js/-/keycloak-js-24.0.2.tgz#9e7320ed1859b0f493acbc57c64728da2b5eb49b"
integrity sha512-V2N8cSz3NfON98XHp+DCzvrb1WW35JalL5Zphe/uoVWOxcof7v522Yz9Q2O3BqXqXP3V/H9ml6o24BwwtXUTGA==
dependencies:
js-sha256 "^0.11.0"
jwt-decode "^4.0.0"
keyv@^4.5.3:
version "4.5.4"
resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
@ -1880,6 +1938,18 @@ micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
minimatch@9.0.3, minimatch@^9.0.3:
version "9.0.3"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
@ -2079,6 +2149,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pinia-plugin-persistedstate@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz#66780602aecd6c7b152dd7e3ddc249a1f7a13fe5"
integrity sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==
pinia@^2.1.7:
version "2.1.7"
resolved "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz#4cf5420d9324ca00b7b4984d3fbf693222115bbc"
@ -2123,6 +2198,11 @@ prelude-ls@^1.2.1:
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"