.env.development | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
.env.production | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
.gitignore | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/api/login.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/api/system/social/auth.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/assets/icons/svg/gitee.svg | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/layout/components/SocialLogin/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/permission.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/router/index.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/types/env.d.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/login.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/monitor/powerjob/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/system/user/profile/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/system/user/profile/thirdParty.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.env.development
@@ -13,7 +13,7 @@ # çæ§å°å VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications' # xxl-job æ§å¶å°å°å VITE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin' # powerjob æ§å¶å°å°å VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/' VITE_APP_PORT = 80 .env.production
@@ -10,8 +10,8 @@ # çæ§å°å VITE_APP_MONITRO_ADMIN = '/admin/applications' # çæ§å°å VITE_APP_XXL_JOB_ADMIN = '/xxl-job-admin' # powerjob æ§å¶å°å°å VITE_APP_POWERJOB_ADMIN = '/powerjob' # ç产ç¯å¢ VITE_APP_BASE_API = '/prod-api' .gitignore
@@ -1,4 +1,5 @@ .DS_Store .history node_modules/ dist/ npm-debug.log* src/api/login.ts
@@ -60,6 +60,21 @@ timeout: 20000 }); } /** * ç¬¬ä¸æ¹ç»å½ * @param source ç¬¬ä¸æ¹ç»å½ç±»å * */ export function socialLogin(source: string, code: any, state: any): AxiosPromise<any> { const data = { code, state }; return request({ url: '/auth/social-login/' + source, method: 'get', params: data }); } // è·åç¨æ·è¯¦ç»ä¿¡æ¯ export function getInfo(): AxiosPromise<UserInfo> { src/api/system/social/auth.ts
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,24 @@ import request from '@/utils/request'; // ç»å®è´¦å· export function authBinding(source: string) { return request({ url: '/auth/binding/' + source, method: 'get' }); } // è§£ç»è´¦å· export function authUnlock(authId: string) { return request({ url: '/auth/unlock/' + authId, method: 'delete' }); } //è·åææå表 export function getAuthList() { return request({ url: '/system/social/list', method: 'get' }); } src/assets/icons/svg/gitee.svg
¶Ô±ÈÐÂÎļþ @@ -0,0 +1 @@ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686919908144" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2521" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 992C246.895625 992 32 777.104375 32 512S246.895625 32 512 32s480 214.895625 480 480-214.895625 480-480 480z m242.9521875-533.3278125h-272.56875a23.7121875 23.7121875 0 0 0-23.71125 23.7121875l-0.024375 59.255625c0 13.08 10.6078125 23.7121875 23.6878125 23.7121875h165.96c13.104375 0 23.7121875 10.6078125 23.7121875 23.6878125v11.855625a71.1121875 71.1121875 0 0 1-71.1121875 71.1121875h-225.215625a23.7121875 23.7121875 0 0 1-23.6878125-23.7121875V423.1278125a71.1121875 71.1121875 0 0 1 71.0878125-71.1121875h331.824375a23.7121875 23.7121875 0 0 0 23.6878125-23.71125l0.0721875-59.2565625a23.7121875 23.7121875 0 0 0-23.68875-23.7121875H423.08a177.76875 177.76875 0 0 0-177.76875 177.7921875V754.953125c0 13.1034375 10.60875 23.7121875 23.713125 23.7121875h349.63125a159.984375 159.984375 0 0 0 159.984375-159.984375V482.36a23.7121875 23.7121875 0 0 0-23.7121875-23.6878125z" fill="#515151" p-id="2522"></path></svg> src/layout/components/SocialLogin/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,36 @@ <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> src/permission.ts
@@ -10,7 +10,7 @@ import usePermissionStore from '@/store/modules/permission'; NProgress.configure({ showSpinner: false }); const whiteList = ['/login', '/register']; const whiteList = ['/login', '/register', '/social-login']; router.beforeEach(async (to, from, next) => { NProgress.start(); src/router/index.ts
@@ -38,6 +38,11 @@ ] }, { path: '/social-login', hidden: true, component: () => import('@/layout/components/SocialLogin/index.vue') }, { path: '/login', component: () => import('@/views/login.vue'), hidden: true src/types/env.d.ts
@@ -65,7 +65,7 @@ VITE_APP_BASE_URL: string; VITE_APP_CONTEXT_PATH: string; VITE_APP_MONITRO_ADMIN: string; VITE_APP_XXL_JOB_ADMIN: string; VITE_APP_POWERJOB_ADMIN: string; VITE_APP_ENV: string; } interface ImportMeta { src/views/index.vue
@@ -21,7 +21,7 @@ * åå¸å¼é Lock4j 注解éãå·¥å ·é å¤ç§å¤æ ·<br /> * åå¸å¼å¹ç Lock4j åºäºåå¸å¼éå®ç°<br /> * åå¸å¼é¾è·¯è¿½è¸ª SkyWalking æ¯æé¾è·¯è¿½è¸ªãç½æ ¼åæã度éèåãå¯è§å<br /> * åå¸å¼ä»»å¡è°åº¦ Xxl-Job 髿§è½ é«å¯é ææ©å±<br /> * åå¸å¼ä»»å¡è°åº¦ PowerJob 髿§è½ é«å¯é ææ©å±<br /> * æä»¶åå¨ Minio æ¬å°åå¨<br /> * æä»¶åå¨ ä¸çãé¿éãè ¾è®¯ äºåå¨<br /> * çæ§æ¡æ¶ SpringBoot-Admin å ¨æ¹ä½æå¡çæ§<br /> src/views/login.vue
@@ -4,7 +4,7 @@ <h3 class="title">RuoYi-Vue-Pluså¤ç§æ·ç®¡çç³»ç»</h3> <el-form-item prop="tenantId" v-if="tenantEnabled"> <el-select v-model="loginForm.tenantId" filterable placeholder="è¯·éæ©/è¾å ¥å ¬å¸åç§°" style="width: 100%"> <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option> <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option> <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template> </el-select> </el-form-item> @@ -36,6 +36,20 @@ <router-link class="link-type" :to="'/register'">ç«å³æ³¨å</router-link> </div> </el-form-item> <div style="display: flex;justify-content: flex-end;flex-direction: row;"> <el-button circle> <svg-icon icon-class="qq" @click="doSocialLogin('qq')" /> </el-button> <el-button circle> <svg-icon icon-class="wechat" @click="doSocialLogin('wechat')" /> </el-button> <el-button circle> <svg-icon icon-class="gitee" @click="doSocialLogin('gitee')" /> </el-button> <el-button circle> <svg-icon icon-class="github" @click="doSocialLogin('github')" /> </el-button> </div> </el-form> <!-- åºé¨ --> <div class="el-login-footer"> @@ -46,11 +60,13 @@ <script setup lang="ts"> import { getCodeImg, getTenantList } from '@/api/login'; import { authBinding } from '@/api/system/social/auth'; import Cookies from 'js-cookie'; import { encrypt, decrypt } from '@/utils/jsencrypt'; import { useUserStore } from '@/store/modules/user'; import { LoginData, TenantVO } from '@/api/types'; import { to } from 'await-to-js'; import { HttpStatus } from "@/enums/RespEnum"; const userStore = useUserStore(); const router = useRouter(); @@ -82,12 +98,12 @@ // 注åå¼å ³ const register = ref(false); const redirect = ref(undefined); const loginRef = ref(ElForm); const loginRef = ref<ElFormInstance>(); // ç§æ·å表 const tenantList = ref<TenantVO[]>([]); const handleLogin = () => { loginRef.value.validate(async (valid: boolean, fields: any) => { loginRef.value?.validate(async (valid: boolean, fields: any) => { if (valid) { loading.value = true; // å¾éäºéè¦è®°ä½å¯ç è®¾ç½®å¨ cookie ä¸è®¾ç½®è®°ä½ç¨æ·ååå¯ç @@ -104,7 +120,6 @@ Cookies.remove('rememberMe'); } // è°ç¨actionçç»å½æ¹æ³ // prittier-ignore const [err] = await to(userStore.login(loginForm.value)); if (!err) { await router.push({ path: redirect.value || '/' }); @@ -161,6 +176,21 @@ } } } /** * ç¬¬ä¸æ¹ç»å½ * @param type */ const doSocialLogin = (type: string) => { authBinding(type).then((res: any) => { if (res.code === HttpStatus.SUCCESS) { window.location.href = res.msg; } else { ElMessage.error(res.msg); } }); }; onMounted(() => { getCode(); src/views/monitor/powerjob/index.vue
ÎļþÃû´Ó src/views/monitor/xxljob/index.vue ÐÞ¸Ä @@ -5,5 +5,5 @@ </template> <script setup lang="ts"> const url = ref(import.meta.env.VITE_APP_XXL_JOB_ADMIN); const url = ref(import.meta.env.VITE_APP_POWERJOB_ADMIN); </script> src/views/system/user/profile/index.vue
@@ -55,6 +55,9 @@ <el-tab-pane label="ä¿®æ¹å¯ç " name="resetPwd"> <resetPwd /> </el-tab-pane> <el-tab-pane label="ç¬¬ä¸æ¹åºç¨" name="thirdParty"> <thirdParty :auths="state.auths" /> </el-tab-pane> </el-tabs> </el-card> </el-col> @@ -66,26 +69,35 @@ import userAvatar from "./userAvatar.vue"; import userInfo from "./userInfo.vue"; import resetPwd from "./resetPwd.vue"; import thirdParty from "./thirdParty.vue"; import { getAuthList } from "@/api/system/social/auth"; import { getUserProfile } from "@/api/system/user"; const activeTab = ref("userinfo"); const state = ref<Record<string, any>>({ user: {}, roleGroup: '', postGroup: '' user: {}, roleGroup: '', postGroup: '', auths: [] }); const userForm = ref({}); const getUser = async () => { const res = await getUserProfile(); state.value.user = res.data.user; userForm.value = { ...res.data.user } state.value.roleGroup = res.data.roleGroup; state.value.postGroup = res.data.postGroup; const res = await getUserProfile(); state.value.user = res.data.user; userForm.value = { ...res.data.user } state.value.roleGroup = res.data.roleGroup; state.value.postGroup = res.data.postGroup; }; const getAuths = async () => { const res = await getAuthList(); state.value.auths = res.data; }; onMounted(() => { getUser(); getUser(); getAuths(); }) </script> src/views/system/user/profile/thirdParty.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,140 @@ <template> <div> <el-table :data="auths" style="width: 100%; height: 100%; font-size: 10px"> <el-table-column label="åºå·" width="50" type="index"></el-table-column> <el-table-column label="ç»å®è´¦å·å¹³å°" width="140" align="center" prop="source" show-overflow-tooltip /> <el-table-column label="头å" width="120" align="center" prop="avatar"> <template v-slot="scope"> <img :src="scope.row.avatar" style="width: 45px; height: 45px" /> </template> </el-table-column> <el-table-column label="ç³»ç»è´¦å·" width="180" align="center" prop="userName" :show-overflow-tooltip="true" /> <el-table-column label="ç»å®æ¶é´" width="180" align="center" prop="createTime" /> <el-table-column label="æä½" width="80" align="center" class-name="small-padding fixed-width"> <template v-slot="scope"> <el-button size="small" type="text" @click="unlockAuth(scope.row)">è§£ç»</el-button> </template> </el-table-column> </el-table> <div id="git-user-binding"> <h4 class="provider-desc">ä½ å¯ä»¥ç»å®ä»¥ä¸ç¬¬ä¸æ¹å¸å·</h4> <div id="authlist" class="user-bind"> <a class="third-app" href="#" @click="authUrl('gitee');" title="ä½¿ç¨ Gitee è´¦å·ææç»å½"> <div class="git-other-login-icon"> <svg-icon icon-class="gitee" /> </div> <span class="app-name">Gitee</span> </a> <a class="third-app" href="#" @click="authUrl('github');" title="ä½¿ç¨ GitHub è´¦å·ææç»å½"> <div class="git-other-login-icon"> <svg-icon icon-class="github" /> </div> <span class="app-name">Github</span> </a> <a class="third-app" href="#" @click="authUrl('wechar');" title="ä½¿ç¨ å¾®ä¿¡ è´¦å·ææç»å½"> <div class="git-other-login-icon"> <svg-icon icon-class="wechat" /> </div> <span class="app-name">WeiXin</span> </a> <a class="third-app" href="#" @click="authUrl('qq');" title="ä½¿ç¨ QQ è´¦å·ææç»å½"> <div class="git-other-login-icon"> <svg-icon icon-class="qq" /> </div> <span class="app-name">QQ</span> </a> </div> </div> </div> </template> <script lang="ts" setup> import { authUnlock, authBinding } from "@/api/system/social/auth"; import { PropType } from "vue"; const props = defineProps({ auths: { type: Object as PropType<any>, } }); const auths = computed(() => props.auths); const unlockAuth = (row: any) => { ElMessageBox.confirm('æ¨ç¡®å®è¦è§£é¤"' + row.source + '"çè´¦å·ç»å®åï¼') .then(() => { return authUnlock(row.id); }).then((res: any) => { if (res.code === 200) { ElMessage.success("è§£ç»æå"); } else { ElMessage.error(res.msg); } }).catch(() => { }); }; const authUrl = (source: string) => { authBinding(source).then((res: any) => { if (res.code === 200) { window.location.href = res.msg; } else { ElMessage.error(res.msg); } }); }; </script> <style type="text/css"> .user-bind .third-app { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; -webkit-box-align: center; -ms-flex-align: center; align-items: center; min-width: 80px; float: left; } .user-bind { font-size: 1rem; text-align: start; height: 50px; margin-top: 10px; } .git-other-login-icon>img { height: 32px; } a { text-decoration: none; cursor: pointer; color: #005980; } .provider-desc { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Liberation Sans", "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti", SimHei, SimSun, "WenQuanYi Zen Hei Sharp", sans-serif; font-size: 1.071rem; } td>img { height: 20px; width: 20px; display: inline-block; border-radius: 50%; margin-right: 5px; } </style>