Browse Source

chore: cleanup

main
刘凯 1 year ago
parent
commit
f599b1b9d1
  1. 81
      src/api/base/login.ts
  2. 9
      src/api/base/model/loginModel.ts
  3. 12
      src/api/base/model/menuModel.ts
  4. 5
      src/api/base/model/uploadModel.ts
  5. 38
      src/api/base/model/userModel.ts
  6. 128
      src/api/base/profile.ts
  7. 7
      src/api/base/upload.ts
  8. 2
      src/components/Upload/src/typing.ts
  9. 4
      src/components/Verifition/index.ts
  10. 132
      src/components/Verifition/src/Verify.vue
  11. 256
      src/components/Verifition/src/Verify/VerifyPoints.vue
  12. 364
      src/components/Verifition/src/Verify/VerifySlide.vue
  13. 4
      src/components/Verifition/src/Verify/index.ts
  14. 278
      src/components/Verifition/src/style/verify.css
  15. 15
      src/components/Verifition/src/utils/ase.ts
  16. 93
      src/components/Verifition/src/utils/util.ts
  17. 15
      src/router/routes/index.ts
  18. 69
      src/views/base/profile/AccountBind.vue
  19. 76
      src/views/base/profile/BaseSetting.vue
  20. 49
      src/views/base/profile/PasswordModal.vue
  21. 47
      src/views/base/profile/SecureSetting.vue
  22. 165
      src/views/base/profile/data.ts
  23. 29
      src/views/base/profile/index.vue
  24. 1
      src/views/device-manage/device/DeviceFormModal.vue
  25. 1
      src/views/product/ProductFormModal.vue
  26. 1
      src/views/product/components/ModelAttributeFormModal.vue
  27. 1
      src/views/product/components/ModelServiceFormModal.vue
  28. 1
      src/views/product/components/TopicFormModal.vue
  29. 1
      src/views/subscription/consumer/ConsumerFormModal.vue
  30. 1
      src/views/subscription/list/SubscriptionFormModal.vue
  31. 1
      src/views/system/dept/DeptFormModal.vue
  32. 1
      src/views/system/menu/MenuFormModal.vue
  33. 1
      src/views/system/role/RoleFormModal.vue
  34. 2
      src/views/system/role/RoleMenuModal.vue
  35. 1
      src/views/system/tenant/TenantFormModal.vue
  36. 1
      src/views/system/user/UserFormModal.vue

81
src/api/base/login.ts

@ -1,81 +0,0 @@
import { defHttp } from '@/utils/http/axios'
import { getRefreshToken } from '@/utils/auth'
enum Api {
Login = '/system/auth/login',
RefreshToken = '/system/auth/refresh-token?refreshToken=',
GetTenantIdByName = '/system/tenant/get-id-by-name?name=',
LoginOut = '/system/auth/logout',
GetUserInfo = '/system/auth/get-permission-info',
GetCaptcha = '/system/captcha/get',
CheckCaptcha = '/system/captcha/check',
}
// 刷新访问令牌
export function refreshToken() {
const refreshToken: string = getRefreshToken()
return defHttp.post({ url: Api.RefreshToken + refreshToken })
}
// 登出
export function loginOut() {
return defHttp.delete({ url: Api.LoginOut })
}
// 获取用户权限信息
export function getUserInfo() {
return defHttp.get({ url: Api.GetUserInfo })
}
// 获取登录验证码
export function sendSmsCode(mobile, scene) {
return defHttp.post({
url: '/system/auth/send-sms-code',
data: {
mobile,
scene,
},
})
}
// 获取验证图片 以及token
export function getCaptcha(data) {
return defHttp.post({ url: Api.GetCaptcha, data }, { isReturnNativeResponse: true })
}
// 滑动或者点选验证
export function checkCaptcha(data) {
return defHttp.post({ url: Api.CheckCaptcha, data }, { isReturnNativeResponse: true })
}
// ========== OAUTH 2.0 相关 ==========
export function getAuthorize(clientId) {
return defHttp.get({ url: `/system/oauth2/authorize?clientId=${clientId}` })
}
export function authorize(responseType, clientId, redirectUri, state, autoApprove, checkedScopes, uncheckedScopes) {
// 构建 scopes
const scopes = {}
for (const scope of checkedScopes)
scopes[scope] = true
for (const scope of uncheckedScopes)
scopes[scope] = false
// 发起请求
return defHttp.post({
url: '/system/oauth2/authorize',
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
params: {
response_type: responseType,
client_id: clientId,
redirect_uri: redirectUri,
state,
auto_approve: autoApprove,
scope: JSON.stringify(scopes),
},
})
}

9
src/api/base/model/loginModel.ts

@ -1,9 +0,0 @@
export interface UserLoginVO {
username: string
password: string
captchaVerification: string
}
export interface TentantNameVO {
id: number
}

12
src/api/base/model/menuModel.ts

@ -1,12 +0,0 @@
import type { RouteMeta } from 'vue-router'
export interface RouteItem {
path: string
component: any
meta: RouteMeta
name?: string
alias?: string | string[]
redirect?: string
caseSensitive?: boolean
children?: RouteItem[]
}

5
src/api/base/model/uploadModel.ts

@ -1,5 +0,0 @@
export interface UploadApiResult {
message: string
code: number
url: string
}

38
src/api/base/model/userModel.ts

