From d160c3a61cc38359de4794b208884bba430f0a1c Mon Sep 17 00:00:00 2001 From: LiuHao <liuhaoai545@gmail.com> Date: 星期一, 10 七月 2023 22:56:42 +0800 Subject: [PATCH] merge --- src/views/index.vue | 8 src/views/system/client/index.vue | 344 ++++++++++++++++++++ src/views/monitor/cache/index.vue | 95 ++-- src/layout/components/SocialCallback/index.vue | 82 ++++ src/plugins/download.ts | 36 + src/api/system/client/types.ts | 138 ++++++++ src/utils/request.ts | 11 src/api/login.ts | 33 + src/utils/crypto.ts | 45 ++ src/store/modules/user.ts | 4 src/api/system/client/index.ts | 80 ++++ src/views/system/user/profile/thirdParty.vue | 2 src/api/types.ts | 13 src/permission.ts | 2 /dev/null | 36 -- src/views/system/menu/index.vue | 2 src/utils/jsencrypt.ts | 3 src/views/login.vue | 13 src/utils/auth.ts | 2 src/layout/components/TagsView/ScrollPane.vue | 2 package.json | 4 src/router/index.ts | 5 src/components/RuoYiDoc/index.vue | 2 23 files changed, 828 insertions(+), 134 deletions(-) diff --git a/package.json b/package.json index 3217898..039083c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ruoyi-vue-plus", - "version": "5.0.0", + "version": "5.1.0", "description": "RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�", "author": "LionLi", "license": "MIT", @@ -29,6 +29,7 @@ "fuse.js": "6.6.2", "js-cookie": "3.0.1", "jsencrypt": "3.3.1", + "crypto-js": "^4.1.1", "nprogress": "0.2.0", "path-browserify": "1.0.1", "path-to-regexp": "6.2.0", @@ -44,6 +45,7 @@ "devDependencies": { "@iconify/json": "^2.2.40", "@intlify/unplugin-vue-i18n": "0.8.2", + "@types/crypto-js": "^4.1.1", "@types/file-saver": "2.0.5", "@types/js-cookie": "3.0.3", "@types/node": "18.14.2", diff --git a/src/api/login.ts b/src/api/login.ts index 9d6d1b7..2f75ecb 100644 --- a/src/api/login.ts +++ b/src/api/login.ts @@ -3,22 +3,24 @@ import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types'; import { UserInfo } from '@/api/system/user/types'; +// pc绔浐瀹氬鎴风鎺堟潈id +const clientId = 'e5cd7e4891bf95d1d19206ce24a7b32e'; + /** * @param data {LoginData} * @returns */ export function login(data: LoginData): AxiosPromise<LoginResult> { const params = { - tenantId: data.tenantId, - username: data.username.trim(), - password: data.password, - code: data.code, - uuid: data.uuid + ...data, + clientId: data.clientId || clientId, + grantType: data.grantType || 'password' }; return request({ url: '/auth/login', headers: { - isToken: false + isToken: false, + isEncrypt: true }, method: 'post', data: params @@ -60,19 +62,20 @@ timeout: 20000 }); } + /** * 绗笁鏂圭櫥褰� - * @param source 绗笁鏂圭櫥褰曠被鍨� - * */ -export function socialLogin(source: string, code: any, state: any): AxiosPromise<any> { - const data = { - code, - state + */ +export function callback(data: LoginData): AxiosPromise<any> { + const LoginData = { + ...data, + clientId: clientId, + grantType: 'social' }; return request({ - url: '/auth/social-login/' + source, - method: 'get', - params: data + url: '/auth/social/callback', + method: 'post', + data: LoginData }); } diff --git a/src/api/system/client/index.ts b/src/api/system/client/index.ts new file mode 100644 index 0000000..06544da --- /dev/null +++ b/src/api/system/client/index.ts @@ -0,0 +1,80 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ClientVO, ClientForm, ClientQuery } from '@/api/system/client/types'; + +/** + * 鏌ヨ瀹㈡埛绔鐞嗗垪琛� + * @param query + * @returns {*} + */ + +export const listClient = (query?: ClientQuery): AxiosPromise<ClientVO[]> => { + return request({ + url: '/system/client/list', + method: 'get', + params: query + }); +}; + +/** + * 鏌ヨ瀹㈡埛绔鐞嗚缁� + * @param id + */ +export const getClient = (id: string | number): AxiosPromise<ClientVO> => { + return request({ + url: '/system/client/' + id, + method: 'get' + }); +}; + +/** + * 鏂板瀹㈡埛绔鐞� + * @param data + */ +export const addClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'post', + data: data + }); +}; + +/** + * 淇敼瀹㈡埛绔鐞� + * @param data + */ +export const updateClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'put', + data: data + }); +}; + +/** + * 鍒犻櫎瀹㈡埛绔鐞� + * @param id + */ +export const delClient = (id: string | number | Array<string | number>) => { + return request({ + url: '/system/client/' + id, + method: 'delete' + }); +}; + +/** + * 鐘舵�佷慨鏀� + * @param id ID + * @param status 鐘舵�� + */ +export function changeStatus(id: number | string, status: string) { + const data = { + id, + status + }; + return request({ + url: '/system/client/changeStatus', + method: 'put', + data: data + }); +} diff --git a/src/api/system/client/types.ts b/src/api/system/client/types.ts new file mode 100644 index 0000000..e67f95f --- /dev/null +++ b/src/api/system/client/types.ts @@ -0,0 +1,138 @@ +export interface ClientVO { + /** + * id + */ + id: string | number; + + /** + * 瀹㈡埛绔痠d + */ + clientId: string | number; + + /** + * 瀹㈡埛绔痥ey + */ + clientKey: string; + + /** + * 瀹㈡埛绔閽� + */ + clientSecret: string; + + /** + * 鎺堟潈绫诲瀷 + */ + grantTypeList: string[]; + + /** + * 璁惧绫诲瀷 + */ + deviceType: string; + + /** + * token娲昏穬瓒呮椂鏃堕棿 + */ + activeTimeout: number; + + /** + * token鍥哄畾瓒呮椂 + */ + timeout: number; + + /** + * 鐘舵�侊紙0姝e父 1鍋滅敤锛� + */ + status: string; + +} + +export interface ClientForm extends BaseEntity { + /** + * id + */ + id?: string | number; + + /** + * 瀹㈡埛绔痠d + */ + clientId?: string | number; + + /** + * 瀹㈡埛绔痥ey + */ + clientKey?: string; + + /** + * 瀹㈡埛绔閽� + */ + clientSecret?: string; + + /** + * 鎺堟潈绫诲瀷 + */ + grantTypeList?: string[]; + + /** + * 璁惧绫诲瀷 + */ + deviceType?: string; + + /** + * token娲昏穬瓒呮椂鏃堕棿 + */ + activeTimeout?: number; + + /** + * token鍥哄畾瓒呮椂 + */ + timeout?: number; + + /** + * 鐘舵�侊紙0姝e父 1鍋滅敤锛� + */ + status?: string; + +} + +export interface ClientQuery extends PageQuery { + /** + * 瀹㈡埛绔痠d + */ + clientId?: string | number; + + /** + * 瀹㈡埛绔痥ey + */ + clientKey?: string; + + /** + * 瀹㈡埛绔閽� + */ + clientSecret?: string; + + /** + * 鎺堟潈绫诲瀷 + */ + grantType?: string; + + /** + * 璁惧绫诲瀷 + */ + deviceType?: string; + + /** + * token娲昏穬瓒呮椂鏃堕棿 + */ + activeTimeout?: number; + + /** + * token鍥哄畾瓒呮椂 + */ + timeout?: number; + + /** + * 鐘舵�侊紙0姝e父 1鍋滅敤锛� + */ + status?: string; + +} diff --git a/src/api/types.ts b/src/api/types.ts index 68fb427..e02e645 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -15,19 +15,24 @@ * 鐧诲綍璇锋眰 */ export interface LoginData { - tenantId: string; - username: string; - password: string; + tenantId?: string; + username?: string; + password?: string; rememberMe?: boolean; + socialCode?: string, + socialState?: string, + source?: string, code?: string; uuid?: string; + clientId: string; + grantType: string; } /** * 鐧诲綍鍝嶅簲 */ export interface LoginResult { - token: string; + access_token: string; } /** diff --git a/src/components/RuoYiDoc/index.vue b/src/components/RuoYiDoc/index.vue index 6dad85c..6c7ac0d 100644 --- a/src/components/RuoYiDoc/index.vue +++ b/src/components/RuoYiDoc/index.vue @@ -5,7 +5,7 @@ </template> <script setup> -const url = ref('https://javalionli.gitee.io/plus-doc'); +const url = ref('https://plus-doc.dromara.org/'); function goto() { window.open(url.value) diff --git a/src/layout/components/SocialCallback/index.vue b/src/layout/components/SocialCallback/index.vue new file mode 100644 index 0000000..faf7d50 --- /dev/null +++ b/src/layout/components/SocialCallback/index.vue @@ -0,0 +1,82 @@ +<template> + <div v-loading="loading" class="social-callback"></div> +</template> + +<script setup lang="ts"> +import { login, callback } from '@/api/login'; +import { setToken } from '@/utils/auth'; +import Cookies from 'js-cookie'; +import { getToken } from '@/utils/auth'; +import { LoginData } from '@/api/types'; + +const route = useRoute(); +const loading = ref(true); + + +/** + * 鎺ユ敹Route浼犻�掔殑鍙傛暟 + * @param {Object} route.query. + */ +const code = route.query.code as string; +const state = route.query.state as string; +const source = route.query.source as string; +const tenantId = Cookies.get("tenantId") ? Cookies.get("tenantId") as string : '000000'; + + +const processResponse = async (res: any) => { + if (res.code !== 200) { + throw new Error(res.msg); + } + setToken(res.data.access_token); + ElMessage.success(res.msg); + location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index'; +}; + +const handleError = (error: any) => { + ElMessage.error(error.message); + location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index'; +}; + +const callbackByCode = async (data: LoginData) => { + try { + const res = await callback(data); + await processResponse(res); + loading.value = false; + } catch (error) { + handleError(error); + } +}; + +const loginByCode = async (data: LoginData) => { + try { + const res = await login(data); + await processResponse(res); + loading.value = false; + } catch (error) { + handleError(error); + } +}; + +const init = async () => { + const data: LoginData = { + socialCode: code, + socialState: state, + tenantId: tenantId, + source: source, + clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e', + grantType: 'social' + }; + + if (!getToken()) { + await loginByCode(data); + } else { + await callbackByCode(data); + } +}; + +onMounted(() => { + nextTick(() => { + init(); + }); +}); +</script> diff --git a/src/layout/components/SocialLogin/index.vue b/src/layout/components/SocialLogin/index.vue deleted file mode 100644 index fad8286..0000000 --- a/src/layout/components/SocialLogin/index.vue +++ /dev/null @@ -1,36 +0,0 @@ -<template> - <div v-loading="loading" class="social-login"></div> -</template> - -<script setup lang="ts"> -import {socialLogin} from '@/api/login'; -import {setToken} from '@/utils/auth'; - -const route = useRoute(); -const router = useRouter(); - - -/** - * 鎺ユ敹Route浼犻�掔殑鍙傛暟 - * @param {Object} route.query. - */ -const code = route.query.code; -const state = route.query.state; -const source = route.query.source as string; -const loading = ref(true); -await socialLogin(source, code, state) - .then(async (res) => { - if (res.code !== 200) { - ElMessage.error(res.msg); - router.go(-2); - return; - } - loading.value = false; - setToken(res.msg); - ElMessage.success('鐧诲綍鎴愬姛'); - router.go(-2); - }) - .catch(() => { - loading.value = false; - }); -</script> diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue index 949e096..b50c628 100644 --- a/src/layout/components/TagsView/ScrollPane.vue +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -95,7 +95,7 @@ bottom: 0px; } :deep(.el-scrollbar__wrap) { - height: 39px; + height: 49px; } } </style> diff --git a/src/permission.ts b/src/permission.ts index c2743eb..4543d08 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -10,7 +10,7 @@ import usePermissionStore from '@/store/modules/permission'; NProgress.configure({ showSpinner: false }); -const whiteList = ['/login', '/register', '/social-login']; +const whiteList = ['/login', '/register', '/social-callback']; router.beforeEach(async (to, from, next) => { NProgress.start(); diff --git a/src/plugins/download.ts b/src/plugins/download.ts index e1c4414..c6c8521 100644 --- a/src/plugins/download.ts +++ b/src/plugins/download.ts @@ -34,21 +34,29 @@ }, async zip(url: string, name: string) { url = baseURL + url; - const res = await axios({ - method: 'get', - url: url, - responseType: 'blob', - headers: { - Authorization: 'Bearer ' + getToken(), - datasource: localStorage.getItem('dataName') + downloadLoadingInstance = ElLoading.service({ text: '姝e湪涓嬭浇鏁版嵁锛岃绋嶅��', background: 'rgba(0, 0, 0, 0.7)' }); + try { + const res = await axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { + Authorization: 'Bearer ' + getToken(), + datasource: localStorage.getItem('dataName') + } + }); + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }); + FileSaver.saveAs(blob, name); + } else { + this.printErrMsg(res.data); } - }); - const isBlob = blobValidate(res.data); - if (isBlob) { - const blob = new Blob([res.data], { type: 'application/zip' }); - FileSaver.saveAs(blob, name); - } else { - this.printErrMsg(res.data); + downloadLoadingInstance.close(); + } catch (r) { + console.error(r) + ElMessage.error('涓嬭浇鏂囦欢鍑虹幇閿欒锛岃鑱旂郴绠$悊鍛橈紒') + downloadLoadingInstance.close(); } }, async printErrMsg(data: any) { diff --git a/src/router/index.ts b/src/router/index.ts index 76a81bc..d6aefe9 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -38,9 +38,9 @@ ] }, { - path: '/social-login', + path: '/social-callback', hidden: true, - component: () => import('@/layout/components/SocialLogin/index.vue') + component: () => import('@/layout/components/SocialCallback/index.vue') }, { path: '/login', @@ -181,4 +181,5 @@ } }); + export default router; diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index 6f30437..2593d1a 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -23,8 +23,8 @@ const [err, res] = await to(loginApi(userInfo)); if (res) { const data = res.data; - setToken(data.token); - token.value = data.token; + setToken(data.access_token); + token.value = data.access_token; return Promise.resolve(); } return Promise.reject(err); diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 4020003..db50ac9 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -4,6 +4,6 @@ export const getToken = () => tokenStorage.value; -export const setToken = (token: string) => (tokenStorage.value = token); +export const setToken = (access_token: string) => (tokenStorage.value = access_token); export const removeToken = () => (tokenStorage.value = null); diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts new file mode 100644 index 0000000..133893e --- /dev/null +++ b/src/utils/crypto.ts @@ -0,0 +1,45 @@ +import CryptoJS from 'crypto-js'; + +/** + * 闅忔満鐢熸垚32浣嶇殑瀛楃涓� + * @returns {string} + */ +const generateRandomString = () => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + const charactersLength = characters.length; + for (let i = 0; i < 32; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +}; + +/** + * 闅忔満鐢熸垚aes 瀵嗛挜 + * @returns {string} + */ +export const generateAesKey = () => { + return CryptoJS.enc.Utf8.parse(generateRandomString()); +}; + +/** + * 鍔犲瘑base64 + * @returns {string} + */ +export const encryptBase64 = (str: CryptoJS.lib.WordArray) => { + return CryptoJS.enc.Base64.stringify(str); +}; + +/** + * 浣跨敤瀵嗛挜瀵规暟鎹繘琛屽姞瀵� + * @param message + * @param aesKey + * @returns {string} + */ +export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => { + const encrypted = CryptoJS.AES.encrypt(message, aesKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return encrypted.toString(); +}; diff --git a/src/utils/jsencrypt.ts b/src/utils/jsencrypt.ts index 18493ad..2d93757 100644 --- a/src/utils/jsencrypt.ts +++ b/src/utils/jsencrypt.ts @@ -2,7 +2,8 @@ // 瀵嗛挜瀵圭敓鎴� http://web.chacuo.net/netrsakeypair const publicKey = - 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='; + 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='; const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + diff --git a/src/utils/request.ts b/src/utils/request.ts index d5fac4e..74f0d19 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -8,6 +8,8 @@ import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; import FileSaver from 'file-saver'; import { getLanguage } from '@/lang'; +import { encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto'; +import { encrypt } from '@/utils/jsencrypt'; let downloadLoadingInstance: LoadingInstance; // 鏄惁鏄剧ず閲嶆柊鐧诲綍 @@ -29,6 +31,8 @@ const isToken = (config.headers || {}).isToken === false; // 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜� const isRepeatSubmit = (config.headers || {}).repeatSubmit === false; + // 鏄惁闇�瑕佸姞瀵� + const isEncrypt = (config.headers || {}).isEncrypt === 'true'; if (getToken() && !isToken) { config.headers['Authorization'] = 'Bearer ' + getToken(); // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀� } @@ -63,6 +67,13 @@ } } } + // 褰撳紑鍚弬鏁板姞瀵� + if (isEncrypt && (config.method === 'post' || config.method === 'put')) { + // 鐢熸垚涓�涓� AES 瀵嗛挜 + const aesKey = generateAesKey(); + config.headers['encrypt-key'] = encrypt(encryptBase64(aesKey)); + config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey); + } // FormData鏁版嵁鍘昏姹傚ごContent-Type if (config.data instanceof FormData) { delete config.headers['Content-Type']; diff --git a/src/views/index.vue b/src/views/index.vue index 4b3bebd..438c1af 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -33,14 +33,14 @@ * 閮ㄧ讲鏂瑰紡 Docker 瀹瑰櫒缂栨帓 涓�閿儴缃蹭笟鍔¢泦缇�<br /> * 鍥介檯鍖� SpringMessage Spring鏍囧噯鍥介檯鍖栨柟妗�<br /> </p> - <p><b>褰撳墠鐗堟湰:</b> <span>v5.0.0</span></p> + <p><b>褰撳墠鐗堟湰:</b> <span>v5.1.0</span></p> <p> <el-tag type="danger">¥鍏嶈垂寮�婧�</el-tag> </p> <p> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">璁块棶鐮佷簯</el-button> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">璁块棶GitHub</el-button> - <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/changlog')" + <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')" >鏇存柊鏃ュ織</el-button > </p> @@ -78,14 +78,14 @@ * 鍒嗗竷寮忕洃鎺� Prometheus銆丟rafana 鍏ㄦ柟浣嶆�ц兘鐩戞帶<br /> * 鍏朵綑涓� Vue 鐗堟湰涓�鑷�<br /> </p> - <p><b>褰撳墠鐗堟湰:</b> <span>v2.0.0</span></p> + <p><b>褰撳墠鐗堟湰:</b> <span>v2.1.0</span></p> <p> <el-tag type="danger">¥鍏嶈垂寮�婧�</el-tag> </p> <p> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">璁块棶鐮佷簯</el-button> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">璁块棶GitHub</el-button> - <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-cloud-plus/changlog')" + <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')" >鏇存柊鏃ュ織</el-button > </p> diff --git a/src/views/login.vue b/src/views/login.vue index 709b0ce..4c8715a 100644 --- a/src/views/login.vue +++ b/src/views/login.vue @@ -72,7 +72,7 @@ const router = useRouter(); const loginForm = ref<LoginData>({ - tenantId: "000000", + tenantId: '000000', username: 'admin', password: 'admin123', rememberMe: false, @@ -176,6 +176,12 @@ } } } + +//妫�娴嬬鎴烽�夋嫨妗嗙殑鍙樺寲 +watch(() => loginForm.value.tenantId, (val: string) => { + Cookies.set("tenantId", loginForm.value.tenantId, { expires: 30 }) +}); + /** * 绗笁鏂圭櫥褰� * @param type @@ -183,8 +189,9 @@ const doSocialLogin = (type: string) => { authBinding(type).then((res: any) => { if (res.code === HttpStatus.SUCCESS) { - window.location.href = res.msg; - } else { + // 鑾峰彇鎺堟潈鍦板潃璺宠浆 + window.location.href = res.data; + } else { ElMessage.error(res.msg); } }); diff --git a/src/views/monitor/cache/index.vue b/src/views/monitor/cache/index.vue index 2914d59..8719c94 100644 --- a/src/views/monitor/cache/index.vue +++ b/src/views/monitor/cache/index.vue @@ -133,56 +133,59 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance; const getList = async () => { - proxy?.$modal.loading("姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒"); - const res = await getCache(); - proxy?.$modal.closeLoading(); - cache.value = res.data; - const commandstatsIntance = echarts.init(commandstats.value, "macarons"); - commandstatsIntance.setOption({ - tooltip: { - trigger: "item", - formatter: "{a} <br/>{b} : {c} ({d}%)" + proxy?.$modal.loading("姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒"); + const res = await getCache(); + proxy?.$modal.closeLoading(); + cache.value = res.data; + const commandstatsIntance = echarts.init(commandstats.value, "macarons"); + commandstatsIntance.setOption({ + tooltip: { + trigger: "item", + formatter: "{a} <br/>{b} : {c} ({d}%)" + }, + series: [ + { + name: "鍛戒护", + type: "pie", + roseType: "radius", + radius: [15, 95], + center: ["50%", "38%"], + data: res.data.commandStats, + animationEasing: "cubicInOut", + animationDuration: 1000 + } + ] + }); + const usedmemoryInstance = echarts.init(usedmemory.value, "macarons"); + usedmemoryInstance.setOption({ + tooltip: { + formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human + }, + series: [ + { + name: "宄板��", + type: "gauge", + min: 0, + max: 1000, + detail: { + formatter: cache.value.info.used_memory_human }, - series: [ - { - name: "鍛戒护", - type: "pie", - roseType: "radius", - radius: [15, 95], - center: ["50%", "38%"], - data: res.data.commandStats, - animationEasing: "cubicInOut", - animationDuration: 1000 - } + data: [ + { + value: parseFloat(cache.value.info.used_memory_human), + name: "鍐呭瓨娑堣��" + } ] - }); - - const usedmemoryInstance = echarts.init(usedmemory.value, "macarons"); - usedmemoryInstance.setOption({ - tooltip: { - formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human - }, - series: [ - { - name: "宄板��", - type: "gauge", - min: 0, - max: 1000, - detail: { - formatter: cache.value.info.used_memory_human - }, - data: [ - { - value: parseFloat(cache.value.info.used_memory_human), - name: "鍐呭瓨娑堣��" - } - ] - } - ] - }) + } + ] + }) + window.addEventListener("resize",()=>{ + commandstatsIntance.resize() + usedmemoryInstance.resize() + }); } onMounted(() => { - getList(); + getList(); }) </script> diff --git a/src/views/system/client/index.vue b/src/views/system/client/index.vue new file mode 100644 index 0000000..2130d03 --- /dev/null +++ b/src/views/system/client/index.vue @@ -0,0 +1,344 @@ +<template> + <div class="p-2"> + <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px"> + <el-form-item label="瀹㈡埛绔痥ey" prop="clientKey"> + <el-input v-model="queryParams.clientKey" placeholder="璇疯緭鍏ュ鎴风key" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="瀹㈡埛绔閽�" prop="clientSecret"> + <el-input v-model="queryParams.clientSecret" placeholder="璇疯緭鍏ュ鎴风绉橀挜" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="鐘舵��" prop="status"> + <el-select v-model="queryParams.status" placeholder="鐘舵��" clearable> + <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + </transition> + + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:client:add']">鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:client:edit']">淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:client:remove']">鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:client:export']">瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="id" align="center" prop="id" v-if="true" /> + <el-table-column label="瀹㈡埛绔痠d" align="center" prop="clientId" /> + <el-table-column label="瀹㈡埛绔痥ey" align="center" prop="clientKey" /> + <el-table-column label="瀹㈡埛绔閽�" align="center" prop="clientSecret" /> + <el-table-column label="鎺堟潈绫诲瀷" align="center"> + <template #default="scope"> + <div> + <template v-for="type in scope.row.grantTypeList"> + <dict-tag class="el-check-tag" :options="sys_grant_type" :value="type" /> + </template> + </div> + </template> + </el-table-column> + <el-table-column label="璁惧绫诲瀷" align="center"> + <template #default="scope"> + <dict-tag :options="sys_device_type" :value="scope.row.deviceType" /> + </template> + </el-table-column> + <el-table-column label="Token娲昏穬瓒呮椂鏃堕棿" align="center" prop="activeTimeout" /> + <el-table-column label="Token鍥哄畾瓒呮椂鏃堕棿" align="center" prop="timeout" /> + <el-table-column label="鐘舵��" align="center" key="status"> + <template #default="scope"> + <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:client:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:client:remove']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total>0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </el-card> + <!-- 娣诲姞鎴栦慨鏀瑰鎴风绠$悊瀵硅瘽妗� --> + <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> + <el-form ref="clientFormRef" :model="form" :rules="rules" label-width="100px"> + <el-form-item label="瀹㈡埛绔痥ey" prop="clientKey"> + <el-input v-model="form.clientKey" :disabled="form.id != null" placeholder="璇疯緭鍏ュ鎴风key" /> + </el-form-item> + <el-form-item label="瀹㈡埛绔閽�" prop="clientSecret"> + <el-input v-model="form.clientSecret" :disabled="form.id != null" placeholder="璇疯緭鍏ュ鎴风绉橀挜" /> + </el-form-item> + <el-form-item label="鎺堟潈绫诲瀷" prop="grantTypeList"> + <el-select v-model="form.grantTypeList" multiple placeholder="璇疯緭鍏ユ巿鏉冪被鍨�"> + <el-option + v-for="dict in sys_grant_type" + :key="dict.value" :label="dict.label" :value="dict.value" + ></el-option> + </el-select> + </el-form-item> + <el-form-item label="璁惧绫诲瀷" prop="deviceType"> + <el-select v-model="form.deviceType" placeholder="璇疯緭鍏ヨ澶囩被鍨�"> + <el-option + v-for="dict in sys_device_type" + :key="dict.value" :label="dict.label" :value="dict.value" + ></el-option> + </el-select> + </el-form-item> + <el-form-item prop="activeTimeout" label-width="auto"> + <template #label> + <span> + <el-tooltip content="鎸囧畾鏃堕棿鏃犳搷浣滃垯杩囨湡锛堝崟浣嶏細绉掞級锛岄粯璁�30鍒嗛挓锛�1800绉掞級" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + Token娲昏穬瓒呮椂鏃堕棿 + </span> + </template> + <el-input v-model="form.activeTimeout" placeholder="璇疯緭鍏oken娲昏穬瓒呮椂鏃堕棿" /> + </el-form-item> + <el-form-item prop="timeout" label-width="auto"> + <template #label> + <span> + <el-tooltip content="鎸囧畾鏃堕棿蹇呭畾杩囨湡锛堝崟浣嶏細绉掞級锛岄粯璁や竷澶╋紙604800绉掞級" placement="top"> + <el-icon><question-filled /></el-icon> + </el-tooltip> + Token鍥哄畾瓒呮椂鏃堕棿 + </span> + </template> + <el-input v-model="form.timeout" placeholder="璇疯緭鍏oken鍥哄畾瓒呮椂鏃堕棿" /> + </el-form-item> + <el-form-item label="鐘舵��"> + <el-radio-group v-model="form.status"> + <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value"> + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Client" lang="ts"> +import { listClient, getClient, delClient, addClient, updateClient, changeStatus } from '@/api/system/client'; +import { ClientVO, ClientQuery, ClientForm } from '@/api/system/client/types'; +import { ComponentInternalInstance } from 'vue'; +import { ElForm } from 'element-plus'; + +const { proxy } = getCurrentInstance() as ComponentInternalInstance; +const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); +const { sys_grant_type } = toRefs<any>(proxy?.useDict("sys_grant_type")); +const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); + +const clientList = ref<ClientVO[]>([]); +const buttonLoading = ref(false); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref<Array<string | number>>([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); + +const queryFormRef = ref(ElForm); +const clientFormRef = ref(ElForm); + +const dialog = reactive<DialogOption>({ + visible: false, + title: '' +}); + +const initFormData: ClientForm = { + id: undefined, + clientId: undefined, + clientKey: undefined, + clientSecret: undefined, + grantTypeList: undefined, + deviceType: undefined, + activeTimeout: undefined, + timeout: undefined, + status: undefined, +} +const data = reactive<PageData<ClientForm, ClientQuery>>({ + form: {...initFormData}, + queryParams: { + pageNum: 1, + pageSize: 10, + clientId: undefined, + clientKey: undefined, + clientSecret: undefined, + grantType: undefined, + deviceType: undefined, + activeTimeout: undefined, + timeout: undefined, + status: undefined, + }, + rules: { + id: [ + { required: true, message: "id涓嶈兘涓虹┖", trigger: "blur" } + ], + clientId: [ + { required: true, message: "瀹㈡埛绔痠d涓嶈兘涓虹┖", trigger: "blur" } + ], + clientKey: [ + { required: true, message: "瀹㈡埛绔痥ey涓嶈兘涓虹┖", trigger: "blur" } + ], + clientSecret: [ + { required: true, message: "瀹㈡埛绔閽ヤ笉鑳戒负绌�", trigger: "blur" } + ], + grantTypeList: [ + { required: true, message: "鎺堟潈绫诲瀷涓嶈兘涓虹┖", trigger: "change" } + ], + deviceType: [ + { required: true, message: "璁惧绫诲瀷涓嶈兘涓虹┖", trigger: "change" } + ], + } +}); + +const { queryParams, form, rules } = toRefs(data); + +/** 鏌ヨ瀹㈡埛绔鐞嗗垪琛� */ +const getList = async () => { + loading.value = true; + const res = await listClient(queryParams.value); + clientList.value = res.rows; + total.value = res.total; + loading.value = false; +} + +/** 鍙栨秷鎸夐挳 */ +const cancel = () => { + reset(); + dialog.visible = false; +} + +/** 琛ㄥ崟閲嶇疆 */ +const reset = () => { + form.value = {...initFormData}; + clientFormRef.value.resetFields(); +} + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} + +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} + +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection: ClientVO[]) => { + ids.value = selection.map(item => item.id); + single.value = selection.length != 1; + multiple.value = !selection.length; +} + +/** 鏂板鎸夐挳鎿嶄綔 */ +const handleAdd = () => { + dialog.visible = true; + dialog.title = "娣诲姞瀹㈡埛绔鐞�"; + nextTick(() => { + reset(); + }); +} + +/** 淇敼鎸夐挳鎿嶄綔 */ +const handleUpdate = (row?: ClientVO) => { + loading.value = true + dialog.visible = true; + dialog.title = "淇敼瀹㈡埛绔鐞�"; + nextTick(async () => { + reset(); + const _id = row?.id || ids.value[0] + const res = await getClient(_id); + loading.value = false; + Object.assign(form.value, res.data); + }); +} + +/** 鎻愪氦鎸夐挳 */ +const submitForm = () => { + clientFormRef.value.validate(async (valid: boolean) => { + if (valid) { + buttonLoading.value = true; + if (form.value.id) { + await updateClient(form.value).finally(() => buttonLoading.value = false); + } else { + await addClient(form.value).finally(() => buttonLoading.value = false); + } + proxy?.$modal.msgSuccess("淇敼鎴愬姛"); + dialog.visible = false; + await getList(); + } + }); +} + +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row?: ClientVO) => { + const _ids = row?.id || ids.value; + await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀹㈡埛绔鐞嗙紪鍙蜂负"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false); + await delClient(_ids); + proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛"); + await getList(); +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +const handleExport = () => { + proxy?.download('system/client/export', { + ...queryParams.value + }, `client_${new Date().getTime()}.xlsx`) +} + +/** 鐘舵�佷慨鏀� */ +const handleStatusChange = async (row: ClientVO) => { + let text = row.status === "0" ? "鍚敤" : "鍋滅敤" + try { + await proxy?.$modal.confirm('纭瑕�"' + text + '"鍚�?'); + await changeStatus(row.id, row.status); + proxy?.$modal.msgSuccess(text + "鎴愬姛"); + } catch (err) { + row.status = row.status === "0" ? "1" : "0"; + } +} + +onMounted(() => { + getList(); +}); +</script> diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue index 00979c9..3b496ea 100644 --- a/src/views/system/menu/index.vue +++ b/src/views/system/menu/index.vue @@ -181,7 +181,7 @@ </el-col> <el-col :span="12" v-if="form.menuType === 'C'"> <el-form-item> - <el-input v-model="form.query" placeholder="璇疯緭鍏ヨ矾鐢卞弬鏁�" maxlength="255" /> + <el-input v-model="form.queryParam" placeholder="璇疯緭鍏ヨ矾鐢卞弬鏁�" maxlength="255" /> <template #label> <span> <el-tooltip content='璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁帮紝濡傦細`{"id": 1, "name": "ry"}`' placement="top"> diff --git a/src/views/system/user/profile/thirdParty.vue b/src/views/system/user/profile/thirdParty.vue index e5791ef..2e4e722 100644 --- a/src/views/system/user/profile/thirdParty.vue +++ b/src/views/system/user/profile/thirdParty.vue @@ -80,7 +80,7 @@ const authUrl = (source: string) => { authBinding(source).then((res: any) => { if (res.code === 200) { - window.location.href = res.msg; + window.location.href = res.data; } else { ElMessage.error(res.msg); } -- Gitblit v1.9.3