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:
bwcx_jzy 2023-04-09 14:48:03 +08:00
commit 9fc83dfda6
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
7 changed files with 88 additions and 186 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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
}
})
})

View File

@ -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
},
},
})

View File

@ -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()

View File

@ -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) {

View 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
}