@ -1,38 +0,0 @@
/**
* @description: Login interface parameters
*/
export interface LoginParams {
username: string
password: string
}
/**
* @description: SmsLogin interface parameters
*/
export interface SmsLoginParams {
mobile: number
code: number
}
/**
* @description: Login interface return value
*/
export interface LoginResultModel {
userId: string | number
accessToken: string
refreshToken: string
expiresTime: number
}
/**
* @description: Get user information return value
*/
export interface GetUserInfoModel {
user: userModel
}
export interface userModel {
id: string | number
avatar: string
nickname: string
}

128
src/api/base/profile.ts

@ -1,128 +0,0 @@
import { ContentTypeEnum } from '@/enums/httpEnum'
import { defHttp } from '@/utils/http/axios'
export interface ProfileDept {
id: number
name: string
}
export interface ProfileRole {
id: number
name: string
}
export interface ProfilePost {
id: number
name: string
}
export interface SocialUser {
id: number
type: number
openid: string
token: string
rawTokenInfo: string
nickname: string
avatar: string
rawUserInfo: string
code: string
state: string
}
export interface ProfileVO {
id: number
username: string
nickname: string
dept: ProfileDept
roles: ProfileRole[]
posts: ProfilePost[]
socialUsers: SocialUser[]
email: string
mobile: string
sex: number
avatar: string
status: number
remark: string
loginIp: string
loginDate: Date
createTime: Date
}
export interface UserProfileUpdateReqVO {
nickname: string
email: string
mobile: string
sex: number
}
enum Api {
getUserProfileApi = '/system/user/profile/get',
putUserProfileApi = '/system/user/profile/update',
uploadAvatarApi = '/system/user/profile/update-avatar',
updateUserPwdApi = '/system/user/profile/update-password',
socialBindApi = '/system/social-user/bind',
socialUnbindApi = '/system/social-user/unbind',
}
/**
* @description: getUserProfileApi
*/
export function getUserProfileApi() {
return defHttp.get({ url: Api.getUserProfileApi })
}
/**
* @description: updateUserProfileApi
*/
export function updateUserProfileApi(data: UserProfileUpdateReqVO) {
return defHttp.put({ url: Api.putUserProfileApi, data })
}
// 用户密码重置
export function updateUserPwdApi(oldPassword: string, newPassword: string) {
return defHttp.put({
url: Api.updateUserPwdApi,
data: {
oldPassword,
newPassword,
},
})
}
// 用户头像上传
export function uploadAvatarApi(data) {
return defHttp.put({
url: Api.uploadAvatarApi,
headers: {
'Content-type': ContentTypeEnum.FORM_DATA,
'ignoreCancelToken': true,
},
data,
})
}
// 社交绑定,使用 code 授权码
export function socialBind(type, code, state) {
return defHttp.post({
url: Api.socialBindApi,
data: {
type,
code,
state,
},
})
}
// 取消社交绑定
export function socialUnbind(type, openid) {
return defHttp.delete({
url: Api.socialUnbindApi,
data: {
type,
openid,
},
})
}
// 社交授权的跳转
export function socialAuthRedirect(type, redirectUri) {
return defHttp.get({
url: `/system/auth/social-auth-redirect?type=${type}&redirectUri=${redirectUri}`,
})
}

7
src/api/base/upload.ts

@ -1,11 +1,16 @@
import type { AxiosProgressEvent } from 'axios'
import type { UploadApiResult } from './model/uploadModel'
import { defHttp } from '@/utils/http/axios'
import type { UploadFileParams } from '@/types/axios'
import { useGlobSetting } from '@/hooks/setting'
const { uploadUrl = '' } = useGlobSetting()
export interface UploadApiResult {
message: string
code: number
url: string
}
/**
* @description: Upload interface
*/

2
src/components/Upload/src/typing.ts

