mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-03 12:29:14 +08:00
Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # web-vue3/src/api/config.ts # web-vue3/src/pages/login/install.vue
This commit is contained in:
commit
9fc83dfda6
@ -5,7 +5,7 @@ import { refreshToken } from './user/user'
|
||||
import { notification } from 'ant-design-vue'
|
||||
import appStore from '@/stores/app'
|
||||
import userStore from '@/stores/user'
|
||||
import { useMenuStore } from '@/stores/menu'
|
||||
import menuStore from '@/stores/menu'
|
||||
|
||||
const _window = window as any
|
||||
const delTimeout = 20 * 1000
|
||||
@ -15,7 +15,7 @@ const pro = process.env.NODE_ENV === 'production'
|
||||
|
||||
// 创建实例
|
||||
const instance: AxiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_API_BASE_URL,
|
||||
baseURL: import.meta.env.JPOM_BASE_API_URL,
|
||||
|
||||
timeout: apiTimeout || delTimeout,
|
||||
headers: {
|
||||
@ -27,13 +27,15 @@ const instance: AxiosInstance = axios.create({
|
||||
// 请求拦截
|
||||
instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||
const { headers } = config
|
||||
headers[TOKEN_HEADER_KEY] = userStore.getToken
|
||||
const accessToken = localStorage.getItem('accessToken')
|
||||
headers['Authorization'] = accessToken ? 'Bearer ' + accessToken : ''
|
||||
headers[TOKEN_HEADER_KEY] = userStore.token
|
||||
headers[CACHE_WORKSPACE_ID] = appStore.getWorkspaceId
|
||||
|
||||
if (_window.routerBase) {
|
||||
// 防止 url 出现 //
|
||||
config.url = (_window.routerBase + config.url).replace(new RegExp('//', 'gm'), '/')
|
||||
}
|
||||
// if (_window.routerBase) {
|
||||
// // 防止 url 出现 //
|
||||
// config.url = (_window.routerBase + config.url).replace(new RegExp('//', 'gm'), '/')
|
||||
// }
|
||||
return config
|
||||
})
|
||||
|
||||
@ -142,8 +144,8 @@ async function redoRequest(config: AxiosRequestConfig) {
|
||||
const result = await refreshToken()
|
||||
if (result.code === 200) {
|
||||
// 调用 store action 存储当前登录的用户名和 token
|
||||
await userStore.reLogin(result.data)
|
||||
await useMenuStore().loadSystemMenus()
|
||||
await userStore.login(result.data)
|
||||
await menuStore.loadSystemMenus()
|
||||
request(config)
|
||||
return result
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ async function redoRequest(config: AxiosRequestConfig) {
|
||||
const result = await refreshToken()
|
||||
if (result.code === 200) {
|
||||
// 调用 store action 存储当前登录的用户名和 token
|
||||
await userStore.reLogin(result.data)
|
||||
await userStore.login(result.data)
|
||||
await useMenuStore().loadSystemMenus()
|
||||
request(config)
|
||||
return result
|
||||
|
@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<div class="init-wrapper">
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 1440 500"
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
>
|
||||
<svg width="100%" height="100%" viewBox="0 0 1440 500" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g>
|
||||
<circle stroke="#13C2C2" cx="500" cy="-20" r="6"></circle>
|
||||
<circle fill-opacity="0.4" fill="#9EE6E6" cx="166" cy="76" r="8"></circle>
|
||||
@ -20,25 +12,18 @@
|
||||
<g>
|
||||
<path
|
||||
d="M1182.79367,448.230356 L1186.00213,453.787581 C1186.55442,454.744166 1186.22667,455.967347 1185.27008,456.519632 C1184.96604,456.695168 1184.62116,456.787581 1184.27008,456.787581 L1177.85315,456.787581 C1176.74858,456.787581 1175.85315,455.89215 1175.85315,454.787581 C1175.85315,454.436507 1175.94556,454.091619 1176.1211,453.787581 L1179.32957,448.230356 C1179.88185,447.273771 1181.10503,446.946021 1182.06162,447.498305 C1182.36566,447.673842 1182.61813,447.926318 1182.79367,448.230356 Z"
|
||||
stroke="#CED4D9"
|
||||
></path>
|
||||
stroke="#CED4D9"></path>
|
||||
<path
|
||||
d="M1376.79367,204.230356 L1380.00213,209.787581 C1380.55442,210.744166 1380.22667,211.967347 1379.27008,212.519632 C1378.96604,212.695168 1378.62116,212.787581 1378.27008,212.787581 L1371.85315,212.787581 C1370.74858,212.787581 1369.85315,211.89215 1369.85315,210.787581 C1369.85315,210.436507 1369.94556,210.091619 1370.1211,209.787581 L1373.32957,204.230356 C1373.88185,203.273771 1375.10503,202.946021 1376.06162,203.498305 C1376.36566,203.673842 1376.61813,203.926318 1376.79367,204.230356 Z"
|
||||
stroke="#2F54EB"
|
||||
></path>
|
||||
stroke="#2F54EB"></path>
|
||||
</g>
|
||||
<g>
|
||||
<rect stroke="#13C2C2" stroke-opacity="0.6" x="120" y="322" width="12" height="12" rx="1"></rect>
|
||||
<rect stroke="#CED4D9" x="108" y="1" width="9" height="9" rx="1"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
<a-card
|
||||
v-if="canInstall"
|
||||
class="card-box"
|
||||
:style="`${setpCurrent === 1 ? 'width: 60vw' : 'width: 550px'}`"
|
||||
hoverable
|
||||
:bodyStyle="{ padding: '24px 0', overflow: 'auto' }"
|
||||
>
|
||||
<a-card v-if="canInstall" class="card-box" :style="`${setpCurrent === 1 ? 'width: 60vw' : 'width: 550px'}`" hoverable
|
||||
:bodyStyle="{ padding: '24px 0', overflow: 'auto' }">
|
||||
<template #title>
|
||||
<a-steps :current="setpCurrent">
|
||||
<a-step title="初始化系统" status="process" description="设置一个超级管理员账号">
|
||||
@ -56,34 +41,20 @@
|
||||
|
||||
<a-row type="flex" justify="center">
|
||||
<a-col :span="16" v-if="setpCurrent === 0">
|
||||
<a-card-meta
|
||||
title="初始化系统账户"
|
||||
style="textalign: center"
|
||||
description="您需要创建一个账户用以后续登录管理系统,请牢记超级管理员账号密码"
|
||||
/>
|
||||
<a-card-meta title="初始化系统账户" style="textalign: center" description="您需要创建一个账户用以后续登录管理系统,请牢记超级管理员账号密码" />
|
||||
<br />
|
||||
<a-form
|
||||
:model="loginForm"
|
||||
name="login"
|
||||
:label-col="{ span: 0 }"
|
||||
:wrapper-col="{ span: 24 }"
|
||||
@finish="handleLogin"
|
||||
class="init-form"
|
||||
>
|
||||
<a-form :model="loginForm" name="login" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }"
|
||||
@finish="handleLogin" class="init-form">
|
||||
<a-form-item class="init-user-name" name="userName" :rules="[{ required: true, message: '请输入账户名' }]">
|
||||
<a-input v-model:value="loginForm.userName" placeholder="账户名称" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
class="init-user-password"
|
||||
name="userPwd"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入密码' },
|
||||
{
|
||||
pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,18}$/,
|
||||
message: '密码必须包含数字,字母,字符,且大于6位',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-form-item class="init-user-password" name="userPwd" :rules="[
|
||||
{ required: true, message: '请输入密码' },
|
||||
{
|
||||
pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,18}$/,
|
||||
message: '密码必须包含数字,字母,字符,且大于6位',
|
||||
},
|
||||
]">
|
||||
<a-input-password v-model:value="loginForm.userPwd" placeholder="密码(6-18位数字、字母、符号组合)" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
@ -111,51 +82,28 @@
|
||||
</a-col>
|
||||
<a-divider type="vertical" />
|
||||
<a-col :span="20">
|
||||
<a-form :form="bindMfaForm" :label-col="{ span: 0 }" @submit="handleMfaSure" class="init-form">
|
||||
<a-form-item
|
||||
label="二维码"
|
||||
:label-col="{ span: 5 }"
|
||||
:wrapper-col="{ span: 18 }"
|
||||
style="margin-bottom: 5px"
|
||||
>
|
||||
<a-form :model="mfaForm" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }" @submit="handleMfaSure"
|
||||
class="init-form">
|
||||
<a-form-item label="二维码" style="margin-bottom: 5px">
|
||||
<div class="qrcode">
|
||||
<qrcode-vue :value="qrCode.value" :size="qrCode.size" level="H" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="MFA key" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
|
||||
<a-input
|
||||
v-clipboard:copy="mfaData.key"
|
||||
v-clipboard:success="
|
||||
() => {
|
||||
notification.success({ message: '复制成功' })
|
||||
}
|
||||
"
|
||||
v-clipboard:error="
|
||||
() => {
|
||||
notification.error({ message: '复制失败' })
|
||||
}
|
||||
"
|
||||
readOnly
|
||||
disabled
|
||||
v-model="mfaData.key"
|
||||
>
|
||||
<copy-outlined />
|
||||
</a-input>
|
||||
<a-form-item label="MFA key" name="mfa">
|
||||
<a-input-group compact>
|
||||
<a-input v-model:value="mfaForm.mfa" disabled style="width: calc(100% - 32px)">
|
||||
</a-input>
|
||||
<a-button style="padding: 4px 6px;">
|
||||
<a-typography-paragraph :copyable="{ text: mfaForm.mfa }"></a-typography-paragraph>
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="验证码" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
|
||||
<a-input
|
||||
v-decorator="[
|
||||
'twoCode',
|
||||
{
|
||||
rules: [
|
||||
{ required: true, message: '请输入两步验证码' },
|
||||
{ pattern: /^\d{6}$/, message: '验证码 6 为纯数字' },
|
||||
],
|
||||
},
|
||||
]"
|
||||
placeholder="两步验证码"
|
||||
/>
|
||||
<a-form-item label="验证码" name="twoCode" :rules="[
|
||||
{ required: true, message: '请输入两步验证码' },
|
||||
{ pattern: /^\d{6}$/, message: '验证码 6 为纯数字' },
|
||||
]">
|
||||
<a-input v-model:value="mfaForm.twoCode" placeholder="两步验证码" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
@ -163,7 +111,7 @@
|
||||
<a-col :span="10">
|
||||
<a-space>
|
||||
<a-button type="primary" html-type="submit" class="btn"> 确认绑定 </a-button>
|
||||
<a-button type="dashed" @click="ignoreBindMfa"> 忽略 </a-button>
|
||||
<a-button type="dashed" @click="handleIgnoreBindMfa"> 忽略 </a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -191,20 +139,24 @@ import { checkSystem } from '@/api/install'
|
||||
import { initInstall } from '@/api/install'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { UserOutlined, SolutionOutlined, CopyOutlined } from '@ant-design/icons-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
import { notification } from 'ant-design-vue'
|
||||
import { Modal, notification } from 'ant-design-vue'
|
||||
import userStore from '@/stores/user'
|
||||
import appStore from '@/stores/app'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const loginForm = reactive({
|
||||
userName: '',
|
||||
userPwd: '',
|
||||
})
|
||||
const bindMfaForm = reactive({})
|
||||
const setpCurrent = ref(0)
|
||||
const mfaData = reactive({
|
||||
key: '',
|
||||
url: '',
|
||||
const mfaForm = reactive({
|
||||
twoCode: '',
|
||||
mfa: ''
|
||||
})
|
||||
const setpCurrent = ref(0)
|
||||
|
||||
const canInstall = ref(true)
|
||||
|
||||
const qrCode = reactive({
|
||||
@ -225,49 +177,32 @@ const handleLogin = (values: any) => {
|
||||
message: res.msg,
|
||||
})
|
||||
const tokenData = res.data.tokenData
|
||||
mfaData.key = res.data.mfaKey
|
||||
mfaData.url = res.data.url
|
||||
mfaForm.mfa = res.data.mfaKey
|
||||
setpCurrent.value = 1
|
||||
qrCode.value = res.data.url
|
||||
|
||||
// // 调用 store action 存储当前登录的用户名和 token
|
||||
// this.$store
|
||||
// .dispatch('login', { token: tokenData.token, longTermToken: tokenData.longTermToken })
|
||||
// .then(() => {
|
||||
// // 跳转主页面
|
||||
// // this.$router.push({ path: "/" });
|
||||
// })
|
||||
// const firstWorkspace = tokenData.bindWorkspaceModels[0]
|
||||
// this.$store.dispatch('changeWorkspace', firstWorkspace.id).then(() => { })
|
||||
userStore.login({ token: tokenData.token, longTermToken: tokenData.longTermToken })
|
||||
router.push({ path: '/' })
|
||||
const firstWorkspace = tokenData.bindWorkspaceModels[0]
|
||||
appStore.changeWorkspace(firstWorkspace.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleMfaSure = (e) => {
|
||||
e.preventDefault()
|
||||
this.bindMfaForm.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
const params = {
|
||||
...values,
|
||||
mfa: this.mfaData.key,
|
||||
}
|
||||
|
||||
bindMfa(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
notification.success({
|
||||
message: res.msg,
|
||||
})
|
||||
// 跳转主页面;
|
||||
router.push({ path: '/' })
|
||||
}
|
||||
const handleMfaSure = () => {
|
||||
bindMfa(mfaForm).then((res) => {
|
||||
if (res.code === 200) {
|
||||
notification.success({
|
||||
message: res.msg,
|
||||
})
|
||||
// 跳转主页面;
|
||||
router.push({ path: '/' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 忽略 mfa
|
||||
const ignoreBindMfa = () => {
|
||||
this.$confirm({
|
||||
const handleIgnoreBindMfa = () => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content: '确定要忽略绑定两步验证吗?强烈建议超级管理员开启两步验证来保证账号安全性',
|
||||
okText: '确认',
|
||||
@ -275,60 +210,19 @@ const ignoreBindMfa = () => {
|
||||
onOk: () => {
|
||||
router.push({ path: '/' })
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const goHome = () => {
|
||||
router.replace({ path: '/' })
|
||||
}
|
||||
// /^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? +]).*$/
|
||||
// 验证密码安全强度
|
||||
const checkPasswordStrong = (fieldValue) => {
|
||||
function checkStrong(sPW) {
|
||||
let Modes = 0
|
||||
for (let i = 0; i < sPW.length; i++) {
|
||||
// 测试每一个字符的类别并统计一共有多少种模式.
|
||||
Modes |= CharMode(sPW.charCodeAt(i))
|
||||
}
|
||||
return bitTotal(Modes)
|
||||
}
|
||||
|
||||
//判断字符类型
|
||||
const CharMode = (iN) => {
|
||||
if (iN >= 48 && iN <= 57)
|
||||
//数字
|
||||
return 1
|
||||
if (iN >= 65 && iN <= 90)
|
||||
//大写字母
|
||||
return 2
|
||||
if (iN >= 97 && iN <= 122)
|
||||
//小写
|
||||
return 4
|
||||
else return 8 //特殊字符
|
||||
}
|
||||
|
||||
//统计字符类型
|
||||
const bitTotal = (num) => {
|
||||
var modes = 0
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (num & 1) modes++
|
||||
num >>>= 1
|
||||
}
|
||||
return modes
|
||||
}
|
||||
|
||||
if (!fieldValue || fieldValue == '') {
|
||||
return false
|
||||
}
|
||||
return checkStrong(fieldValue) >= 3
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkSystem().then((res) => {
|
||||
if (res.code === 222) {
|
||||
// canInstall.value = true
|
||||
canInstall.value = true
|
||||
} else {
|
||||
// canInstall.value = false
|
||||
canInstall.value = false
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { CACHE_WORKSPACE_ID } from '@/utils/const'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { getHashQuery } from '@/utils/utils'
|
||||
|
||||
const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
@ -25,10 +25,8 @@ const useAppStore = defineStore('app', {
|
||||
},
|
||||
getters: {
|
||||
getWorkspaceId(state) {
|
||||
const route = useRoute()
|
||||
// let wid = router.app.$route.query.wid
|
||||
let wid = route.query.wid
|
||||
return wid || state.workspaceId
|
||||
const query = getHashQuery()
|
||||
return query.wid || state.workspaceId
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -17,7 +17,8 @@ interface IState {
|
||||
menus: any[]
|
||||
activeMenu: string
|
||||
}
|
||||
export const useMenuStore = defineStore('menu', {
|
||||
|
||||
const useMenuStore = defineStore('menu', {
|
||||
state: (): IState => ({
|
||||
activeTabKey: localStorage.getItem(ACTIVE_TAB_KEY) || '',
|
||||
tabList: localStorage.getItem(TAB_LIST_KEY) ? JSON.parse(localStorage.getItem(TAB_LIST_KEY)!) : [],
|
||||
@ -220,3 +221,5 @@ export const useMenuStore = defineStore('menu', {
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useMenuStore()
|
||||
|
@ -2,9 +2,7 @@ import { TOKEN_KEY, USER_INFO_KEY, MENU_KEY, LONG_TERM_TOKEN } from '@/utils/con
|
||||
|
||||
import { getUserInfo, loginOut } from '@/api/user/user'
|
||||
import { defineStore } from 'pinia'
|
||||
import { useMenuStore } from './menu'
|
||||
|
||||
const menuStore = useMenuStore()
|
||||
import menuStore from './menu'
|
||||
|
||||
const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
@ -34,7 +32,7 @@ const useUserStore = defineStore('user', {
|
||||
})
|
||||
},
|
||||
// 登录 data = {token: 'xxx', userName: 'name'}
|
||||
async reLogin(data: any) {
|
||||
async login(data: any) {
|
||||
this.token = data.token || ''
|
||||
this.longTermToken = data.longTermToken || ''
|
||||
if (this.token) {
|
||||
|
7
web-vue3/src/utils/utils.ts
Normal file
7
web-vue3/src/utils/utils.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export function getHashQuery() {
|
||||
const querys: Record<string, any> = {}
|
||||
location.hash.replace(/[?&]+([^=&]+)=([^&]*)/gi, (_m: string, key: string, value: any) => {
|
||||
querys[key] = value
|
||||
})
|
||||
return querys
|
||||
}
|
Loading…
Reference in New Issue
Block a user