@ -1,5 +1,5 @@
import type { BasicColumn } from '../../Table'
import type { UploadApiResult } from '@/api/base/model/uploadModel'
import type { UploadApiResult } from '@/api/base/upload'
export enum UploadResultStatus {
SUCCESS = 'success',

4
src/components/Verifition/index.ts

@ -1,4 +0,0 @@
import verify from './src/Verify.vue'
import { withInstall } from '@/utils/index'
export const Verify = withInstall(verify)

132
src/components/Verifition/src/Verify.vue

@ -1,132 +0,0 @@
<script type="text/babel">
/**
* Verify 验证码组件
* @description 分发验证码使用
*/
import { computed, ref, toRefs, watchEffect } from 'vue'
import VerifySlide from './Verify/VerifySlide.vue'
import VerifyPoints from './Verify/VerifyPoints.vue'
import { useI18n } from '@/hooks/web/useI18n'
import './style/verify.css'
export default {
name: 'Vue3Verify',
components: {
VerifySlide,
VerifyPoints,
},
props: {
captchaType: {
type: String,
required: true,
},
figure: {
type: Number,
},
arith: {
type: Number,
},
mode: {
type: String,
default: 'pop',
},
vSpace: {
type: Number,
},
explain: {
type: String,
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px',
}
},
},
blockSize: {
type: Object,
},
barSize: {
type: Object,
},
},
setup(props) {
const { t } = useI18n()
const { captchaType, mode } = toRefs(props)
const clickShow = ref(false)
const verifyType = ref(undefined)
const componentType = ref(undefined)
const instance = ref({})
const showBox = computed(() => {
if (mode.value === 'pop')
return clickShow.value
else
return true
})
/**
* refresh
* @description 刷新
*/
const refresh = () => {
if (instance.value.refresh)
instance.value.refresh()
}
const closeBox = () => {
clickShow.value = false
refresh()
}
const show = () => {
if (mode.value === 'pop')
clickShow.value = true
}
watchEffect(() => {
switch (captchaType.value) {
case 'blockPuzzle':
verifyType.value = '2'
componentType.value = 'VerifySlide'
break
case 'clickWord':
verifyType.value = ''
componentType.value = 'VerifyPoints'
break
}
})
return {
t,
clickShow,
verifyType,
componentType,
instance,
showBox,
closeBox,
show,
}
},
}
</script>
<template>
<div v-show="showBox" :class="mode === 'pop' ? 'mask' : ''">
<div :class="mode === 'pop' ? 'verifybox' : ''" :style="{ 'max-width': `${parseInt(imgSize.width) + 20}px` }">
<div v-if="mode === 'pop'" class="verifybox-top">
{{ t('component.captcha.verification') }}
<span class="verifybox-close" @click="closeBox">
<i class="iconfont icon-close" />
</span>
</div>
<div class="verifybox-bottom" :style="{ padding: mode === 'pop' ? '10px' : '0' }">
<!-- 验证码容器 -->
<component
:is="componentType" v-if="componentType" ref="instance" :captcha-type="captchaType" :type="verifyType"
:figure="figure" :arith="arith" :mode="mode" :spaces="vSpace" :explain="explain" :img-size="imgSize"
:block-size="blockSize" :bar-size="barSize"
/>
</div>
</div>
</div>
</template>

256
src/components/Verifition/src/Verify/VerifyPoints.vue

@ -1,256 +0,0 @@
<script type="text/babel" setup>
/**
* VerifyPoints
* @description 点选
*/
import { getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs } from 'vue'
import { resetSize } from './../utils/util'
import { aesEncrypt } from './../utils/ase'
import { checkCaptcha, getCaptcha } from '@/api/base/login'
import { useI18n } from '@/hooks/web/useI18n'
const props = defineProps({
// popfixed
mode: {
type: String,
default: 'fixed',
},
captchaType: {
type: String,
},
//
spaces: {
type: Number,
default: 5,
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px',
}
},
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px',
}
},
},
})
const { t } = useI18n()
const { mode, captchaType } = toRefs(props)
const { proxy } = getCurrentInstance()
const secretKey = ref('') // ase
const checkNum = ref(3) //
const fontPos = reactive([]) //
const checkPosArr = reactive([]) //
const num = ref(1) //
const pointBackImgBase = ref('') //
const poinTextList = reactive([]) //
const backToken = ref('') // token
const setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0,
})
const tempPoints = reactive([])
const text = ref('')
const barAreaColor = ref(undefined)
const barAreaBorderColor = ref(undefined)
const showRefresh = ref(true)
const bindingClick = ref(true)
function init() {
//
fontPos.splice(0, fontPos.length)
checkPosArr.splice(0, checkPosArr.length)
num.value = 1
getPictrue()
nextTick(() => {
const { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
proxy.$parent.$emit('ready', proxy)
})
}
onMounted(() => {
//
init()
proxy.$el.onselectstart = function () {
return false
}
})
const canvas = ref(null)
//
const getMousePos = function (obj, e) {
const x = e.offsetX
const y = e.offsetY
return { x, y }
}
//
const createPoint = function (pos) {
tempPoints.push(Object.assign({}, pos))
return num.value + 1
}
//
const pointTransfrom = function (pointArr, imgSize) {
const newPointArr = pointArr.map((p) => {
const x = Math.round((310 * p.x) / Number.parseInt(imgSize.imgWidth))
const y = Math.round((155 * p.y) / Number.parseInt(imgSize.imgHeight))
return { x, y }
})
return newPointArr
}
const refresh = async function () {
tempPoints.splice(0, tempPoints.length)
barAreaColor.value = '#000'
barAreaBorderColor.value = '#ddd'
bindingClick.value = true
fontPos.splice(0, fontPos.length)
checkPosArr.splice(0, checkPosArr.length)
num.value = 1
await getPictrue()
showRefresh.value = true
}
function canvasClick(e) {
checkPosArr.push(getMousePos(canvas, e))
if (num.value === checkNum.value) {
num.value = createPoint(getMousePos(canvas, e))
//
const arr = pointTransfrom(checkPosArr, setSize)
checkPosArr.length = 0
checkPosArr.push(...arr)
//
setTimeout(() => {
// var flag = this.comparePos(this.fontPos, this.checkPosArr);
//
const captchaVerification = secretKey.value
? aesEncrypt(`${backToken.value}---${JSON.stringify(checkPosArr)}`, secretKey.value)
: `${backToken.value}---${JSON.stringify(checkPosArr)}`
const data = {
captchaType: captchaType.value,
pointJson: secretKey.value ? aesEncrypt(JSON.stringify(checkPosArr), secretKey.value) : JSON.stringify(checkPosArr),
token: backToken.value,
}
checkCaptcha(data).then((response) => {
const res = response.data
if (res.repCode === '0000') {
barAreaColor.value = '#4cae4c'
barAreaBorderColor.value = '#5cb85c'
text.value = t('component.captcha.success')
bindingClick.value = false
if (mode.value === 'pop') {
setTimeout(() => {
proxy.$parent.clickShow = false
refresh()
}, 1500)
}
proxy.$parent.$emit('success', { captchaVerification })
}
else {
proxy.$parent.$emit('error', proxy)
barAreaColor.value = '#d9534f'
barAreaBorderColor.value = '#d9534f'
text.value = t('component.captcha.fail')
setTimeout(() => {
refresh()
}, 700)
}
})
}, 400)
}
if (num.value < checkNum.value)
num.value = createPoint(getMousePos(canvas, e))
}
//
async function getPictrue() {
const data = {
captchaType: captchaType.value,
}
const res = await getCaptcha(data)
if (res.data.repCode === '0000') {
pointBackImgBase.value = res.data.repData.originalImageBase64
backToken.value = res.data.repData.token
secretKey.value = res.data.repData.secretKey
poinTextList.value = res.data.repData.wordList
text.value = `${t('component.captcha.point')}${poinTextList.value.join(',')}`
}
else {
text.value = res.data.repMsg
}
}
</script>
<template>
<div style="position: relative">
<div class="verify-img-out">
<div
class="verify-img-panel"
:style="{
'width': setSize.imgWidth,
'height': setSize.imgHeight,
'background-size': `${setSize.imgWidth} ${setSize.imgHeight}`,
'margin-bottom': `${spaces}px`,
}"
>
<div v-show="showRefresh" class="verify-refresh" style="z-index: 3" @click="refresh">
<i class="iconfont icon-refresh" />
</div>
<img
ref="canvas"
:src="`data:image/png;base64,${pointBackImgBase}`"
alt=""
style=" display: block;width: 100%; height: 100%"
@click="bindingClick ? canvasClick($event) : undefined"
>
<div
v-for="(tempPoint, index) in tempPoints"
:key="index"
class="point-area"
:style="{
'background-color': '#1abd6c',
'color': '#fff',
'z-index': 9999,
'width': '20px',
'height': '20px',
'text-align': 'center',
'line-height': '20px',
'border-radius': '50%',
'position': 'absolute',
'top': `${parseInt(tempPoint.y - 10)}px`,
'left': `${parseInt(tempPoint.x - 10)}px`,
}"
>
{{ index + 1 }}
</div>
</div>
</div>
<!-- 'height': this.barSize.height, -->
<div
class="verify-bar-area"
:style="{
'width': setSize.imgWidth,
'color': barAreaColor,
'border-color': barAreaBorderColor,
'line-height': barSize.height,
}"
>
<span class="verify-msg">{{ text }}</span>
</div>
</div>
</template>

364
src/components/Verifition/src/Verify/VerifySlide.vue

@ -1,364 +0,0 @@
<script type="text/babel" setup>
/**
* VerifySlide
* @description 滑块
*/
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue'
import { aesEncrypt } from './../utils/ase'
import { resetSize } from './../utils/util'
import { checkCaptcha, getCaptcha } from '@/api/base/login'
import { useI18n } from '@/hooks/web/useI18n'
const props = defineProps({
captchaType: {
type: String,
},
type: {
type: String,
default: '1',
},
// popfixed
mode: {
type: String,
default: 'fixed',
},
spaces: {
type: Number,
default: 5,
},
explain: {
type: String,
default: '',
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px',
}
},
},
blockSize: {
type: Object,
default() {
return {
width: '50px',
height: '50px',
}
},
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '30px',
}
},
},
})
const { t } = useI18n()
const { mode, captchaType, type, blockSize, explain } = toRefs(props)
const { proxy } = getCurrentInstance()
const secretKey = ref('') // ase
const passFlag = ref('') //
const backImgBase = ref('') //
const blockBackImgBase = ref('') //
const backToken = ref('') // token
const startMoveTime = ref('') //
const endMovetime = ref('') //
const tipWords = ref('')
const text = ref('')
const finishText = ref('')
const setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0,
})
const moveBlockLeft = ref(undefined)
const leftBarWidth = ref(undefined)
//
const moveBlockBackgroundColor = ref(undefined)
const leftBarBorderColor = ref('#ddd')
const iconColor = ref(undefined)
const iconClass = ref('icon-right')
const status = ref(false) //
const isEnd = ref(false) //
const showRefresh = ref(true)
const transitionLeft = ref('')
const transitionWidth = ref('')
const startLeft = ref(0)
const barArea = computed(() => {
return proxy.$el.querySelector('.verify-bar-area')
})
function init() {
if (explain.value === '')
text.value = t('component.captcha.slide')
else
text.value = explain.value
getPictrue()
nextTick(() => {
const { imgHeight, imgWidth, barHeight, barWidth } = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
proxy.$parent.$emit('ready', proxy)
})
window.removeEventListener('touchmove', (e) => {
move(e)
})
window.removeEventListener('mousemove', (e) => {
move(e)
})
//
window.removeEventListener('touchend', () => {
end()
})
window.removeEventListener('mouseup', () => {
end()
})
window.addEventListener('touchmove', (e) => {
move(e)
})
window.addEventListener('mousemove', (e) => {
move(e)
})
//
window.addEventListener('touchend', () => {
end()
})
window.addEventListener('mouseup', () => {
end()
})
}
watch(type, () => {
init()
})
onMounted(() => {
//
init()
proxy.$el.onselectstart = function () {
return false
}
})
//
function start(e) {
e = e || window.event
let x
if (!e.touches) {
// PC
x = e.clientX
}
else {
//
x = e.touches[0].pageX
}
startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left)
startMoveTime.value = +new Date() //
if (isEnd.value === false) {
text.value = ''
moveBlockBackgroundColor.value = '#337ab7'
leftBarBorderColor.value = '#337AB7'
iconColor.value = '#fff'
e.stopPropagation()
status.value = true
}
}
//
function move(e) {
e = e || window.event
let x
if (status.value && isEnd.value === false) {
if (!e.touches) {
// PC
x = e.clientX
}
else {
//
x = e.touches[0].pageX
}
const bar_area_left = barArea.value.getBoundingClientRect().left
let move_block_left = x - bar_area_left // left
if (move_block_left >= barArea.value.offsetWidth - Number.parseInt(Number.parseInt(blockSize.value.width) / 2) - 2)
move_block_left = barArea.value.offsetWidth - Number.parseInt(Number.parseInt(blockSize.value.width) / 2) - 2
if (move_block_left <= 0)
move_block_left = Number.parseInt(Number.parseInt(blockSize.value.width) / 2)
// left
moveBlockLeft.value = `${move_block_left - startLeft.value}px`
leftBarWidth.value = `${move_block_left - startLeft.value}px`
}
}
//
function end() {
endMovetime.value = +new Date()
//
if (status.value && isEnd.value === false) {
let moveLeftDistance = Number.parseInt((moveBlockLeft.value || '').replace('px', ''))
moveLeftDistance = (moveLeftDistance * 310) / Number.parseInt(setSize.imgWidth)
const data = {
captchaType: captchaType.value,
pointJson: secretKey.value
? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), secretKey.value)
: JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
token: backToken.value,
}
checkCaptcha(data).then((response) => {
const res = response.data
if (res.repCode === '0000') {
moveBlockBackgroundColor.value = '#5cb85c'
leftBarBorderColor.value = '#5cb85c'
iconColor.value = '#fff'
iconClass.value = 'icon-check'
showRefresh.value = false
isEnd.value = true
if (mode.value === 'pop') {
setTimeout(() => {
proxy.$parent.clickShow = false
refresh()
}, 1500)
}
passFlag.value = true
tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s
${t('component.captcha.success')}`
const captchaVerification = secretKey.value
? aesEncrypt(`${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`, secretKey.value)
: `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5.0 })}`
setTimeout(() => {
tipWords.value = ''
proxy.$parent.closeBox()
proxy.$parent.$emit('success', { captchaVerification })
}, 1000)
}
else {
moveBlockBackgroundColor.value = '#d9534f'
leftBarBorderColor.value = '#d9534f'
iconColor.value = '#fff'
iconClass.value = 'icon-close'
passFlag.value = false
setTimeout(() => {
refresh()
}, 1000)
proxy.$parent.$emit('error', proxy)
tipWords.value = t('component.captcha.fail')
setTimeout(() => {
tipWords.value = ''
}, 1000)
}
})
status.value = false
}
}
async function refresh() {
showRefresh.value = true
finishText.value = ''
transitionLeft.value = 'left .3s'
moveBlockLeft.value = 0
leftBarWidth.value = undefined
transitionWidth.value = 'width .3s'
leftBarBorderColor.value = '#ddd'
moveBlockBackgroundColor.value = '#fff'
iconColor.value = '#000'
iconClass.value = 'icon-right'
isEnd.value = false
await getPictrue()
setTimeout(() => {
transitionWidth.value = ''
transitionLeft.value = ''
text.value = explain.value
}, 300)
}
//
async function getPictrue() {
const data = {
captchaType: captchaType.value,
}
const res = await getCaptcha(data)
if (res.data.repCode === '0000') {
backImgBase.value = res.data.repData.originalImageBase64
blockBackImgBase.value = `data:image/png;base64,${res.data.repData.jigsawImageBase64}`
backToken.value = res.data.repData.token
secretKey.value = res.data.repData.secretKey
}
else {
tipWords.value = res.data.repMsg
}
}
</script>
<template>
<div style="position: relative">
<div v-if="type === '2'" class="verify-img-out" :style="{ height: `${parseInt(setSize.imgHeight) + spaces}px` }">
<div class="verify-img-panel" :style="{ width: setSize.imgWidth, height: setSize.imgHeight }">
<img :src="`data:image/png;base64,${backImgBase}`" alt="" style=" display: block;width: 100%; height: 100%">
<div v-show="showRefresh" class="verify-refresh" @click="refresh">
<i class="iconfont icon-refresh" />
</div>
<transition name="tips">
<span v-if="tipWords" class="verify-tips" :class="passFlag ? 'suc-bg' : 'err-bg'">
{{ tipWords }}
</span>
</transition>
</div>
</div>
<!-- 公共部分 -->
<div class="verify-bar-area" :style="{ 'width': setSize.imgWidth, 'height': barSize.height, 'line-height': barSize.height }">
<span class="verify-msg" v-text="text" />
<div
class="verify-left-bar"
:style="{
'width': leftBarWidth !== undefined ? leftBarWidth : barSize.height,
'height': barSize.height,
'border-color': leftBarBorderColor,
'transaction': transitionWidth,
}"
>
<span class="verify-msg" v-text="finishText" />
<div
class="verify-move-block"
:style="{
'width': barSize.height,
'height': barSize.height,
'background-color': moveBlockBackgroundColor,
'left': moveBlockLeft,
'transition': transitionLeft,
}"
@touchstart="start"
@mousedown="start"
>
<i class="iconfont verify-icon" :class="[iconClass]" :style="{ color: iconColor }" />
<div
v-if="type === '2'"
class="verify-sub-block"
:style="{
'width': `${Math.floor((parseInt(setSize.imgWidth) * 47) / 310)}px`,
'height': setSize.imgHeight,
'top': `-${parseInt(setSize.imgHeight) + spaces}px`,
'background-size': `${setSize.imgWidth} ${setSize.imgHeight}`,
}"
>
<img :src="blockBackImgBase" alt="" style=" display: block;width: 100%; height: 100%; -webkit-user-drag: none">
</div>
</div>
</div>
</div>
</div>
</template>

4
src/components/Verifition/src/Verify/index.ts

@ -1,4 +0,0 @@
import VerifySlide from './VerifySlide.vue'
import VerifyPoints from './VerifyPoints.vue'
export { VerifySlide, VerifyPoints }

278
src/components/Verifition/src/style/verify.css

File diff suppressed because one or more lines are too long

15
src/components/Verifition/src/utils/ase.ts

@ -1,15 +0,0 @@
import CryptoJS from 'crypto-js'
/**
* @word
* @keyWord String
*/
export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') {
const key = CryptoJS.enc.Utf8.parse(keyWord)
const srcs = CryptoJS.enc.Utf8.parse(word)
const encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
})
return encrypted.toString()
}

93
src/components/Verifition/src/utils/util.ts

@ -1,93 +0,0 @@
export function resetSize(vm) {
let img_width, img_height, bar_width, bar_height // 图片的宽度、高度,移动条的宽度、高度
const EmployeeWindow = window as any
const parentWidth = vm.$el.parentNode.offsetWidth || EmployeeWindow.offsetWidth
const parentHeight = vm.$el.parentNode.offsetHeight || EmployeeWindow.offsetHeight
if (vm.imgSize.width.includes('%'))
img_width = `${(Number.parseInt(vm.imgSize.width) / 100) * parentWidth}px`
else
img_width = vm.imgSize.width
if (vm.imgSize.height.includes('%'))
img_height = `${(Number.parseInt(vm.imgSize.height) / 100) * parentHeight}px`
else
img_height = vm.imgSize.height
if (vm.barSize.width.includes('%'))
bar_width = `${(Number.parseInt(vm.barSize.width) / 100) * parentWidth}px`
else
bar_width = vm.barSize.width
if (vm.barSize.height.includes('%'))
bar_height = `${(Number.parseInt(vm.barSize.height) / 100) * parentHeight}px`
else
bar_height = vm.barSize.height
return { imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height }
}
export const _code_chars = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
]
export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']
export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']

15
src/router/routes/index.ts

@ -46,20 +46,7 @@ export const ProfileRoute: AppRouteRecordRaw = {
title: t('routes.basic.profile'),
hidden: true,
},
children: [
{
path: 'index',
component: () => import('@/views/base/profile/index.vue'),
name: 'UserProfile',
meta: {
canTo: true,
hidden: true,
noTagsView: false,
icon: 'i-ant-design:user-outlined',
title: t('routes.basic.profile'),
},
},
],
children: [],
}
// Basic routing without permission

69
src/views/base/profile/AccountBind.vue

@ -1,69 +0,0 @@
<script lang="ts" setup>
import { List } from 'ant-design-vue'
import { onMounted } from 'vue'
import type { ListItem } from './data'
import { CollapseContainer } from '@/components/Container/index'
import { getUserProfileApi } from '@/api/base/profile'
const accountBindList: ListItem[] = [
{
key: '20',
title: '钉钉',
description: '当前未绑定钉钉账号',
extra: '绑定',
avatar: 'i-ri:dingding-fill',
color: '#2eabff',
},
{
key: '30',
title: '企业微信',
description: '当前未绑定企业微信',
extra: '绑定',
avatar: 'i-ri:wechat-line',
color: '#2eabff',
},
]
async function init() {
const userInfo = await getUserProfileApi()
// TODO
for (const i in accountBindList) {
if (userInfo.socialUsers) {
for (const j in userInfo.socialUsers) {
if (accountBindList[i].key === userInfo.socialUsers[j].type) {
accountBindList[i].title = '已绑定'
break
}
}
}
}
}
onMounted(async () => {
await init()
})
</script>
<template>
<CollapseContainer title="账号绑定" :can-expan="false">
<List>
<template v-for="item in accountBindList" :key="item.key">
<List.Item>
<List.Item.Meta>
<template #avatar>
<span v-if="item.avatar" class="text-4xl" :class="item.avatar" :style="{ color: item.color }" />
</template>
<template #title>
{{ item.title }}
<a-button v-if="item.extra" type="link" size="small" class="float-right mr-7.5 mt-2.5 cursor-pointer">
{{ item.extra }}
</a-button>
</template>
<template #description>
<div>{{ item.description }}</div>
</template>
</List.Item.Meta>
</List.Item>
</template>
</List>
</CollapseContainer>
</template>

76
src/views/base/profile/BaseSetting.vue

@ -1,76 +0,0 @@
<script lang="ts" setup>
import { Button, Col, Row } from 'ant-design-vue'
import { computed, onMounted } from 'vue'
import { baseSetschemas } from './data'
import { BasicForm, useForm } from '@/components/Form/index'
import { CollapseContainer } from '@/components/Container'
import { CropperAvatar } from '@/components/Cropper'
import { useMessage } from '@/hooks/web/useMessage'
import headerImg from '@/assets/images/header.jpg'
import { useUserStore } from '@/store/modules/user'
import { getUserProfileApi, updateUserProfileApi, uploadAvatarApi } from '@/api/base/profile'
const { createMessage } = useMessage()
const userStore = useUserStore()
const [register, { setFieldsValue, validate }] = useForm({
labelWidth: 120,
schemas: baseSetschemas,
showActionButtonGroup: false,
})
onMounted(async () => {
const data = await getUserProfileApi()
setFieldsValue(data)
})
const avatar = computed(() => {
const { avatar } = userStore.getUserInfo.user
return avatar || headerImg
})
async function updateAvatar({ data }) {
const res = await uploadAvatarApi({ avatarFile: data })
const userinfo = userStore.getUserInfo
userinfo.user.avatar = res
userStore.setUserInfo(userinfo)
}
async function handleSubmit() {
try {
const values = await validate()
await updateUserProfileApi(values as any)
}
finally {
createMessage.success('更新成功!')
}
}
</script>
<template>
<CollapseContainer title="基本设置" :can-expan="false">
<Row :gutter="24">
<Col :span="14">
<BasicForm @register="register" />
</Col>
<Col :span="10">
<div>
<div class="mb-2">
头像
</div>
<CropperAvatar
:value="avatar"
btn-text="更换头像"
:btn-props="{ preIcon: 'i-ant-design:cloud-upload-outlined' }"
width="150"
class="mb-4 block rounded-full"
@change="updateAvatar"
/>
</div>
</Col>
</Row>
<Button type="primary" @click="handleSubmit">
更新基本信息
</Button>
</CollapseContainer>
</template>

49
src/views/base/profile/PasswordModal.vue

@ -1,49 +0,0 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { passwordSchema } from './data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { updateUserPwdApi } from '@/api/base/profile'
defineOptions({ name: 'PasswordModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const title = ref('修改密码')
const [registerForm, { resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: passwordSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(() => {
resetFields()
setModalProps({ confirmLoading: false })
})
async function handleSubmit() {
try {
const values = await validate()
await updateUserPwdApi(values.oldPassword, values.newPassword)
setModalProps({ confirmLoading: true })
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="title" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

47
src/views/base/profile/SecureSetting.vue

@ -1,47 +0,0 @@
<script lang="ts" setup>
import { List } from 'ant-design-vue'
import { secureSettingList } from './data'
import PasswordModal from './PasswordModal.vue'
import { CollapseContainer } from '@/components/Container/index'
import { useModal } from '@/components/Modal'
import { useMessage } from '@/hooks/web/useMessage'
const ListItem = List.Item
const ListItemMeta = List.Item.Meta
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
function handleEdit(title: string) {
if (title === '账户密码')
openModal(true, {})
}
function handleSuccess() {
createMessage.success('更新成功!')
}
</script>
<template>
<CollapseContainer title="安全设置" :can-expan="false">
<List>
<template v-for="item in secureSettingList" :key="item.key">
<ListItem>
<ListItemMeta>
<template #title>
{{ item.title }}
<div v-if="item.extra" class="float-right mr-7.5 mt-2.5 cursor-pointer text-blue-500 font-normal">
<a-button type="link" @click="handleEdit(item.title)">
{{ item.extra }}
</a-button>
</div>
</template>
<template #description>
<div>{{ item.description }}</div>
</template>
</ListItemMeta>
</ListItem>
</template>
</List>
</CollapseContainer>
<PasswordModal @register="registerModal" @success="handleSuccess" />
</template>

165
src/views/base/profile/data.ts

@ -1,165 +0,0 @@
/* eslint-disable prefer-promise-reject-errors */
import type { FormSchema } from '@/components/Form'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
export interface ListItem {
key: string
title: string
description: string
extra?: string
avatar?: string
color?: string
}
// tab的list
export const settingList = [
{
key: '1',
name: '基本设置',
component: 'BaseSetting',
},
{
key: '2',
name: '安全设置',
component: 'SecureSetting',
},
{
key: '3',
name: '账号绑定',
component: 'AccountBind',
},
]
// 基础设置 form
export const baseSetschemas: FormSchema[] = [
{
field: 'nickname',
component: 'Input',
label: t('profile.user.nickname'),
colProps: { span: 18 },
},
{
field: 'mobile',
component: 'Input',
label: t('profile.user.mobile'),
colProps: { span: 18 },
},
{
field: 'email',
component: 'Input',
label: t('profile.user.email'),
colProps: { span: 18 },
},
{
field: 'sex',
component: 'RadioGroup',
componentProps: {
options: [
{ label: '男', value: 1 },
{ label: '女', value: 2 },
],
},
label: t('profile.user.sex'),
colProps: { span: 18 },
},
]
// 安全设置 list
export const secureSettingList: ListItem[] = [
{
key: '1',
title: '账户密码',
description: '当前密码强度::强',
extra: '修改',
},
{
key: '2',
title: '密保手机',
description: '已绑定手机::138****8293',
extra: '修改',
},
{
key: '3',
title: '密保问题',
description: '未设置密保问题,密保问题可有效保护账户安全',
extra: '修改',
},
{
key: '4',
title: '备用邮箱',
description: '已绑定邮箱::ant***sign.com',
extra: '修改',
},
{
key: '5',
title: 'MFA 设备',
description: '未绑定 MFA 设备,绑定后,可以进行二次确认',
extra: '修改',
},
]
// 新消息通知 list
export const msgNotifyList: ListItem[] = [
{
key: '1',
title: '账户密码',
description: '其他用户的消息将以站内信的形式通知',
},
{
key: '2',
title: '系统消息',
description: '系统消息将以站内信的形式通知',
},
{
key: '3',
title: '待办任务',
description: '待办任务将以站内信的形式通知',
},
]
export const passwordSchema: FormSchema[] = [
{
field: 'oldPassword',
label: '当前密码',
component: 'InputPassword',
required: true,
},
{
field: 'newPassword',
label: '新密码',
component: 'StrengthMeter',
componentProps: {
placeholder: '新密码',
},
rules: [
{
required: true,
message: '请输入新密码',
},
],
},
{
field: 'confirmPassword',
label: '确认密码',
component: 'InputPassword',
dynamicRules: ({ values }) => {
return [
{
required: true,
validator: (_, value) => {
if (!value)
return Promise.reject('密码不能为空')
if (value !== values.newPassword)
return Promise.reject('两次输入的密码不一致!')
return Promise.resolve()
},
},
]
},
},
]

29
src/views/base/profile/index.vue

@ -1,29 +0,0 @@
<script lang="ts" setup>
import { TabPane, Tabs } from 'ant-design-vue'
import { ref } from 'vue'
import { settingList } from './data'
import BaseSetting from './BaseSetting.vue'
import SecureSetting from './SecureSetting.vue'
import AccountBind from './AccountBind.vue'
import { ScrollContainer } from '@/components/Container/index'
const wrapperRef = ref(null)
const tabBarStyle = { width: '220px' }
</script>
<template>
<ScrollContainer>
<div ref="wrapperRef" class="m-3 rounded-1.5 bg-[var(--component-background)]">
<Tabs tab-position="left" :tab-bar-style="tabBarStyle">
<template v-for="item in settingList" :key="item.key">
<TabPane :tab="item.name">
<BaseSetting v-if="item.component === 'BaseSetting'" />
<SecureSetting v-if="item.component === 'SecureSetting'" />
<AccountBind v-if="item.component === 'AccountBind'" />
</TabPane>
</template>
</Tabs>
</div>
</ScrollContainer>
</template>

1
src/views/device-manage/device/DeviceFormModal.vue

@ -43,7 +43,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
width="30%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/product/ProductFormModal.vue

@ -43,7 +43,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
width="30%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/product/components/ModelAttributeFormModal.vue

@ -177,7 +177,6 @@ function validatorValueScope(value1: number, value2: number) {
<template>
<BasicModal
v-bind="$attrs"
width="40%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/product/components/ModelServiceFormModal.vue

@ -67,7 +67,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
width="30%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/product/components/TopicFormModal.vue

@ -81,7 +81,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
width="30%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/subscription/consumer/ConsumerFormModal.vue

@ -53,7 +53,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
width="30%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/subscription/list/SubscriptionFormModal.vue

@ -47,7 +47,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
width="30%"
:min-height="100"
:title="isUpdate ? '编辑' : '新增'"

1
src/views/system/dept/DeptFormModal.vue

@ -49,7 +49,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
:title="isUpdate ? t('action.edit') : t('action.create')"
:after-close="() => isUpdate = false"
@register="registerModal"

1
src/views/system/menu/MenuFormModal.vue

@ -45,7 +45,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
:title="isUpdate ? '编辑' : '新建'"
:after-close="() => isUpdate = false"
@register="registerModal"

1
src/views/system/role/RoleFormModal.vue

@ -46,7 +46,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
:title="isUpdate ? '编辑' : '新增'"
:after-close="() => isUpdate = false"
@register="registerModal"

2
src/views/system/role/RoleMenuModal.vue

@ -40,7 +40,7 @@ function handleSubmit() {
</script>
<template>
<BasicModal v-bind="$attrs" title="菜单权限配置" width="20%" @register="registerModal" @ok="handleSubmit">
<BasicModal title="菜单权限配置" width="20%" @register="registerModal" @ok="handleSubmit">
<BasicTree
v-if="state.length"
v-model:value="checkedIds"

1
src/views/system/tenant/TenantFormModal.vue

@ -42,7 +42,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
:title="isUpdate ? '编辑' : '新增'"
:after-close="() => isUpdate = false"
@register="registerModal"

1
src/views/system/user/UserFormModal.vue

@ -46,7 +46,6 @@ async function handleSubmit() {
<template>
<BasicModal
v-bind="$attrs"
:title="isUpdate ? t('action.edit') : t('action.create')"
:after-close="() => isUpdate = false"
@register="registerModal"

Loading…
Cancel
Save