mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-03-22 02:37:16 +08:00
【升级】开源版前端升级v3.5.0
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
"@antv/g2plot": "2.4.32",
|
||||
"@chenfengyuan/vue-qrcode": "2.0.0",
|
||||
"@highlightjs/vue-plugin": "2.1.0",
|
||||
"@kangc/v-md-editor": "2.3.18",
|
||||
"@tinymce/tinymce-vue": "5.1.1",
|
||||
"@vue-office/docx": "1.6.2",
|
||||
"@vue-office/excel": "1.7.11",
|
||||
|
||||
@@ -45,5 +45,21 @@ export default {
|
||||
// 获取系统基础配置
|
||||
configSysBaseList(data) {
|
||||
return request('sysBaseList', data, 'get')
|
||||
},
|
||||
// 获取机构树
|
||||
configOrgTree(data) {
|
||||
return request('orgTree', data, 'get')
|
||||
},
|
||||
// 获取机构选择器
|
||||
configOrgSelector(data) {
|
||||
return request('orgSelector', data, 'get')
|
||||
},
|
||||
// 获取角色选择器
|
||||
configRoleSelector(data) {
|
||||
return request('roleSelector', data, 'get')
|
||||
},
|
||||
// 获取职位选择器
|
||||
configPositionSelector(data) {
|
||||
return request('positionSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,5 +83,9 @@ export default {
|
||||
// 删除文件
|
||||
fileDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 物理删除文件
|
||||
fileDeleteAbsolute(data) {
|
||||
return request('deleteAbsolute', data)
|
||||
}
|
||||
}
|
||||
|
||||
68
snowy-admin-web/src/api/dev/pushApi.js
Normal file
68
snowy-admin-web/src/api/dev/pushApi.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/dev/push/` + url, ...arg)
|
||||
/**
|
||||
* 消息推送
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取消息推送分页
|
||||
pushPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 删除消息推送
|
||||
pushDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取消息推送详情
|
||||
pushDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 动态推送消息
|
||||
pushDynamicText(data) {
|
||||
return request('pushDynamicText', data)
|
||||
},
|
||||
|
||||
// 推送飞书TEXT消息
|
||||
pushFeiShuText(data) {
|
||||
return request('pushFeiShuText', data)
|
||||
},
|
||||
|
||||
// 推送钉钉TEXT消息
|
||||
pushDingTalkText(data) {
|
||||
return request('pushDingTalkText', data)
|
||||
},
|
||||
// 推送消息——钉钉MARKDOWN
|
||||
pushDingTalkMarkdown(data) {
|
||||
return request('pushDingTalkMarkdown', data)
|
||||
},
|
||||
// 推送消息——钉钉LINK
|
||||
pushDingTalkLink(data) {
|
||||
return request('pushDingTalkLink', data)
|
||||
},
|
||||
|
||||
// 推送消息——企业微信TXT
|
||||
pushWorkWechatText(data) {
|
||||
return request('pushWorkWechatText', data)
|
||||
},
|
||||
// 推送消息——企业微信MARKDOWN
|
||||
pushWorkWechatMarkdown(data) {
|
||||
return request('pushWorkWechatMarkdown', data)
|
||||
},
|
||||
// 推送消息——企业微信NEWS
|
||||
pushWorkWechatNews(data) {
|
||||
return request('pushWorkWechatNews', data)
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,49 @@ export default {
|
||||
userFindPasswordByEmail(data) {
|
||||
return request('findPasswordByEmail', data)
|
||||
},
|
||||
// 修改用户密码
|
||||
userUpdatePassword(data) {
|
||||
return request('updatePassword', data)
|
||||
// 修改密码获取手机验证码
|
||||
userUpdatePasswordGetPhoneValidCode(data) {
|
||||
return request('updatePasswordGetPhoneValidCode', data, 'get')
|
||||
},
|
||||
// 修改密码获取邮箱验证码
|
||||
userUpdatePasswordGetEmailValidCode(data) {
|
||||
return request('updatePasswordGetEmailValidCode', data, 'get')
|
||||
},
|
||||
// 通过验证旧密码修改用户密码
|
||||
userUpdatePasswordByOld(data) {
|
||||
return request('updatePasswordByOld', data)
|
||||
},
|
||||
// 通过验证手机号修改用户密码
|
||||
userUpdatePasswordByPhone(data) {
|
||||
return request('updatePasswordByPhone', data)
|
||||
},
|
||||
// 通过验证邮箱修改用户密码
|
||||
userUpdatePasswordByEmail(data) {
|
||||
return request('updatePasswordByEmail', data)
|
||||
},
|
||||
// 绑定手机号获取手机验证码
|
||||
userBindPhoneGetPhoneValidCode(data) {
|
||||
return request('bindPhoneGetPhoneValidCode', data)
|
||||
},
|
||||
// 修改绑定手机号获取手机验证码
|
||||
userUpdateBindPhoneGetPhoneValidCode(data) {
|
||||
return request('updateBindPhoneGetPhoneValidCode', data)
|
||||
},
|
||||
// 修改绑定手机号获取手机验证码
|
||||
userBindPhone(data) {
|
||||
return request('bindPhone', data)
|
||||
},
|
||||
// 绑定邮箱获取邮箱验证码
|
||||
userBindEmailGetEmailValidCode(data) {
|
||||
return request('bindEmailGetEmailValidCode', data)
|
||||
},
|
||||
// 修改绑定邮箱获取邮箱验证码
|
||||
userUpdateBindEmailGetEmailValidCode(data) {
|
||||
return request('updateBindEmailGetEmailValidCode', data)
|
||||
},
|
||||
// 绑定邮箱
|
||||
userBindEmail(data) {
|
||||
return request('bindEmail', data)
|
||||
},
|
||||
// 修改用户头像
|
||||
userUpdateAvatar(data) {
|
||||
@@ -104,6 +144,22 @@ export default {
|
||||
},
|
||||
// 根据id获取头像
|
||||
userCenterGtAvatarById(data) {
|
||||
return request('getAvatarById', data)
|
||||
return request('getAvatarById', data, 'get')
|
||||
},
|
||||
// 判断当前用户是否需要绑定手机号
|
||||
userCenterIsUserNeedBindPhone(data) {
|
||||
return request('isUserNeedBindPhone', data, 'get')
|
||||
},
|
||||
// 判断当前用户是否需要绑定邮箱
|
||||
userCenterIsUserNeedBindEmail(data) {
|
||||
return request('isUserNeedBindEmail', data, 'get')
|
||||
},
|
||||
// 判断当前用户密码是否过期
|
||||
userCenterIsUserPasswordExpired(data) {
|
||||
return request('isUserPasswordExpired', data, 'get')
|
||||
},
|
||||
// 获取修改密码验证方式及配置
|
||||
userGetUpdatePasswordValidConfig(data) {
|
||||
return request('getUpdatePasswordValidConfig', data, 'get')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
<script setup name="sysBizDataCard">
|
||||
import indexApi from '@/api/sys/indexApi'
|
||||
import { UserOutlined, ClusterOutlined, ApartmentOutlined, DeploymentUnitOutlined } from '@ant-design/icons-vue'
|
||||
const title = ref('业务数据')
|
||||
const apiLoading = ref(false)
|
||||
const dataSource = ref({
|
||||
|
||||
55
snowy-admin-web/src/components/XnMdEditor/mdEditor.js
Normal file
55
snowy-admin-web/src/components/XnMdEditor/mdEditor.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 该插件采用:https://code-farmer-i.github.io/vue-markdown-editor/zh/
|
||||
*
|
||||
* 使用时按需引入即可:import { XnMdEditor, XnMdPreview } from '@/components/XnMdEditor/mdEditor'
|
||||
* 如果需要参照,参考消息推送模块
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2025年5月12日19:28:03
|
||||
*/
|
||||
import VMdEditor from '@kangc/v-md-editor/lib/codemirror-editor'
|
||||
import '@kangc/v-md-editor/lib/style/codemirror-editor.css'
|
||||
import githubTheme from '@kangc/v-md-editor/lib/theme/github'
|
||||
import '@kangc/v-md-editor/lib/theme/style/github.css'
|
||||
|
||||
// highlightjs
|
||||
import 'highlight.js/styles/atom-one-dark.css'
|
||||
import 'highlight.js/lib/common'
|
||||
import hljsVuePlugin from '@highlightjs/vue-plugin'
|
||||
|
||||
// codemirror 编辑器的相关资源
|
||||
import Codemirror from 'codemirror'
|
||||
// mode
|
||||
import 'codemirror/mode/markdown/markdown'
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/mode/css/css'
|
||||
import 'codemirror/mode/htmlmixed/htmlmixed'
|
||||
import 'codemirror/mode/vue/vue'
|
||||
// edit
|
||||
import 'codemirror/addon/edit/closebrackets'
|
||||
import 'codemirror/addon/edit/closetag'
|
||||
import 'codemirror/addon/edit/matchbrackets'
|
||||
// placeholder
|
||||
import 'codemirror/addon/display/placeholder'
|
||||
// active-line
|
||||
import 'codemirror/addon/selection/active-line'
|
||||
// scrollbar
|
||||
import 'codemirror/addon/scroll/simplescrollbars'
|
||||
import 'codemirror/addon/scroll/simplescrollbars.css'
|
||||
// style
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
|
||||
import VMdPreview from '@kangc/v-md-editor/lib/preview'
|
||||
import '@kangc/v-md-editor/lib/style/preview.css'
|
||||
|
||||
const XnMdEditor = VMdEditor
|
||||
XnMdEditor.Codemirror = Codemirror
|
||||
|
||||
VMdPreview.use(githubTheme)
|
||||
const XnMdPreview = VMdPreview
|
||||
|
||||
XnMdEditor.use(githubTheme, {
|
||||
Hljs: hljsVuePlugin
|
||||
})
|
||||
|
||||
export { XnMdEditor, XnMdPreview }
|
||||
@@ -3,7 +3,7 @@
|
||||
<a-alert message="无任何菜单" type="info" :closable="false" />
|
||||
</div>
|
||||
<template v-for="navMenu in navMenus" :key="navMenu">
|
||||
<a-menu-item v-if="!hasChildren(navMenu) & !hasHidden(navMenu)" :key="navMenu.path">
|
||||
<a-menu-item v-if="!hasChildren(navMenu) && !hasHidden(navMenu)" :key="navMenu.path">
|
||||
<template v-if="navMenu.meta.icon" #icon>
|
||||
<component :is="navMenu.meta.icon" />
|
||||
</template>
|
||||
@@ -16,10 +16,11 @@
|
||||
>
|
||||
<a v-else>{{ navMenu.meta.title }}</a>
|
||||
</a-menu-item>
|
||||
<a-sub-menu v-else-if="!hasHidden(navMenu)" :key="navMenu.path" :title="navMenu.meta.title">
|
||||
<a-sub-menu v-else-if="!hasHidden(navMenu)" :key="navMenu.path">
|
||||
<template v-if="navMenu.meta.icon" #icon>
|
||||
<component :is="navMenu.meta.icon" />
|
||||
</template>
|
||||
<template #title>{{ navMenu.meta.title }}</template>
|
||||
<NavMenu :nav-menus="navMenu.children" />
|
||||
</a-sub-menu>
|
||||
</template>
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { AppstoreOutlined } from '@ant-design/icons-vue'
|
||||
import router from '@/router'
|
||||
import tool from '@/utils/tool'
|
||||
import { globalStore } from '@/store'
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
@keypress.up="handleKeyUp"
|
||||
@keypress.down="handleKeyDown"
|
||||
class="xn-mn10p0"
|
||||
|
||||
>
|
||||
<div ref="cardListRef" class="search-card beauty-scroll">
|
||||
<a-list size="small" :data-source="resultsList">
|
||||
@@ -47,7 +46,7 @@
|
||||
<a-list-item
|
||||
@click="handleSelect(item.fullPath)"
|
||||
@mouseover="onCardItemHover(index)"
|
||||
:class="{ active: index === cardIndex },'xn-pr10'"
|
||||
:class="[{ active: index === cardIndex }, 'xn-pr10']"
|
||||
>
|
||||
<template #actions>
|
||||
<a>
|
||||
@@ -100,6 +99,7 @@
|
||||
import { searchStore } from '@/store'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import { SearchOutlined, ArrowUpOutlined, ArrowDownOutlined, EnterOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -121,7 +121,7 @@
|
||||
return search.hotkey
|
||||
})
|
||||
const mixinSearch = computed(() => {
|
||||
return mixinSearch
|
||||
return search.mixinSearch
|
||||
})
|
||||
// 这份数据是展示在搜索面板下面的
|
||||
const resultsList = computed(() => {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="d2-panel-search-item" :class="props.hoverMode ? 'can-hover' : ''" flex>
|
||||
<div class="d2-panel-search-item__icon" flex-box="0">
|
||||
<div class="d2-panel-search-item__icon-box" flex="main:center cross:center">
|
||||
<a-icon v-if="props.item.icon" :type="props.item.icon" />
|
||||
<a-icon v-else type="menu" />
|
||||
<component v-if="props.item.icon" :is="props.item.icon" />
|
||||
<menu-outlined v-else />
|
||||
</div>
|
||||
</div>
|
||||
<div class="d2-panel-search-item__info" flex-box="1" flex="dir:top">
|
||||
@@ -21,11 +21,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { MenuOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
},
|
||||
hoverMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import NavMenu from './NavMenu.vue'
|
||||
import { globalStore } from '@/store'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { AppstoreOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const router = useRouter()
|
||||
const store = globalStore()
|
||||
|
||||
@@ -78,15 +78,13 @@
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import tool from '@/utils/tool'
|
||||
import { notification, Button } from 'ant-design-vue'
|
||||
import { FullscreenExitOutlined } from '@ant-design/icons-vue'
|
||||
import ClassicalMenu from '@/layout/menu/classicalMenu.vue'
|
||||
import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
|
||||
import TopMenu from '@/layout/menu/topMenu.vue'
|
||||
import { NextLoading } from '@/utils/loading'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { userStore } from '@/store/user'
|
||||
import { getLocalHash, checkHash } from '@/utils/version'
|
||||
import sysConfig from '@/config/index'
|
||||
import dictApi from '@/api/dev/dictApi'
|
||||
|
||||
const store = globalStore()
|
||||
const kStore = keepAliveStore()
|
||||
@@ -252,15 +250,6 @@
|
||||
updateVersion()
|
||||
nextTick(() => {
|
||||
getNav()
|
||||
// 刷新登录信息,不影响其他
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
// 刷新菜单信息,不影响其他
|
||||
useMenuStore().refreshApiMenu()
|
||||
// 刷新字典信息,不影响其他
|
||||
dictApi.dictTree().then((data) => {
|
||||
// 设置字典到store中
|
||||
tool.data.set('DICT_TYPE_TREE_DATA', data)
|
||||
})
|
||||
})
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
|
||||
<script setup>
|
||||
import { useRoute } from 'vue-router'
|
||||
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'
|
||||
const route = useRoute()
|
||||
|
||||
import UserBar from '@/layout/components/userbar.vue'
|
||||
|
||||
@@ -20,6 +20,8 @@ import { cloneDeep } from 'lodash-es'
|
||||
import { globalStore } from '@/store'
|
||||
import { NextLoading } from '@/utils/loading'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { useDictStore } from '@/store/dict'
|
||||
|
||||
// 进度条配置
|
||||
NProgress.configure({ showSpinner: false, speed: 500 })
|
||||
@@ -76,12 +78,31 @@ router.beforeEach(async (to, from, next) => {
|
||||
next({ ...to, replace: true })
|
||||
return false
|
||||
}
|
||||
|
||||
const token = tool.data.get('TOKEN')
|
||||
|
||||
// 页面刷新,加载loading
|
||||
if (from.path === '/' && to.path !== '/login' && !window.nextLoading && token) {
|
||||
NextLoading.start()
|
||||
// 并行刷新请求
|
||||
try {
|
||||
await Promise.all([
|
||||
// 刷新登录信息
|
||||
useUserStore().refreshUserLoginUserInfo(),
|
||||
// 刷新菜单信息
|
||||
useMenuStore().refreshApiMenu(),
|
||||
// 刷新字典
|
||||
useDictStore().refreshDict()
|
||||
])
|
||||
} catch (error) {
|
||||
console.error('页面刷新,数据初始化失败:', error)
|
||||
}
|
||||
} else if (
|
||||
to.matched.length === 1 &&
|
||||
!to.matched.some((record) => record.path.includes('layout')) &&
|
||||
to.path !== '/login' &&
|
||||
token
|
||||
) {
|
||||
// 从404等页面返回时只刷新菜单
|
||||
useMenuStore().refreshApiMenu()
|
||||
}
|
||||
if (to.path === '/login') {
|
||||
// 当用户输入了login路由,将其跳转首页即可
|
||||
|
||||
@@ -12,10 +12,10 @@ import config from '@/config'
|
||||
import tool from '@/utils/tool'
|
||||
import routerUtil from '@/utils/routerUtil'
|
||||
|
||||
import Layout from '@/layout/index.vue'
|
||||
import Login from '@/views/auth/login/login.vue'
|
||||
import Findpwd from '@/views/auth/findPwd/index.vue'
|
||||
import Callback from '@/views/auth/login/callback.vue'
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
const Login = () => import('@/views/auth/login/login.vue')
|
||||
const Findpwd = () => import('@/views/auth/findPwd/index.vue')
|
||||
const Callback = () => import('@/views/auth/login/callback.vue')
|
||||
|
||||
// 系统路由
|
||||
const routes = [
|
||||
|
||||
23
snowy-admin-web/src/store/dict.js
Normal file
23
snowy-admin-web/src/store/dict.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import dictApi from '@/api/dev/dictApi'
|
||||
import tool from '@/utils/tool'
|
||||
export const useDictStore = defineStore('useDictStore', () => {
|
||||
// 刷新字典信息
|
||||
const refreshDict = async () => {
|
||||
dictApi.dictTree().then((data) => {
|
||||
// 设置字典到store中
|
||||
tool.data.set('DICT_TYPE_TREE_DATA', data)
|
||||
})
|
||||
}
|
||||
return { refreshDict }
|
||||
})
|
||||
@@ -12,7 +12,7 @@ import { defineStore } from 'pinia'
|
||||
import loginApi from '@/api/auth/loginApi'
|
||||
import { useGlobalStore } from '@/store'
|
||||
import tool from '@/utils/tool'
|
||||
export const userStore = defineStore('userStore', () => {
|
||||
export const useUserStore = defineStore('useUserStore', () => {
|
||||
// 初始化用户信息
|
||||
const initUserInfo = async () => {
|
||||
const data = await loginApi.getLoginUser()
|
||||
|
||||
@@ -61,7 +61,6 @@ a, button, input, textarea {
|
||||
|
||||
/* 双排菜单布局 */
|
||||
.snowy-doublerow-layout-menu {
|
||||
padding-right: 5px;
|
||||
line-height: 0;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -371,6 +370,9 @@ body,
|
||||
.card-div,
|
||||
.ant-table-body,
|
||||
.left-tree-container,
|
||||
.ant-card-body,
|
||||
.approval-card-record,
|
||||
.approval-card-form,
|
||||
|
||||
.admin-ui-main{
|
||||
&::-webkit-scrollbar {
|
||||
|
||||
@@ -99,6 +99,9 @@ tool.dictTypeData = (dictValue, value) => {
|
||||
return '无此字典'
|
||||
}
|
||||
const children = tree.children
|
||||
if (!tree.children) {
|
||||
return '无此字典'
|
||||
}
|
||||
const dict = children.find((item) => item.dictValue === value)
|
||||
return dict ? dict.dictLabel : '无此字典项'
|
||||
}
|
||||
@@ -123,15 +126,18 @@ tool.dictList = (dictValue) => {
|
||||
return []
|
||||
}
|
||||
const tree = dictTypeTree.find((item) => item.dictValue === dictValue)
|
||||
if (tree) {
|
||||
return tree.children.map((item) => {
|
||||
return {
|
||||
value: item['dictValue'],
|
||||
label: item['name']
|
||||
}
|
||||
})
|
||||
if (!tree) {
|
||||
return []
|
||||
}
|
||||
return []
|
||||
if (!tree.children) {
|
||||
return []
|
||||
}
|
||||
return tree.children.map((item) => {
|
||||
return {
|
||||
value: item['dictValue'],
|
||||
label: item['name']
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 树形翻译 需要指定最顶级的 parentValue 和当级的value
|
||||
@@ -218,4 +224,19 @@ tool.parseTime = (time, cFormat) => {
|
||||
return time_str
|
||||
}
|
||||
|
||||
// 判断不为空
|
||||
tool.isNotEmpty = (value) => {
|
||||
if (typeof value === 'object') {
|
||||
for (const key in value) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return !(value === null || value === undefined || value === 'undefined' || value === '')
|
||||
}
|
||||
// 判断为空
|
||||
tool.isEmpty = (value) => {
|
||||
return !tool.isNotEmpty(value)
|
||||
}
|
||||
|
||||
export default tool
|
||||
|
||||
@@ -42,6 +42,23 @@ export default {
|
||||
router.push('/')
|
||||
}
|
||||
},
|
||||
// 关闭并返回
|
||||
closeBack(tag) {
|
||||
const route = tag || router.currentRoute.value
|
||||
const store = viewTagsStore()
|
||||
store.removeViewTags(route)
|
||||
iframeStore().removeIframeList(route)
|
||||
keepAliveStore().removeKeepLive(route.name)
|
||||
// 获取当前路由历史
|
||||
const currentHistory = router.options.history
|
||||
// 如果有上一个路由,则返回上一个路由
|
||||
if (currentHistory.state.back) {
|
||||
router.back()
|
||||
} else {
|
||||
// 如果没有上一个路由,则跳转到首页
|
||||
router.push('/')
|
||||
}
|
||||
},
|
||||
// 关闭标签后处理
|
||||
closeNext(next) {
|
||||
const route = router.currentRoute.value
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import dictApi from '@/api/dev/dictApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import router from '@/router'
|
||||
import tool from '@/utils/tool'
|
||||
import { message } from 'ant-design-vue'
|
||||
import routerUtil from '@/utils/routerUtil'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { userStore } from '@/store/user'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
export const afterLogin = async (loginToken) => {
|
||||
const menuStore = useMenuStore()
|
||||
tool.data.set('TOKEN', loginToken)
|
||||
// 初始化用户信息
|
||||
await userStore().initUserInfo()
|
||||
await useUserStore().initUserInfo()
|
||||
|
||||
// 获取用户的菜单
|
||||
const menu = await userCenterApi.userLoginMenu()
|
||||
let indexMenu = routerUtil.getIndexMenu(menu).path
|
||||
await menuStore.fetchMenu()
|
||||
const menu = tool.data.get('MENU')
|
||||
let indexMenu = routerUtil.getIndexMenu(menu).path
|
||||
|
||||
// 重置系统默认应用
|
||||
tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id)
|
||||
message.success('登录成功')
|
||||
@@ -46,4 +47,10 @@ export const afterLogin = async (loginToken) => {
|
||||
await router.replace({
|
||||
path: indexMenu
|
||||
})
|
||||
// 判断用户密码是否过期
|
||||
userCenterApi.userCenterIsUserPasswordExpired().then((expired) => {
|
||||
if (expired) {
|
||||
message.warning('当前登录密码已过期,请及时更改!')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
<a-form-item label="阿里云密钥SECRET:" name="SNOWY_EMAIL_ALIYUN_ACCESS_KEY_SECRET">
|
||||
<a-input v-model:value="formData.SNOWY_EMAIL_ALIYUN_ACCESS_KEY_SECRET" placeholder="请输入阿里云密钥SECRET" />
|
||||
</a-form-item>
|
||||
<a-form-item label="阿里云区域ID:" name="SNOWY_EMAIL_ALIYUN_REGION_ID">
|
||||
<a-input v-model:value="formData.SNOWY_EMAIL_ALIYUN_REGION_ID" placeholder="请输入阿里云区域ID" />
|
||||
<a-form-item label="默认发送账号:" name="SNOWY_EMAIL_ALIYUN_FROM">
|
||||
<a-input v-model:value="formData.SNOWY_EMAIL_ALIYUN_FROM" placeholder="请输入默认发送账号" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
@@ -55,7 +55,7 @@
|
||||
const formRules = {
|
||||
SNOWY_EMAIL_ALIYUN_ACCESS_KEY_ID: [required('请输入阿里云密钥ID')],
|
||||
SNOWY_EMAIL_ALIYUN_ACCESS_KEY_SECRET: [required('请输入阿里云密钥SECRET')],
|
||||
SNOWY_EMAIL_ALIYUN_REGION_ID: [required('请输入阿里云区域ID')]
|
||||
SNOWY_EMAIL_ALIYUN_FROM: [required('请输入默认发送账号')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
|
||||
@@ -25,14 +25,26 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否需要用户名密码验证:" name="SNOWY_EMAIL_LOCAL_AUTH">
|
||||
<a-switch v-model:checked="formData.SNOWY_EMAIL_LOCAL_AUTH" placeholder="请选择是否需要用户名密码验证" />
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_EMAIL_LOCAL_AUTH"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否需要用户名密码验证"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否使用SSL安全连接:" name="SNOWY_EMAIL_LOCAL_SSL_ENABLE">
|
||||
<a-switch v-model:checked="formData.SNOWY_EMAIL_LOCAL_SSL_ENABLE" placeholder="请选择是否使用SSL安全连接" />
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_EMAIL_LOCAL_SSL_ENABLE"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否使用SSL安全连接"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否使用STARTTLS安全连接:" name="SNOWY_EMAIL_LOCAL_STARTTLS_ENABLE">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_EMAIL_LOCAL_STARTTLS_ENABLE"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否使用STARTTLS安全连接"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -80,7 +92,10 @@
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_EMAIL_LOCAL_FROM: [required('请输入发送邮箱号')],
|
||||
SNOWY_EMAIL_LOCAL_PASSWORD: [required('请输入邮箱密钥')]
|
||||
SNOWY_EMAIL_LOCAL_PASSWORD: [required('请输入邮箱密钥')],
|
||||
SNOWY_EMAIL_LOCAL_AUTH: [required('请选择是否需要用户名密码验证')],
|
||||
SNOWY_EMAIL_LOCAL_SSL_ENABLE: [required('请选择是否使用SSL安全连接')],
|
||||
SNOWY_EMAIL_LOCAL_STARTTLS_ENABLE: [required('请选择是否使用STARTTLS安全连接')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
<a-form-item label="腾讯云密钥SECRET:" name="SNOWY_EMAIL_TENCENT_SECRET_KEY">
|
||||
<a-input v-model:value="formData.SNOWY_EMAIL_TENCENT_SECRET_KEY" placeholder="请输入腾讯云密钥SECRET" />
|
||||
</a-form-item>
|
||||
<a-form-item label="腾讯云区域ID:" name="SNOWY_EMAIL_TENCENT_REGION_ID">
|
||||
<a-input v-model:value="formData.SNOWY_EMAIL_TENCENT_REGION_ID" placeholder="请输入腾讯云区域ID" />
|
||||
<a-form-item label="默认发送账号:" name="SNOWY_EMAIL_TENCENT_FROM">
|
||||
<a-input v-model:value="formData.SNOWY_EMAIL_TENCENT_FROM" placeholder="请输入默认发送账号" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
@@ -55,7 +55,7 @@
|
||||
const formRules = {
|
||||
SNOWY_EMAIL_TENCENT_SECRET_ID: [required('请输入腾讯云密钥ID')],
|
||||
SNOWY_EMAIL_TENCENT_SECRET_KEY: [required('请输入腾讯云密钥SECRET')],
|
||||
SNOWY_EMAIL_TENCENT_REGION_ID: [required('请输入腾讯云密钥SECRET')]
|
||||
SNOWY_EMAIL_TENCENT_FROM: [required('请输入默认发送账号')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-table :dataSource="dataSource" :columns="columns" :pagination="false" bordered size="middle">
|
||||
<template #bodyCell="{ record, column }">
|
||||
<template v-if="column.dataIndex === 'subject'">
|
||||
<a-tag color="#FAAD14">{{ JSON.parse(record.configValue).subject }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-space>
|
||||
<a-button size="small" @click="emailPreviewRef.onOpen(JSON.parse(record.configValue).content)">
|
||||
<EyeOutlined />
|
||||
预览
|
||||
</a-button>
|
||||
<a-button type="primary" size="small" @click="emailSettingRef.onOpen(record)">
|
||||
<SettingOutlined />
|
||||
配置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<email-preview ref="emailPreviewRef" />
|
||||
<email-setting ref="emailSettingRef" @successful="onSubmit" />
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import { SettingOutlined, EyeOutlined } from '@ant-design/icons-vue'
|
||||
import EmailPreview from './preview.vue'
|
||||
import EmailSetting from './setting.vue'
|
||||
|
||||
const emailPreviewRef = ref()
|
||||
const emailSettingRef = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const dataSource = ref([])
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark'
|
||||
},
|
||||
{
|
||||
title: '邮件主题',
|
||||
dataIndex: 'subject',
|
||||
key: 'subject'
|
||||
},
|
||||
{
|
||||
title: '配置',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200
|
||||
}
|
||||
]
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'EMAIL_TEMPLATE_FOR_B'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
dataSource.value = data
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
const onSubmit = (param) => {
|
||||
submitLoading.value = true
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {
|
||||
initData()
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-table :dataSource="dataSource" :columns="columns" :pagination="false" bordered size="middle">
|
||||
<template #bodyCell="{ record, column }">
|
||||
<template v-if="column.dataIndex === 'subject'">
|
||||
<a-tag :bordered="false" color="processing">{{ JSON.parse(record.configValue).subject }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a-space>
|
||||
<a-button size="small" @click="emailPreviewRef.onOpen(JSON.parse(record.configValue).content)">
|
||||
<EyeOutlined />
|
||||
预览
|
||||
</a-button>
|
||||
<a-button type="primary" size="small" @click="emailSettingRef.onOpen(record)">
|
||||
<SettingOutlined />
|
||||
配置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<email-preview ref="emailPreviewRef" />
|
||||
<email-setting ref="emailSettingRef" @successful="onSubmit" />
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="cForm">
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import { SettingOutlined, EyeOutlined } from '@ant-design/icons-vue'
|
||||
import EmailPreview from './preview.vue'
|
||||
import EmailSetting from './setting.vue'
|
||||
|
||||
const emailPreviewRef = ref()
|
||||
const emailSettingRef = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const dataSource = ref([])
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark'
|
||||
},
|
||||
{
|
||||
title: '邮件主题',
|
||||
dataIndex: 'subject',
|
||||
key: 'subject'
|
||||
},
|
||||
{
|
||||
title: '配置',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 200
|
||||
}
|
||||
]
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'EMAIL_TEMPLATE_FOR_C'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
dataSource.value = data
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
const onSubmit = (param) => {
|
||||
submitLoading.value = true
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {
|
||||
initData()
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left">
|
||||
<a-tab-pane key="bForm" tab="后台模板">
|
||||
<b-form />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="cForm" tab="前台模板">
|
||||
<c-form />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup name="emailTemplateConfig">
|
||||
import BForm from './bForm.vue'
|
||||
import CForm from './cForm.vue'
|
||||
|
||||
const activeKey = ref('bForm')
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<a-modal title="预览" :width="700" :open="open" :destroy-on-close="true" :footer="null" @cancel="onClose">
|
||||
<div v-html="previewHtml"></div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup name="emailTemplatePreview">
|
||||
// 默认是关闭状态
|
||||
const open = ref(false)
|
||||
const previewHtml = ref('')
|
||||
const formRef = ref()
|
||||
// 打开抽屉
|
||||
const onOpen = (html) => {
|
||||
open.value = true
|
||||
previewHtml.value = html
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
previewHtml.value = ''
|
||||
open.value = false
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<xn-form-container title="模板设置" :width="800" :open="open" :destroy-on-close="true" @close="onClose">
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-form-item label="主题:" name="subject">
|
||||
<a-input v-model:value="formData.subject" />
|
||||
</a-form-item>
|
||||
<a-form-item name="content">
|
||||
<template #label>
|
||||
<div style="display: flex">
|
||||
<div>内容模板:</div>
|
||||
<div>
|
||||
<a-button size="small" @click="emailPreviewRef.onOpen(formData.content)">
|
||||
<EyeOutlined />
|
||||
预览
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="formData.content"
|
||||
placeholder="请输入邮件模板"
|
||||
allow-clear
|
||||
:auto-size="{ minRows: 20, maxRows: 20 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button class="xn-mr8" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" @click="onSubmit">保存</a-button>
|
||||
</template>
|
||||
</xn-form-container>
|
||||
<email-preview ref="emailPreviewRef" />
|
||||
</template>
|
||||
|
||||
<script setup name="emailTemplateSetting">
|
||||
import EmailPreview from './preview.vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
// 默认是关闭状态
|
||||
const open = ref(false)
|
||||
const formRef = ref()
|
||||
const emailPreviewRef = ref()
|
||||
const emit = defineEmits({ successful: null })
|
||||
// 表单数据
|
||||
const formData = ref({})
|
||||
// 打开编辑的key
|
||||
const recordConfigKey = ref('')
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
subject: [required('请输入邮件主题')],
|
||||
content: [required('请输入邮件模板内容')]
|
||||
}
|
||||
// 打开抽屉
|
||||
const onOpen = (record) => {
|
||||
open.value = true
|
||||
formData.value = JSON.parse(record.configValue)
|
||||
recordConfigKey.value = record.configKey
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
formData.value = {}
|
||||
recordConfigKey.value = ''
|
||||
open.value = false
|
||||
}
|
||||
// 提交数据
|
||||
const onSubmit = () => {
|
||||
// 验证完成后把数据回传到父界面,由父界面调用接口进行修改
|
||||
formRef.value.validate().then((value) => {
|
||||
const configKeyClone = recordConfigKey.value
|
||||
const configValueClone = JSON.stringify(value)
|
||||
const param = [
|
||||
{
|
||||
configKey: configKeyClone,
|
||||
configValue: configValueClone
|
||||
}
|
||||
]
|
||||
emit('successful', param)
|
||||
onClose()
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
@@ -9,12 +9,27 @@
|
||||
<p v-if="noTitleKey === 'sysConfig'">
|
||||
<SysConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'registerConfig'">
|
||||
<RegisterConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'loginConfig'">
|
||||
<LoginConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'passwordConfig'">
|
||||
<PasswordConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'emailConfig'">
|
||||
<EmailConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'emailTemplateConfig'">
|
||||
<EmailTemplateConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'smsConfig'">
|
||||
<SmsConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'smsTemplateConfig'">
|
||||
<SmsTemplateConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'fileConfig'">
|
||||
<FileConfig />
|
||||
</p>
|
||||
@@ -22,18 +37,23 @@
|
||||
<ThirdConfig />
|
||||
</p>
|
||||
<p v-else-if="noTitleKey === 'otherConfig'">
|
||||
<other-config />
|
||||
<OtherConfig />
|
||||
</p>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup name="devConfig">
|
||||
import SysConfig from './sysConfig.vue'
|
||||
import RegisterConfig from './registerConfig/index.vue'
|
||||
import LoginConfig from './loginConfig/index.vue'
|
||||
import PasswordConfig from './passwordConfig/index.vue'
|
||||
import EmailConfig from './emailConfig/index.vue'
|
||||
import EmailTemplateConfig from './emailTemplateConfig/index.vue'
|
||||
import SmsConfig from './smsConfig/index.vue'
|
||||
import SmsTemplateConfig from './smsTemplateConfig/index.vue'
|
||||
import FileConfig from './fileConfig/index.vue'
|
||||
import ThirdConfig from './thirdConfig/index.vue'
|
||||
import otherConfig from './otherConfig/index.vue'
|
||||
import OtherConfig from './otherConfig/index.vue'
|
||||
const key = ref('sysConfig')
|
||||
const noTitleKey = ref('sysConfig')
|
||||
|
||||
@@ -42,14 +62,34 @@
|
||||
key: 'sysConfig',
|
||||
tab: '系统配置'
|
||||
},
|
||||
{
|
||||
key: 'registerConfig',
|
||||
tab: '注册配置'
|
||||
},
|
||||
{
|
||||
key: 'loginConfig',
|
||||
tab: '登录配置'
|
||||
},
|
||||
{
|
||||
key: 'passwordConfig',
|
||||
tab: '密码配置'
|
||||
},
|
||||
{
|
||||
key: 'emailConfig',
|
||||
tab: '邮件配置'
|
||||
},
|
||||
{
|
||||
key: 'emailTemplateConfig',
|
||||
tab: '邮件模板'
|
||||
},
|
||||
{
|
||||
key: 'smsConfig',
|
||||
tab: '短信配置'
|
||||
},
|
||||
{
|
||||
key: 'smsTemplateConfig',
|
||||
tab: '短信模板'
|
||||
},
|
||||
{
|
||||
key: 'fileConfig',
|
||||
tab: '文件配置'
|
||||
@@ -72,3 +112,8 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-tabs-tab) {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
201
snowy-admin-web/src/views/dev/config/loginConfig/bForm.vue
Normal file
201
snowy-admin-web/src/views/dev/config/loginConfig/bForm.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
>
|
||||
<a-form-item label="连续登录失败持续时间:" name="SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_DURATION_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_DURATION_FOR_B"
|
||||
placeholder="分钟"
|
||||
style="width: 50%"
|
||||
>
|
||||
<template #addonAfter> 分钟 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="连续登录失败次数:" name="SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_TIMES_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_TIMES_FOR_B"
|
||||
placeholder="分钟"
|
||||
style="width: 50%"
|
||||
>
|
||||
<template #addonAfter> 次 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="连续登录失败锁定时间:" name="SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_LOCK_DURATION_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_LOCK_DURATION_FOR_B"
|
||||
placeholder="分钟"
|
||||
style="width: 50%"
|
||||
>
|
||||
<template #addonAfter> 分钟 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否允许手机号登录:" name="SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_B">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_B"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否允许手机号登录"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_B"
|
||||
name="SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_B"
|
||||
>
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title>是否能配置自动创建用户,取决于注册策略是否开启注册</template>
|
||||
<QuestionCircleOutlined /> 手机号无对应用户时策略:
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_B"
|
||||
:options="strategyWhenNoUserOptions"
|
||||
:disabled="loginNoUserPhoneDisabled"
|
||||
placeholder="请选择手机号无对应用户时策略"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否允许邮箱登录:" name="SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_B">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_B"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否允许邮箱登录"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_B"
|
||||
name="SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_B"
|
||||
>
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title>是否能配置自动创建用户,取决于注册策略是否开启注册</template>
|
||||
<QuestionCircleOutlined /> 邮箱无对应用户时策略:
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_B"
|
||||
:options="strategyWhenNoUserOptions"
|
||||
:disabled="loginNoUserEmailDisabled"
|
||||
placeholder="请选择邮箱无对应用户时策略"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import tool from '@/utils/tool'
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const strategyWhenNoUserOptions = tool.dictList('NO_USER_STRATEGY')
|
||||
const loginNoUserPhoneDisabled = ref(false)
|
||||
const loginNoUserEmailDisabled = ref(false)
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'LOGIN_STRATEGY_FOR_B'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
})
|
||||
registerConfig()
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
// 判断注册策略变更登录配置
|
||||
const registerConfig = () => {
|
||||
// 查询注册策略是否开启
|
||||
const regParam = {
|
||||
category: 'REGISTER_STRATEGY_FOR_B'
|
||||
}
|
||||
configApi.configList(regParam).then((regData) => {
|
||||
const regFlagResult = regData.find((f) => {
|
||||
if (f.configKey === 'SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B') {
|
||||
return f
|
||||
}
|
||||
})
|
||||
if (!regFlagResult) {
|
||||
return
|
||||
}
|
||||
const regFlag = regFlagResult.configValue
|
||||
if (regFlag !== 'true') {
|
||||
// 禁用
|
||||
loginNoUserPhoneDisabled.value = true
|
||||
loginNoUserEmailDisabled.value = true
|
||||
// 并且将其设为-不允许登录
|
||||
formData.value.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_B = 'NOT_ALLOW_LOGIN'
|
||||
formData.value.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_B = 'NOT_ALLOW_LOGIN'
|
||||
}
|
||||
})
|
||||
}
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_DURATION_FOR_B: [required('请输入连续登录失败持续时间')],
|
||||
SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_TIMES_FOR_B: [required('请输入连续登录失败次数')],
|
||||
SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_LOCK_DURATION_FOR_B: [required('请输入连续登录失败锁定时间')],
|
||||
SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_B: [required('请选择是否允许手机号登录')],
|
||||
SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_B: [required('请选择手机号无对应用户时策略')],
|
||||
SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_B: [required('请选择是否允许邮箱登录')],
|
||||
SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_B: [required('请选择邮箱无对应用户时策略')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 4
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
</script>
|
||||
200
snowy-admin-web/src/views/dev/config/loginConfig/cForm.vue
Normal file
200
snowy-admin-web/src/views/dev/config/loginConfig/cForm.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
>
|
||||
<a-form-item label="连续登录失败持续时间:" name="SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_DURATION_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_DURATION_FOR_C"
|
||||
placeholder="分钟"
|
||||
style="width: 50%"
|
||||
>
|
||||
<template #addonAfter> 分钟 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="连续登录失败次数:" name="SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_TIMES_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_TIMES_FOR_C"
|
||||
placeholder="分钟"
|
||||
style="width: 50%"
|
||||
>
|
||||
<template #addonAfter> 次 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="连续登录失败锁定时间:" name="SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_LOCK_DURATION_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_LOCK_DURATION_FOR_C"
|
||||
placeholder="分钟"
|
||||
style="width: 50%"
|
||||
>
|
||||
<template #addonAfter> 分钟 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否允许手机号登录:" name="SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_C">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_C"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否允许手机号登录"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_C"
|
||||
name="SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_C"
|
||||
>
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title>是否能配置自动创建用户,取决于注册策略是否开启注册</template>
|
||||
<QuestionCircleOutlined /> 手机号无对应用户时策略:
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_C"
|
||||
:options="strategyWhenNoUserOptions"
|
||||
:disabled="loginNoUserPhoneDisabled"
|
||||
placeholder="请选择手机号无对应用户时策略"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否允许邮箱登录:" name="SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_C">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_C"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否允许邮箱登录"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_C"
|
||||
name="SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_C"
|
||||
>
|
||||
<template #label>
|
||||
<a-tooltip>
|
||||
<template #title>是否能配置自动创建用户,取决于注册策略是否开启注册</template>
|
||||
<QuestionCircleOutlined /> 邮箱无对应用户时策略:
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_C"
|
||||
:options="strategyWhenNoUserOptions"
|
||||
:disabled="loginNoUserEmailDisabled"
|
||||
placeholder="请选择邮箱无对应用户时策略"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="cForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import tool from '@/utils/tool'
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const strategyWhenNoUserOptions = tool.dictList('NO_USER_STRATEGY')
|
||||
const loginNoUserPhoneDisabled = ref(false)
|
||||
const loginNoUserEmailDisabled = ref(false)
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'LOGIN_STRATEGY_FOR_C'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
})
|
||||
registerConfig()
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
// 判断注册策略变更登录配置
|
||||
const registerConfig = () => {
|
||||
// 查询注册策略是否开启
|
||||
const regParam = {
|
||||
category: 'REGISTER_STRATEGY_FOR_C'
|
||||
}
|
||||
configApi.configList(regParam).then((regData) => {
|
||||
const regFlagResult = regData.find((f) => {
|
||||
if (f.configKey === 'SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_C') {
|
||||
return f
|
||||
}
|
||||
})
|
||||
if (!regFlagResult) {
|
||||
return
|
||||
}
|
||||
const regFlag = regFlagResult.configValue
|
||||
if (regFlag !== 'true') {
|
||||
// 禁用
|
||||
loginNoUserPhoneDisabled.value = true
|
||||
loginNoUserEmailDisabled.value = true
|
||||
// 并且将其设为-不允许登录
|
||||
formData.value.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_C = 'NOT_ALLOW_LOGIN'
|
||||
formData.value.SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_C = 'NOT_ALLOW_LOGIN'
|
||||
}
|
||||
})
|
||||
}
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_DURATION_FOR_C: [required('请输入连续登录失败持续时间')],
|
||||
SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_TIMES_FOR_C: [required('请输入连续登录失败次数')],
|
||||
SNOWY_SYS_DEFAULT_CONTINUOUS_LOGIN_FAIL_LOCK_DURATION_FOR_C: [required('请输入连续登录失败锁定时间')],
|
||||
SNOWY_SYS_DEFAULT_ALLOW_PHONE_LOGIN_FLAG_FOR_C: [required('请选择是否允许手机号登录')],
|
||||
SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_PHONE_FOR_C: [required('请选择手机号无对应用户时策略')],
|
||||
SNOWY_SYS_DEFAULT_ALLOW_EMAIL_LOGIN_FLAG_FOR_C: [required('请选择是否允许邮箱登录')],
|
||||
SNOWY_SYS_DEFAULT_STRATEGY_WHEN_NO_USER_WITH_EMAIL_FOR_C: [required('请选择邮箱无对应用户时策略')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 4
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
</script>
|
||||
16
snowy-admin-web/src/views/dev/config/loginConfig/index.vue
Normal file
16
snowy-admin-web/src/views/dev/config/loginConfig/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left">
|
||||
<a-tab-pane key="bForm" tab="后台登录">
|
||||
<b-form />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="cForm" tab="前台登录">
|
||||
<c-form />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup name="loginConfig">
|
||||
import CForm from './cForm.vue'
|
||||
import BForm from './bForm.vue'
|
||||
const activeKey = ref('bForm')
|
||||
</script>
|
||||
269
snowy-admin-web/src/views/dev/config/passwordConfig/bForm.vue
Normal file
269
snowy-admin-web/src/views/dev/config/passwordConfig/bForm.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
style="width: 60%"
|
||||
>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="默认用户密码:" name="SNOWY_SYS_DEFAULT_PASSWORD_FOR_B">
|
||||
<a-input
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_FOR_B"
|
||||
placeholder="请输入默认用户密码"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码修改验证方式:" name="SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B"
|
||||
:options="updatePasswordValidTypeOptions"
|
||||
placeholder="请选择密码修改验证方式"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码最小长度:" name="SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_B"
|
||||
placeholder="请输入密码最小长度"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 位 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码最大长度:" name="SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_B"
|
||||
placeholder="请输入密码最大长度"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 位 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码复杂度:" name="SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_B">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_B"
|
||||
:options="passwordComplexityTypeOptions"
|
||||
direction="vertical"
|
||||
class="vertical-radio-group"
|
||||
placeholder="请选择密码复杂度"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="密码不能连续存在相同字符个数:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_B"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_B"
|
||||
placeholder="请输入密码不能连续存在相同字符个数"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 个 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码不能包含用户信息:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTAINS_USER_INFO_FLAG_FOR_B"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTAINS_USER_INFO_FLAG_FOR_B"
|
||||
checked-children="不能"
|
||||
un-checked-children="包含"
|
||||
placeholder="请选择密码不能包含用户信息"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码不能使用历史密码:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_FLAG_FOR_B"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_FLAG_FOR_B"
|
||||
checked-children="不能"
|
||||
un-checked-children="允许"
|
||||
placeholder="请选择密码不能使用历史密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="密码不能使用历史密码范围个数:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_COUNT_FOR_B"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_COUNT_FOR_B"
|
||||
placeholder="请输入密码不能使用历史密码范围个数"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 个 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="密码不能使用弱密码库中密码:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_WEAK_FLAG_FOR_B"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_WEAK_FLAG_FOR_B"
|
||||
checked-children="不能"
|
||||
un-checked-children="允许"
|
||||
placeholder="请选择密码不能使用弱密码库中密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码有效期天数:" name="SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_DAYS_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_DAYS_FOR_B"
|
||||
placeholder="天"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 天 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码过期提前提醒天数:" name="SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_NOTICE_DAYS_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_NOTICE_DAYS_FOR_B"
|
||||
placeholder="天"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 天 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="自定义额外弱密码库:" name="SNOWY_SYS_DEFAULT_PASSWORD_DEFINE_WEAK_DATABASE_FOR_B">
|
||||
<a-input
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_DEFINE_WEAK_DATABASE_FOR_B"
|
||||
placeholder="请输入自定义额外弱密码库"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const updatePasswordValidTypeOptions = tool.dictList('UPDATE_PASSWORD_VALID_TYPE')
|
||||
const passwordComplexityTypeOptions = tool.dictList('PASSWORD_COMPLEXITY_TYPE')
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'PASSWORD_STRATEGY_FOR_B'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_FOR_B: [required('请输入默认用户密码')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B: [required('请选择密码修改验证方式')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_B: [required('请输入密码最小长度')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_B: [required('请输入密码最大长度')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_B: [required('请选择密码复杂度')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_B: [
|
||||
required('请输入密码不能连续存在相同字符个数')
|
||||
],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTAINS_USER_INFO_FLAG_FOR_B: [required('请选择密码不能包含用户信息')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_FLAG_FOR_B: [required('请选择密码不能使用历史密码')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_COUNT_FOR_B: [required('密码不能使用历史密码范围个数')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_WEAK_FLAG_FOR_B: [required('请选择密码不能使用弱密码库中密码')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_DEFINE_WEAK_DATABASE_FOR_B: [required('请输入自定义额外弱密码库')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_DAYS_FOR_B: [required('请输入密码有效期天数')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_NOTICE_DAYS_FOR_B: [required('请输入密码过期天数')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 16
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 22
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vertical-radio-group :deep(.ant-radio-wrapper) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
269
snowy-admin-web/src/views/dev/config/passwordConfig/cForm.vue
Normal file
269
snowy-admin-web/src/views/dev/config/passwordConfig/cForm.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
style="width: 60%"
|
||||
>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="默认用户密码:" name="SNOWY_SYS_DEFAULT_PASSWORD_FOR_C">
|
||||
<a-input
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_FOR_C"
|
||||
placeholder="请输入默认用户密码"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码修改验证方式:" name="SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_C">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_C"
|
||||
:options="updatePasswordValidTypeOptions"
|
||||
placeholder="请选择密码修改验证方式"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码最小长度:" name="SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_C"
|
||||
placeholder="请输入密码最小长度"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 位 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码最大长度:" name="SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_C"
|
||||
placeholder="请输入密码最大长度"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 位 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码复杂度:" name="SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_C">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_C"
|
||||
:options="passwordComplexityTypeOptions"
|
||||
direction="vertical"
|
||||
class="vertical-radio-group"
|
||||
placeholder="请选择密码复杂度"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="密码不能连续存在相同字符个数:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_C"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_C"
|
||||
placeholder="请输入密码不能连续存在相同字符个数"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 个 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码不能包含用户信息:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTAINS_USER_INFO_FLAG_FOR_C"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTAINS_USER_INFO_FLAG_FOR_C"
|
||||
checked-children="不能"
|
||||
un-checked-children="包含"
|
||||
placeholder="请选择密码不能包含用户信息"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="密码不能使用历史密码:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_FLAG_FOR_C"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_FLAG_FOR_C"
|
||||
checked-children="不能"
|
||||
un-checked-children="允许"
|
||||
placeholder="请选择密码不能使用历史密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="密码不能使用历史密码范围个数:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_COUNT_FOR_C"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_COUNT_FOR_C"
|
||||
placeholder="请输入密码不能使用历史密码范围个数"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 个 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="密码不能使用弱密码库中密码:"
|
||||
name="SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_WEAK_FLAG_FOR_C"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_WEAK_FLAG_FOR_C"
|
||||
checked-children="不能"
|
||||
un-checked-children="允许"
|
||||
placeholder="请选择密码不能使用弱密码库中密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码有效期天数:" name="SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_DAYS_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_DAYS_FOR_C"
|
||||
placeholder="天"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 天 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="密码过期提前提醒天数:" name="SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_NOTICE_DAYS_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_NOTICE_DAYS_FOR_C"
|
||||
placeholder="天"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter> 天 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="自定义额外弱密码库:" name="SNOWY_SYS_DEFAULT_PASSWORD_DEFINE_WEAK_DATABASE_FOR_C">
|
||||
<a-input
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD_DEFINE_WEAK_DATABASE_FOR_C"
|
||||
placeholder="请输入自定义额外弱密码库"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const updatePasswordValidTypeOptions = tool.dictList('UPDATE_PASSWORD_VALID_TYPE')
|
||||
const passwordComplexityTypeOptions = tool.dictList('PASSWORD_COMPLEXITY_TYPE')
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'PASSWORD_STRATEGY_FOR_C'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_FOR_C: [required('请输入默认用户密码')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_C: [required('请选择密码修改验证方式')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_C: [required('请输入密码最小长度')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_C: [required('请输入密码最大长度')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_C: [required('请选择密码复杂度')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_C: [
|
||||
required('请输入密码不能连续存在相同字符个数')
|
||||
],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTAINS_USER_INFO_FLAG_FOR_C: [required('请选择密码不能包含用户信息')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_FLAG_FOR_C: [required('请选择密码不能使用历史密码')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_HISTORY_COUNT_FOR_C: [required('密码不能使用历史密码范围个数')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_USE_WEAK_FLAG_FOR_C: [required('请选择密码不能使用弱密码库中密码')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_DEFINE_WEAK_DATABASE_FOR_C: [required('请输入自定义额外弱密码库')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_DAYS_FOR_C: [required('请输入密码有效期天数')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD_EXPIRED_NOTICE_DAYS_FOR_C: [required('请输入密码过期天数')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 16
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 22
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vertical-radio-group :deep(.ant-radio-wrapper) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left">
|
||||
<a-tab-pane key="bForm" tab="后台密码">
|
||||
<b-form />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="cForm" tab="前台密码">
|
||||
<c-form />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup name="passwordConfig">
|
||||
import CForm from './cForm.vue'
|
||||
import BForm from './bForm.vue'
|
||||
const activeKey = ref('bForm')
|
||||
</script>
|
||||
186
snowy-admin-web/src/views/dev/config/registerConfig/bForm.vue
Normal file
186
snowy-admin-web/src/views/dev/config/registerConfig/bForm.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
>
|
||||
<a-form-item label="是否允许注册:" name="SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否允许注册"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B"
|
||||
label="注册后是否需要绑定手机号:"
|
||||
name="SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_PHONE_FOR_B"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_PHONE_FOR_B"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择注册后是否需要绑定手机号"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B"
|
||||
label="注册后是否需要绑定邮箱:"
|
||||
name="SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_EMAIL_FOR_B"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_EMAIL_FOR_B"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择注册后是否需要绑定邮箱"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B"
|
||||
label="新用户默认机构:"
|
||||
name="SNOWY_SYS_DEFAULT_NEW_USER_ORG_FOR_B"
|
||||
>
|
||||
<xn-org-selector
|
||||
:org-tree-api="selectApiFunction.orgTreeApi"
|
||||
:org-page-api="selectApiFunction.orgPageApi"
|
||||
:radioModel="true"
|
||||
dataType="string"
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_NEW_USER_ORG_FOR_B"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B"
|
||||
label="新用户默认职位:"
|
||||
name="SNOWY_SYS_DEFAULT_NEW_USER_POSITION_FOR_B"
|
||||
>
|
||||
<xn-position-selector
|
||||
:org-tree-api="selectApiFunction.orgTreeApi"
|
||||
:position-page-api="selectApiFunction.positionPageApi"
|
||||
:radioModel="true"
|
||||
dataType="string"
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_NEW_USER_POSITION_FOR_B"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B"
|
||||
label="新用户默认角色:"
|
||||
name="SNOWY_SYS_DEFAULT_NEW_USER_ROLE_FOR_B"
|
||||
>
|
||||
<xn-role-selector
|
||||
:org-tree-api="selectApiFunction.orgTreeApi"
|
||||
:role-page-api="selectApiFunction.rolePageApi"
|
||||
:radioModel="true"
|
||||
dataType="string"
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_NEW_USER_ROLE_FOR_B"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'REGISTER_STRATEGY_FOR_B'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
// 传递选择组件需要的API
|
||||
const selectApiFunction = {
|
||||
orgTreeApi: () => {
|
||||
return configApi.configOrgTree().then((data) => {
|
||||
return Promise.resolve(data)
|
||||
})
|
||||
},
|
||||
orgPageApi: (param) => {
|
||||
return configApi.configOrgSelector(param).then((data) => {
|
||||
return Promise.resolve(data)
|
||||
})
|
||||
},
|
||||
positionPageApi: (param) => {
|
||||
return configApi.configPositionSelector(param).then((data) => {
|
||||
return Promise.resolve(data)
|
||||
})
|
||||
},
|
||||
rolePageApi: (param) => {
|
||||
return configApi.configRoleSelector(param).then((data) => {
|
||||
return Promise.resolve(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_B: [required('请选择是否需要注册')],
|
||||
SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_PHONE_FOR_B: [required('请选择注册时是否需要绑定手机号')],
|
||||
SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_EMAIL_FOR_B: [required('请选择注册时是否需要绑定邮箱')],
|
||||
SNOWY_SYS_DEFAULT_NEW_USER_ORG_FOR_B: [required('请选择注册时新用户默认的机构')],
|
||||
SNOWY_SYS_DEFAULT_NEW_USER_ROLE_FOR_B: [required('请选择注册时新用户默认的角色')],
|
||||
SNOWY_SYS_DEFAULT_NEW_USER_POSITION_FOR_B: [required('请选择注册时新用户默认的职位')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 4
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
</script>
|
||||
120
snowy-admin-web/src/views/dev/config/registerConfig/cForm.vue
Normal file
120
snowy-admin-web/src/views/dev/config/registerConfig/cForm.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
>
|
||||
<a-form-item label="是否允许注册:" name="SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_C">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_C"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择是否允许注册"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_C"
|
||||
label="注册后是否需要绑定手机号:"
|
||||
name="SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_PHONE_FOR_C"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_PHONE_FOR_C"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择注册后是否需要绑定手机号"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="formData.SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_C"
|
||||
label="注册后是否需要绑定邮箱:"
|
||||
name="SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_EMAIL_FOR_C"
|
||||
>
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_EMAIL_FOR_C"
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
placeholder="请选择注册后是否需要绑定邮箱"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="cForm">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { required } from '@/utils/formRules'
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'REGISTER_STRATEGY_FOR_C'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
data.forEach((item) => {
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
SNOWY_SYS_DEFAULT_ALLOW_REGISTER_FLAG_FOR_C: [required('请选择是否需要注册')],
|
||||
SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_PHONE_FOR_C: [required('请选择注册时是否需要绑定手机号')],
|
||||
SNOWY_SYS_DEFAULT_REGISTER_NEED_BIND_EMAIL_FOR_C: [required('请选择注册时是否需要绑定邮箱')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
let submitParam = cloneDeep(formData.value)
|
||||
const param = Object.entries(submitParam).map((item) => {
|
||||
return {
|
||||
configKey: item[0],
|
||||
configValue: item[1]
|
||||
}
|
||||
})
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 4
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 12
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left">
|
||||
<a-tab-pane key="bForm" tab="后台注册">
|
||||
<b-form />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="cForm" tab="前台注册">
|
||||
<c-form />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup name="registerConfig">
|
||||
import BForm from './bForm.vue'
|
||||
import CForm from './cForm.vue'
|
||||
|
||||
const activeKey = ref('bForm')
|
||||
</script>
|
||||
@@ -14,9 +14,6 @@
|
||||
<a-form-item label="阿里云密钥SECRET:" name="SNOWY_SMS_ALIYUN_ACCESS_KEY_SECRET">
|
||||
<a-input v-model:value="formData.SNOWY_SMS_ALIYUN_ACCESS_KEY_SECRET" placeholder="请输入阿里云密钥SECRET" />
|
||||
</a-form-item>
|
||||
<a-form-item label="阿里云短信端点:" name="SNOWY_SMS_ALIYUN_END_POINT">
|
||||
<a-input v-model:value="formData.SNOWY_SMS_ALIYUN_END_POINT" placeholder="请输入阿里云短信端点" />
|
||||
</a-form-item>
|
||||
<a-form-item label="阿里云短信签名:" name="SNOWY_SMS_ALIYUN_DEFAULT_SIGN_NAME">
|
||||
<a-input v-model:value="formData.SNOWY_SMS_ALIYUN_DEFAULT_SIGN_NAME" placeholder="请输入阿里云短信签名" />
|
||||
</a-form-item>
|
||||
@@ -58,7 +55,6 @@
|
||||
const formRules = {
|
||||
SNOWY_SMS_ALIYUN_ACCESS_KEY_ID: [required('请输入阿里云密钥ID')],
|
||||
SNOWY_SMS_ALIYUN_ACCESS_KEY_SECRET: [required('请输入阿里云密钥SECRET')],
|
||||
SNOWY_SMS_ALIYUN_END_POINT: [required('请输入阿里云短信端点')],
|
||||
SNOWY_SMS_ALIYUN_DEFAULT_SIGN_NAME: [required('请输入阿里云短信签名')]
|
||||
}
|
||||
// 验证并提交数据
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
<a-form-item label="腾讯云密钥SECRET:" name="SNOWY_SMS_TENCENT_SECRET_KEY">
|
||||
<a-input v-model:value="formData.SNOWY_SMS_TENCENT_SECRET_KEY" placeholder="请输入腾讯云密钥SECRET" />
|
||||
</a-form-item>
|
||||
<a-form-item label="腾讯云区域ID:" name="SNOWY_SMS_TENCENT_REGION_ID">
|
||||
<a-input v-model:value="formData.SNOWY_SMS_TENCENT_REGION_ID" placeholder="请输入腾讯云区域ID" />
|
||||
</a-form-item>
|
||||
<a-form-item label="腾讯云应用ID:" name="SNOWY_SMS_TENCENT_DEFAULT_SDK_APP_ID">
|
||||
<a-input v-model:value="formData.SNOWY_SMS_TENCENT_DEFAULT_SDK_APP_ID" placeholder="请输入腾讯云应用ID" />
|
||||
</a-form-item>
|
||||
@@ -61,7 +58,6 @@
|
||||
const formRules = {
|
||||
SNOWY_SMS_TENCENT_SECRET_ID: [required('请输入腾讯云密钥ID')],
|
||||
SNOWY_SMS_TENCENT_SECRET_KEY: [required('请输入腾讯云密钥SECRET')],
|
||||
SNOWY_SMS_TENCENT_REGION_ID: [required('请输入腾讯云区域ID')],
|
||||
SNOWY_SMS_TENCENT_DEFAULT_SDK_APP_ID: [required('请输入腾讯云应用ID')],
|
||||
SNOWY_SMS_TENCENT_DEFAULT_SIGN_NAME: [required('请输入腾讯云短信签名')]
|
||||
}
|
||||
|
||||
115
snowy-admin-web/src/views/dev/config/smsTemplateConfig/bForm.vue
Normal file
115
snowy-admin-web/src/views/dev/config/smsTemplateConfig/bForm.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-table :dataSource="dataSource" :columns="columns" :pagination="false" bordered size="middle">
|
||||
<template #bodyCell="{ text, record, column }">
|
||||
<template v-if="column.dataIndex === 'templateCode'">
|
||||
<a-input
|
||||
v-model:value="record.templateCode"
|
||||
@input="handleInput($event, record)"
|
||||
placeholder="请输入模板号"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'templateContent'">
|
||||
<div class="flex items-center">
|
||||
<span>{{ text }}</span>
|
||||
<a-button type="link" @click="copyContent(text)">
|
||||
<template #icon><copy-outlined /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="pt-3">
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => resetCode()">重置</a-button>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import { CopyOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const dataSource = ref([])
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark'
|
||||
},
|
||||
{
|
||||
title: '模板内容',
|
||||
dataIndex: 'templateContent',
|
||||
key: 'templateContent'
|
||||
},
|
||||
{
|
||||
title: '模板ID/CODE',
|
||||
dataIndex: 'templateCode',
|
||||
key: 'templateCode'
|
||||
}
|
||||
]
|
||||
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'SMS_TEMPLATE_FOR_B'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
dataSource.value = data.map((item) => {
|
||||
const configValueObj = JSON.parse(item.configValue)
|
||||
return {
|
||||
...item,
|
||||
templateCode: configValueObj.code,
|
||||
templateContent: configValueObj.content
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
submitLoading.value = true
|
||||
const param = dataSource.value.map((item) => ({
|
||||
configKey: item.configKey,
|
||||
configValue: JSON.stringify({
|
||||
code: item.templateCode,
|
||||
content: item.templateContent
|
||||
})
|
||||
}))
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
// 复制内容
|
||||
const copyContent = (text) => {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
message.success('复制成功')
|
||||
})
|
||||
}
|
||||
// 输入框限制不能输入汉字
|
||||
const handleInput = (e, record) => {
|
||||
const value = e.target.value
|
||||
if (/[\u4e00-\u9fa5]/.test(value)) {
|
||||
record.templateCode = value.replace(/[\u4e00-\u9fa5]/g, '')
|
||||
message.warning('不能输入汉字')
|
||||
}
|
||||
}
|
||||
// 重置
|
||||
const resetCode = () => {
|
||||
dataSource.value = dataSource.value.map((item) => ({
|
||||
...item,
|
||||
templateCode: ''
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
115
snowy-admin-web/src/views/dev/config/smsTemplateConfig/cForm.vue
Normal file
115
snowy-admin-web/src/views/dev/config/smsTemplateConfig/cForm.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-table :dataSource="dataSource" :columns="columns" :pagination="false" bordered size="middle">
|
||||
<template #bodyCell="{ text, record, column }">
|
||||
<template v-if="column.dataIndex === 'templateCode'">
|
||||
<a-input
|
||||
v-model:value="record.templateCode"
|
||||
@input="handleInput($event, record)"
|
||||
placeholder="请输入模板号"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'templateContent'">
|
||||
<div class="flex items-center">
|
||||
<span>{{ text }}</span>
|
||||
<a-button type="link" @click="copyContent(text)">
|
||||
<template #icon><copy-outlined /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="pt-3">
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="() => resetCode()">重置</a-button>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup name="bForm">
|
||||
import { message } from 'ant-design-vue'
|
||||
import configApi from '@/api/dev/configApi'
|
||||
import { CopyOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const loadSpinning = ref(true)
|
||||
const dataSource = ref([])
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark'
|
||||
},
|
||||
{
|
||||
title: '模板内容',
|
||||
dataIndex: 'templateContent',
|
||||
key: 'templateContent'
|
||||
},
|
||||
{
|
||||
title: '模板ID/CODE',
|
||||
dataIndex: 'templateCode',
|
||||
key: 'templateCode'
|
||||
}
|
||||
]
|
||||
|
||||
// 查询此界面的配置项,并转为表单
|
||||
const param = {
|
||||
category: 'SMS_TEMPLATE_FOR_C'
|
||||
}
|
||||
configApi.configList(param).then((data) => {
|
||||
loadSpinning.value = false
|
||||
if (data) {
|
||||
dataSource.value = data.map((item) => {
|
||||
const configValueObj = JSON.parse(item.configValue)
|
||||
return {
|
||||
...item,
|
||||
templateCode: configValueObj.code,
|
||||
templateContent: configValueObj.content
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.warning('表单项不存在,请初始化数据库')
|
||||
}
|
||||
})
|
||||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
submitLoading.value = true
|
||||
const param = dataSource.value.map((item) => ({
|
||||
configKey: item.configKey,
|
||||
configValue: JSON.stringify({
|
||||
code: item.templateCode,
|
||||
content: item.templateContent
|
||||
})
|
||||
}))
|
||||
configApi
|
||||
.configEditForm(param)
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
// 复制内容
|
||||
const copyContent = (text) => {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
message.success('复制成功')
|
||||
})
|
||||
}
|
||||
// 输入框限制不能输入汉字
|
||||
const handleInput = (e, record) => {
|
||||
const value = e.target.value
|
||||
if (/[\u4e00-\u9fa5]/.test(value)) {
|
||||
record.templateCode = value.replace(/[\u4e00-\u9fa5]/g, '')
|
||||
message.warning('不能输入汉字')
|
||||
}
|
||||
}
|
||||
// 重置
|
||||
const resetCode = () => {
|
||||
dataSource.value = dataSource.value.map((item) => ({
|
||||
...item,
|
||||
templateCode: ''
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left">
|
||||
<a-tab-pane key="bForm" tab="后台模板">
|
||||
<b-form />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="cForm" tab="前台模板">
|
||||
<c-form />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup name="smsTemplateConfig">
|
||||
import BForm from './bForm.vue'
|
||||
import CForm from './cForm.vue'
|
||||
|
||||
const activeKey = ref('bForm')
|
||||
</script>
|
||||
@@ -1,8 +1,16 @@
|
||||
<template>
|
||||
<a-spin :spinning="loadSpinning">
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical" :label-col="labelCol">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
:label-col="{ ...layout.labelCol, offset: 0 }"
|
||||
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
|
||||
style="width: 90%"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="3">
|
||||
<a-col :span="4">
|
||||
<a-form-item label="系统LOGO:" name="SNOWY_SYS_LOGO">
|
||||
<a-upload
|
||||
v-model:file-list="formData.SNOWY_SYS_LOGO"
|
||||
@@ -21,60 +29,134 @@
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="系统名称:" name="SNOWY_SYS_NAME">
|
||||
<a-input v-model:value="formData.SNOWY_SYS_NAME" placeholder="请输入系统名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="后台验证码开关:" name="SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_B">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_B"
|
||||
checked-children="开"
|
||||
un-checked-children="关"
|
||||
placeholder="请选择后台验证码开关"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="前台验证码开关:" name="SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_C">
|
||||
<a-switch
|
||||
v-model:checked="formData.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_C"
|
||||
checked-children="开"
|
||||
un-checked-children="关"
|
||||
placeholder="请选择前台验证码开关"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="系统版本:" name="SNOWY_SYS_VERSION">
|
||||
<a-input v-model:value="formData.SNOWY_SYS_VERSION" placeholder="请输入系统版本" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="全局后台验证码失效时间:" name="SNOWY_SYS_DEFAULT_CAPTCHA_EXPIRED_DURATION_FOR_B">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CAPTCHA_EXPIRED_DURATION_FOR_B"
|
||||
placeholder="分钟"
|
||||
>
|
||||
<template #addonAfter> 分钟 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="全局前台验证码失效时间:" name="SNOWY_SYS_DEFAULT_CAPTCHA_EXPIRED_DURATION_FOR_C">
|
||||
<a-input-number
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CAPTCHA_EXPIRED_DURATION_FOR_C"
|
||||
placeholder="分钟"
|
||||
>
|
||||
<template #addonAfter> 分钟 </template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="版权信息:" name="SNOWY_SYS_COPYRIGHT">
|
||||
<a-input v-model:value="formData.SNOWY_SYS_COPYRIGHT" placeholder="请输入版权信息" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="默认文件引擎:" name="SNOWY_SYS_DEFAULT_FILE_ENGINE">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_FILE_ENGINE"
|
||||
:options="fileEngineOptions"
|
||||
placeholder="请选择系统默认文件引擎"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="默认短信引擎:" name="SNOWY_SYS_DEFAULT_SMS_ENGINE">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_SMS_ENGINE"
|
||||
:options="smsEngineOptions"
|
||||
placeholder="请选择系统默认短信引擎"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="版权链接URL:" name="SNOWY_SYS_COPYRIGHT_URL">
|
||||
<a-input v-model:value="formData.SNOWY_SYS_COPYRIGHT_URL" placeholder="请输入版权链接URL" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="验证码开关:" name="SNOWY_SYS_DEFAULT_CAPTCHA_OPEN">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN"
|
||||
:options="commonSwitchOptions"
|
||||
placeholder="请选择验证码开关"
|
||||
></a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="默认文件引擎:" name="SNOWY_SYS_DEFAULT_FILE_ENGINE">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_FILE_ENGINE"
|
||||
:options="fileEngineOptions"
|
||||
placeholder="请选择系统默认文件引擎"
|
||||
></a-radio-group>
|
||||
</a-form-item>
|
||||
<a-col :span="14">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="默认邮件引擎:" name="SNOWY_SYS_DEFAULT_EMAIL_ENGINE">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_EMAIL_ENGINE"
|
||||
:options="emailEngineOptions"
|
||||
placeholder="请选择系统默认邮件引擎"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="默认消息推送引擎:" name="SNOWY_SYS_DEFAULT_PUSH_ENGINE">
|
||||
<a-radio-group
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_PUSH_ENGINE"
|
||||
:options="pushEngineOptions"
|
||||
placeholder="请选择系统默认消息推送引擎"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="默认快捷方式:" name="SNOWY_SYS_DEFAULT_WORKBENCH_DATA">
|
||||
<menuTreeSelect ref="menuTreeSelectRef" :resultData="true" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="系统默认密码:" name="SNOWY_SYS_DEFAULT_PASSWORD">
|
||||
<a-input v-model:value="formData.SNOWY_SYS_DEFAULT_PASSWORD" placeholder="请输入系统默认密码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="14">
|
||||
<a-form-item label="系统描述:" name="SNOWY_SYS_DEFAULT_DESCRRIPTION">
|
||||
<a-textarea
|
||||
v-model:value="formData.SNOWY_SYS_DEFAULT_DESCRRIPTION"
|
||||
@@ -84,8 +166,8 @@
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="24">
|
||||
<a-row :gutter="14">
|
||||
<a-col :span="12">
|
||||
<a-form-item>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit()">保存</a-button>
|
||||
<a-button class="xn-ml10" @click="resetForm">重置</a-button>
|
||||
@@ -134,7 +216,7 @@
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
} else {
|
||||
formData.value[item.configKey] = item.configValue
|
||||
formData.value[item.configKey] = transferBooleanInValue(item.configValue)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -143,9 +225,21 @@
|
||||
})
|
||||
// 文件引擎
|
||||
const fileEngineOptions = tool.dictList('FILE_ENGINE')
|
||||
// 开关
|
||||
const commonSwitchOptions = tool.dictList('COMMON_SWITCH')
|
||||
// 短信引擎
|
||||
const smsEngineOptions = tool.dictList('SMS_ENGINE')
|
||||
// 邮件引擎
|
||||
const emailEngineOptions = tool.dictList('EMAIL_ENGINE')
|
||||
// 消息推送引擎
|
||||
const pushEngineOptions = tool.dictList('PUSH_ENGINE')
|
||||
|
||||
// 转换值
|
||||
const transferBooleanInValue = (value) => {
|
||||
if (value === 'true' || value === 'false') {
|
||||
return value === 'true'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
const customRequest = (data) => {
|
||||
formData.value.SNOWY_SYS_LOGO = ref([])
|
||||
getBase64(data.file)
|
||||
@@ -177,13 +271,19 @@
|
||||
SNOWY_SYS_COPYRIGHT: [required('请输入版权信息')],
|
||||
SNOWY_SYS_COPYRIGHT_URL: [required('请输入版权链接URL')],
|
||||
SNOWY_SYS_DEFAULT_FILE_ENGINE: [required('请选择系统默认文件引擎')],
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_OPEN: [required('请选择系统验证码开关')],
|
||||
SNOWY_SYS_DEFAULT_SMS_ENGINE: [required('请选择系统默认短信引擎')],
|
||||
SNOWY_SYS_DEFAULT_EMAIL_ENGINE: [required('请选择系统默认邮件引擎')],
|
||||
SNOWY_SYS_DEFAULT_PUSH_ENGINE: [required('请选择系统默认消息推送引擎')],
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_C: [required('请选择前台验证码开关')],
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_B: [required('请选择后台验证码开关')],
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_EXPIRED_DURATION_FOR_C: [required('请输入过期时间')],
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_EXPIRED_DURATION_FOR_B: [required('请输入过期时间')],
|
||||
SNOWY_SYS_DEFAULT_PASSWORD: [required('请输入系统重置密码默认密码')]
|
||||
}
|
||||
// 表单固定label实现
|
||||
const labelCol = ref({
|
||||
style: {
|
||||
width: '150px'
|
||||
width: '200px'
|
||||
}
|
||||
})
|
||||
// 验证并提交数据
|
||||
@@ -222,8 +322,20 @@
|
||||
const resetForm = () => {
|
||||
imageUrl.value = ''
|
||||
formData.value = {
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_OPEN: 'true',
|
||||
SNOWY_SYS_DEFAULT_FILE_ENGINE: 'LOCAL'
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_C: true,
|
||||
SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_FLAG_FOR_B: true,
|
||||
SNOWY_SYS_DEFAULT_FILE_ENGINE: 'LOCAL',
|
||||
SNOWY_SYS_DEFAULT_SMS_ENGINE: 'XIAONUO',
|
||||
SNOWY_SYS_DEFAULT_EMAIL_ENGINE: 'LOCAL',
|
||||
SNOWY_SYS_DEFAULT_PUSH_ENGINE: 'DINGTALK'
|
||||
}
|
||||
}
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 16
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 22
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,4 +20,7 @@
|
||||
:deep(.ant-card-body) {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
:deep(.ant-tabs-tab) {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
<script setup name="devMonitor">
|
||||
import { onMounted } from 'vue'
|
||||
import monitorApi from '@/api/dev/monitorApi'
|
||||
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons-vue'
|
||||
const spinning = ref(false)
|
||||
const networkSpinning = ref(false)
|
||||
// CPU信息
|
||||
|
||||
66
snowy-admin-web/src/views/dev/push/detail.vue
Normal file
66
snowy-admin-web/src/views/dev/push/detail.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<xn-form-container title="详情" :width="800" :visible="visible" :destroy-on-close="true" @close="onClose">
|
||||
<a-descriptions :column="1" size="middle" bordered class="mb-2">
|
||||
<a-descriptions-item label="消息标题">{{ formData.title }}</a-descriptions-item>
|
||||
<a-descriptions-item label="消息引擎">
|
||||
{{ $TOOL.dictTypeData('PUSH_ENGINE', formData.engine) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="消息类别">{{ formData.type }}</a-descriptions-item>
|
||||
<a-descriptions-item label="发送人" v-if="formData.createUserName">
|
||||
{{ formData.createUserName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="发送时间">{{ formData.createTime }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<a-space direction="vertical" class="mb-2 xn-wd">
|
||||
详细信息:
|
||||
<xn-md-preview ref="mdPreviewRef" id="mdPreviewRef" :text="formData.content" class="md-preview" />
|
||||
</a-space>
|
||||
<a-space direction="vertical" class="mb-2 xn-wd">
|
||||
回执信息:
|
||||
<XnHighlightjs language="JSON" :code="receiptInfo"></XnHighlightjs>
|
||||
</a-space>
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script setup name="smsDetail">
|
||||
import pushApi from '@/api/dev/pushApi'
|
||||
import { XnMdPreview } from '@/components/XnMdEditor/mdEditor'
|
||||
// 默认是关闭状态
|
||||
const visible = ref(false)
|
||||
const receiptInfo = ref()
|
||||
// 表单数据
|
||||
const formData = ref({})
|
||||
// 打开抽屉
|
||||
const onOpen = (record) => {
|
||||
visible.value = true
|
||||
getFileDetail(record)
|
||||
}
|
||||
// 获取站内信列表
|
||||
const getFileDetail = (record) => {
|
||||
const param = {
|
||||
id: record.id
|
||||
}
|
||||
pushApi.pushDetail(param).then((data) => {
|
||||
Object.assign(record, data)
|
||||
formData.value = record
|
||||
if (record.receiptInfo) {
|
||||
const jsonStr = JSON.parse(record.receiptInfo)
|
||||
receiptInfo.value = JSON.stringify(jsonStr, undefined, 2)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
formData.value = {}
|
||||
visible.value = false
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.github-markdown-body) {
|
||||
padding: 16px 0 !important;
|
||||
}
|
||||
</style>
|
||||
74
snowy-admin-web/src/views/dev/push/form.vue
Normal file
74
snowy-admin-web/src/views/dev/push/form.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<xn-form-container
|
||||
title="推送消息"
|
||||
:width="1000"
|
||||
:visible="visible"
|
||||
:destroy-on-close="true"
|
||||
:bodyStyle="{ 'padding-top': '0px' }"
|
||||
@close="onClose"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="feishuPushSend" tab="飞书消息">
|
||||
<feishu-push-send ref="feishuPushSendRef" @loadingStart="loadingStart" @loadingEnd="loadingEnd" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="dingPushSend" tab="钉钉消息">
|
||||
<ding-push-send ref="dingPushSendRef" @loadingStart="loadingStart" @loadingEnd="loadingEnd" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="workWechatPushSend" tab="企业微信消息">
|
||||
<work-wechat-push-send ref="workWechatPushSendRef" @loadingStart="loadingStart" @loadingEnd="loadingEnd" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<template #footer>
|
||||
<a-button class="xn-mr8" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" @click="onSubmit" :loading="sendLoading">发送</a-button>
|
||||
</template>
|
||||
</xn-form-container>
|
||||
</template>
|
||||
|
||||
<script setup name="pushForm">
|
||||
import FeishuPushSend from './send/feishuPushSend.vue'
|
||||
import DingPushSend from './send/dingPushSend.vue'
|
||||
import WorkWechatPushSend from './send/workWechatPushSend.vue'
|
||||
|
||||
const feishuPushSendRef = ref()
|
||||
const dingPushSendRef = ref()
|
||||
const workWechatPushSendRef = ref()
|
||||
|
||||
// 默认是关闭状态
|
||||
const visible = ref(false)
|
||||
const activeKey = ref('feishuPushSend')
|
||||
const sendLoading = ref(false)
|
||||
const emit = defineEmits({ successful: null })
|
||||
// 打开抽屉
|
||||
const onOpen = () => {
|
||||
visible.value = true
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
visible.value = false
|
||||
emit('successful')
|
||||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
const tabActiveKey = activeKey.value
|
||||
if (tabActiveKey === 'feishuPushSend') {
|
||||
feishuPushSendRef.value.send()
|
||||
} else if (tabActiveKey === 'dingPushSend') {
|
||||
dingPushSendRef.value.send()
|
||||
} else if (tabActiveKey === 'workWechatPushSend') {
|
||||
workWechatPushSendRef.value.send()
|
||||
}
|
||||
}
|
||||
// 请求loading开始
|
||||
const loadingStart = () => {
|
||||
sendLoading.value = true
|
||||
}
|
||||
// 请求loading结束
|
||||
const loadingEnd = () => {
|
||||
sendLoading.value = false
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
175
snowy-admin-web/src/views/dev/push/index.vue
Normal file
175
snowy-admin-web/src/views/dev/push/index.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<a-card :bordered="false" class="xn-mb10">
|
||||
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="8">
|
||||
<a-form-item name="engine" label="消息引擎">
|
||||
<a-select
|
||||
v-model:value="searchFormState.engine"
|
||||
:options="engineOptions"
|
||||
placeholder="请选择消息引擎"
|
||||
:getPopupContainer="(trigger) => trigger.parentNode"
|
||||
allow-clear
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item name="searchKey" label="消息标题关键词">
|
||||
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入消息标题关键词"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-button type="primary" @click="tableRef.refresh(true)">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button class="snowy-button-left" @click="reset">
|
||||
<template #icon><redo-outlined /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
<a-card :bordered="false">
|
||||
<s-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:data="loadData"
|
||||
:expand-row-by-click="true"
|
||||
:alert="options.alert.show"
|
||||
bordered
|
||||
:row-key="(record) => record.id"
|
||||
:row-selection="options.rowSelection"
|
||||
>
|
||||
<template #operator class="table-operator">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="formRef.onOpen()">
|
||||
<template #icon><plus-outlined /></template>
|
||||
推送消息
|
||||
</a-button>
|
||||
<xn-batch-button
|
||||
buttonName="批量删除"
|
||||
icon="DeleteOutlined"
|
||||
buttonDanger
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
@batchCallBack="deleteBatchPush"
|
||||
/>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'engine'">
|
||||
{{ $TOOL.dictTypeData('PUSH_ENGINE', record.engine) }}
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a @click="detailRef.onOpen(record)">详情</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm title="删除此消息?" @confirm="deletePush(record)">
|
||||
<a-button type="link" danger size="small">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</s-table>
|
||||
</a-card>
|
||||
<Form ref="formRef" @successful="tableRef.refresh(true)" />
|
||||
<detail ref="detailRef" />
|
||||
</template>
|
||||
|
||||
<script setup name="devPushIndex">
|
||||
import pushApi from '@/api/dev/pushApi'
|
||||
import Form from './form.vue'
|
||||
import Detail from './detail.vue'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
const tableRef = ref(null)
|
||||
const formRef = ref()
|
||||
const searchFormRef = ref()
|
||||
const searchFormState = ref({})
|
||||
const detailRef = ref()
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '消息标题',
|
||||
dataIndex: 'title'
|
||||
},
|
||||
{
|
||||
title: '消息引擎',
|
||||
dataIndex: 'engine',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '消息类别',
|
||||
dataIndex: 'type',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '消息内容',
|
||||
dataIndex: 'content',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '回执信息',
|
||||
dataIndex: 'receiptInfo',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: '150px'
|
||||
}
|
||||
]
|
||||
let selectedRowKeys = ref([])
|
||||
// 列表选择配置
|
||||
const options = {
|
||||
alert: {
|
||||
show: false,
|
||||
clear: () => {
|
||||
selectedRowKeys = ref([])
|
||||
}
|
||||
},
|
||||
rowSelection: {
|
||||
onChange: (selectedRowKey, selectedRows) => {
|
||||
selectedRowKeys.value = selectedRowKey
|
||||
}
|
||||
}
|
||||
}
|
||||
// 表格查询 返回 Promise 对象
|
||||
const loadData = (parameter) => {
|
||||
return pushApi.pushPage(Object.assign(parameter, searchFormState.value)).then((data) => {
|
||||
return data
|
||||
})
|
||||
}
|
||||
// 重置
|
||||
const reset = () => {
|
||||
searchFormRef.value.resetFields()
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const engineOptions = tool.dictList('PUSH_ENGINE')
|
||||
// 删除
|
||||
const deletePush = (record) => {
|
||||
let params = [
|
||||
{
|
||||
id: record.id
|
||||
}
|
||||
]
|
||||
pushApi.pushDelete(params).then(() => {
|
||||
tableRef.value.refresh(true)
|
||||
})
|
||||
}
|
||||
// 批量删除
|
||||
const deleteBatchPush = (params) => {
|
||||
pushApi.pushDelete(params).then(() => {
|
||||
tableRef.value.clearRefreshSelected()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.snowy-button-left {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
156
snowy-admin-web/src/views/dev/push/send/dingPushSend.vue
Normal file
156
snowy-admin-web/src/views/dev/push/send/dingPushSend.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-form-item label="推送方式:" name="pushType">
|
||||
<a-radio-group v-model:value="formData.pushType" button-style="solid" @change="pushTypeChange(formData.pushType)">
|
||||
<a-radio-button value="TEXT">文本消息</a-radio-button>
|
||||
<a-radio-button value="MARKDOWN">Markdown</a-radio-button>
|
||||
<a-radio-button value="LINK">LINK</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="通知类型:" name="noticeType" v-if="formData.pushType !== 'LINK'">
|
||||
<a-radio-group v-model:value="formData.noticeType">
|
||||
<a-radio value="NONE">无</a-radio>
|
||||
<a-radio value="PHONE" v-if="formData.pushType === 'TEXT'">指定手机号</a-radio>
|
||||
<a-radio value="ALL">通知所有人</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="消息标题:"
|
||||
name="title"
|
||||
v-if="formData.pushType === 'MARKDOWN' || formData.pushType === 'LINK'"
|
||||
>
|
||||
<a-input v-model:value="formData.title" placeholder="请输入消息标题" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="消息内容:" name="content" v-if="formData.pushType !== 'MARKDOWN'">
|
||||
<a-textarea
|
||||
v-model:value="formData.content"
|
||||
placeholder="请输入推送内容"
|
||||
:auto-size="{ minRows: 5, maxRows: 10 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="Markdown内容:" v-if="formData.pushType === 'MARKDOWN'">
|
||||
<XnMdEditor
|
||||
v-model="markdownContent"
|
||||
@upload-image="handleUploadImage"
|
||||
:disabled-menus="[]"
|
||||
height="500px"
|
||||
left-toolbar="undo redo clear | h bold italic strikethrough quote | ul ol table hr | link image"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图片URL:" name="picUrl" v-if="formData.pushType === 'LINK'">
|
||||
<a-input v-model:value="formData.picUrl" placeholder="请输入图片URL" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="跳转地址:" name="messageUrl" v-if="formData.pushType === 'LINK'">
|
||||
<a-input v-model:value="formData.messageUrl" placeholder="请输入跳转地址" allow-clear />
|
||||
</a-form-item>
|
||||
<div v-if="formData.pushType === 'TEXT'">
|
||||
<a-form-item label="手机号:" name="phones" v-if="formData.noticeType === 'PHONE'">
|
||||
<a-textarea
|
||||
v-model:value="formData.phones"
|
||||
placeholder="请输入手机号,多个逗号拼接"
|
||||
:auto-size="{ minRows: 2, maxRows: 5 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup name="DingPushSend">
|
||||
import { message } from 'ant-design-vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import pushApi from '@/api/dev/pushApi'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { XnMdEditor } from '@/components/XnMdEditor/mdEditor'
|
||||
import fileApi from '@/api/dev/fileApi'
|
||||
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({ loadingStart: null, loadingEnd: null })
|
||||
const formRef = ref()
|
||||
// 表单数据,也就是默认给一些数据
|
||||
const formData = ref({
|
||||
noticeType: 'NONE',
|
||||
pushType: 'TEXT'
|
||||
})
|
||||
const markdownContent = ref('')
|
||||
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
pushType: [required('请选择推送方式')],
|
||||
title: [required('请输入消息标题')],
|
||||
picUrl: [required('请输入图片URL')],
|
||||
messageUrl: [required('请输入跳转地址')],
|
||||
phones: [required('请输入手机号,多个逗号拼接')],
|
||||
noticeType: [required('请选择推送类型')],
|
||||
content: [required('请输入推送内容')]
|
||||
}
|
||||
// 切换消息类型
|
||||
const pushTypeChange = (value) => {
|
||||
if (value) {
|
||||
formData.value.noticeType = 'NONE'
|
||||
}
|
||||
}
|
||||
// 推送消息
|
||||
const send = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
emit('loadingStart')
|
||||
const formDataClone = cloneDeep(formData.value)
|
||||
if (formDataClone.pushType === 'TEXT') {
|
||||
pushApi
|
||||
.pushDingTalkText(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
} else if (formDataClone.pushType === 'MARKDOWN') {
|
||||
formData.value.content = markdownContent.value
|
||||
pushApi
|
||||
.pushDingTalkMarkdown(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
markdownContent.value = ''
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
} else if (formDataClone.pushType === 'LINK') {
|
||||
pushApi
|
||||
.pushDingTalkLink(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
} else {
|
||||
message.warning('不支持的推送类型')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
// md编辑器上传图片
|
||||
const handleUploadImage = (event, insertImage, files) => {
|
||||
const fileData = new FormData()
|
||||
fileData.append('file', files[0])
|
||||
fileApi.fileUploadLocalReturnUrl(fileData).then((data) => {
|
||||
// 上传成功后回填编辑器中
|
||||
insertImage({
|
||||
url: data,
|
||||
desc: files[0].name
|
||||
// width: 'auto',
|
||||
// height: 'auto'
|
||||
})
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
send
|
||||
})
|
||||
</script>
|
||||
58
snowy-admin-web/src/views/dev/push/send/feishuPushSend.vue
Normal file
58
snowy-admin-web/src/views/dev/push/send/feishuPushSend.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-form-item label="通知类型:" name="noticeType">
|
||||
<a-radio-group v-model:value="formData.noticeType">
|
||||
<a-radio value="NONE">无</a-radio>
|
||||
<a-radio value="ALL">通知所有人</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="推送内容:" name="content">
|
||||
<a-textarea
|
||||
v-model:value="formData.content"
|
||||
placeholder="请输入推送内容"
|
||||
:auto-size="{ minRows: 5, maxRows: 10 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup name="FeishuPushSend">
|
||||
import { message } from 'ant-design-vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import pushApi from '@/api/dev/pushApi'
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({ loadingStart: null, loadingEnd: null })
|
||||
const formRef = ref()
|
||||
// 表单数据,也就是默认给一些数据
|
||||
const formData = ref({
|
||||
noticeType: 'NONE'
|
||||
})
|
||||
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
content: [required('请输入推送内容')],
|
||||
noticeType: [required('请选择通知类型')]
|
||||
}
|
||||
// 发送短信
|
||||
const send = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
emit('loadingStart')
|
||||
pushApi
|
||||
.pushFeiShuText(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
send
|
||||
})
|
||||
</script>
|
||||
153
snowy-admin-web/src/views/dev/push/send/workWechatPushSend.vue
Normal file
153
snowy-admin-web/src/views/dev/push/send/workWechatPushSend.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-form-item label="推送方式:" name="pushType">
|
||||
<a-radio-group v-model:value="formData.pushType" button-style="solid" @change="pushTypeChange(formData.pushType)">
|
||||
<a-radio-button value="TEXT">文本消息</a-radio-button>
|
||||
<a-radio-button value="MARKDOWN">Markdown</a-radio-button>
|
||||
<a-radio-button value="NEWS">企业微信NEWS</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="通知类型:" name="noticeType" v-if="formData.pushType === 'TEXT'">
|
||||
<a-radio-group v-model:value="formData.noticeType">
|
||||
<a-radio value="NONE">无</a-radio>
|
||||
<a-radio value="PHONE" v-if="formData.pushType === 'TEXT'">指定手机号</a-radio>
|
||||
<a-radio value="ALL">通知所有人</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="消息标题:"
|
||||
name="title"
|
||||
v-if="formData.pushType === 'MARKDOWN' || formData.pushType === 'NEWS'"
|
||||
>
|
||||
<a-input v-model:value="formData.title" placeholder="请输入消息标题" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="消息内容:" name="content" v-if="formData.pushType !== 'MARKDOWN'">
|
||||
<a-textarea
|
||||
v-model:value="formData.content"
|
||||
placeholder="请输入推送内容"
|
||||
:auto-size="{ minRows: 5, maxRows: 10 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="Markdown内容:" v-if="formData.pushType === 'MARKDOWN'">
|
||||
<XnMdEditor
|
||||
v-model="markdownContent"
|
||||
@upload-image="handleUploadImage"
|
||||
:disabled-menus="[]"
|
||||
height="500px"
|
||||
left-toolbar="undo redo clear | h bold italic strikethrough quote | ul ol table hr | link image"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图片URL:" name="picUrl" v-if="formData.pushType === 'NEWS'">
|
||||
<a-input v-model:value="formData.picUrl" placeholder="请输入图片URL" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="跳转地址:" name="messageUrl" v-if="formData.pushType === 'NEWS'">
|
||||
<a-input v-model:value="formData.messageUrl" placeholder="请输入跳转地址" allow-clear />
|
||||
</a-form-item>
|
||||
<div v-if="formData.pushType === 'TEXT'">
|
||||
<a-form-item label="手机号:" name="phones" v-if="formData.noticeType === 'PHONE'">
|
||||
<a-textarea
|
||||
v-model:value="formData.phones"
|
||||
placeholder="请输入手机号,多个逗号拼接"
|
||||
:auto-size="{ minRows: 2, maxRows: 5 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup name="WorkWechatPushSend">
|
||||
import { message } from 'ant-design-vue'
|
||||
import { required } from '@/utils/formRules'
|
||||
import pushApi from '@/api/dev/pushApi'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { XnMdEditor } from '@/components/XnMdEditor/mdEditor'
|
||||
import fileApi from '@/api/dev/fileApi'
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({ loadingStart: null, loadingEnd: null })
|
||||
const formRef = ref()
|
||||
// 表单数据,也就是默认给一些数据
|
||||
const formData = ref({
|
||||
noticeType: 'NONE',
|
||||
pushType: 'TEXT'
|
||||
})
|
||||
const markdownContent = ref('')
|
||||
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
pushType: [required('请选择推送方式')],
|
||||
title: [required('请输入消息标题')],
|
||||
picUrl: [required('请输入图片URL')],
|
||||
messageUrl: [required('请输入跳转地址')],
|
||||
phones: [required('请输入手机号,多个逗号拼接')],
|
||||
noticeType: [required('请选择推送类型')],
|
||||
content: [required('请输入推送内容')]
|
||||
}
|
||||
// 切换消息类型
|
||||
const pushTypeChange = (value) => {
|
||||
if (value) {
|
||||
formData.value.noticeType = 'NONE'
|
||||
}
|
||||
}
|
||||
// 推送消息
|
||||
const send = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
emit('loadingStart')
|
||||
const formDataClone = cloneDeep(formData.value)
|
||||
if (formDataClone.pushType === 'TEXT') {
|
||||
pushApi
|
||||
.pushWorkWechatText(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
} else if (formDataClone.pushType === 'MARKDOWN') {
|
||||
formData.value.content = markdownContent.value
|
||||
pushApi
|
||||
.pushWorkWechatMarkdown(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
} else if (formDataClone.pushType === 'NEWS') {
|
||||
pushApi
|
||||
.pushWorkWechatNews(formData.value)
|
||||
.then(() => {
|
||||
message.success('推送成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
emit('loadingEnd')
|
||||
})
|
||||
} else {
|
||||
message.warning('不支持的推送类型')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
// md编辑器上传图片
|
||||
const handleUploadImage = (event, insertImage, files) => {
|
||||
const fileData = new FormData()
|
||||
fileData.append('file', files[0])
|
||||
fileApi.fileUploadLocalReturnUrl(fileData).then((data) => {
|
||||
// 上传成功后回填编辑器中
|
||||
insertImage({
|
||||
url: data,
|
||||
desc: files[0].name
|
||||
// width: 'auto',
|
||||
// height: 'auto'
|
||||
})
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
send
|
||||
})
|
||||
</script>
|
||||
@@ -21,13 +21,6 @@
|
||||
:auto-size="{ minRows: 3, maxRows: 5 }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="sdkAppId:" name="sdkAppId">
|
||||
<a-input
|
||||
v-model:value="formData.sdkAppId"
|
||||
placeholder="请输入在短信控制台添加应用后生成的实际SdkAppId"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="短信签名:" name="signName">
|
||||
<a-input v-model:value="formData.signName" placeholder="请输入短信签名" allow-clear />
|
||||
</a-form-item>
|
||||
|
||||
@@ -163,13 +163,13 @@
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
width: 220
|
||||
},
|
||||
{
|
||||
title: '组件',
|
||||
dataIndex: 'component',
|
||||
ellipsis: true,
|
||||
width: 150
|
||||
width: 220
|
||||
},
|
||||
{
|
||||
title: '是否可见',
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<a-form-item label="角色名称:" name="name">
|
||||
<a-input v-model:value="formData.name" placeholder="请输入角色名称" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="角色编码:" name="code">
|
||||
<a-input v-model:value="formData.code" placeholder="请输入角色编码" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="角色分类:" name="category">
|
||||
<a-select
|
||||
v-model:value="formData.category"
|
||||
@@ -66,9 +69,6 @@
|
||||
// 打开抽屉
|
||||
const onOpen = (record, category, orgId) => {
|
||||
visible.value = true
|
||||
formData.value = {
|
||||
sortCode: 99
|
||||
}
|
||||
// 判断角色的类型
|
||||
if (category) {
|
||||
formData.value.category = category
|
||||
@@ -79,6 +79,9 @@
|
||||
}
|
||||
if (record) {
|
||||
formData.value = Object.assign({}, record)
|
||||
} else {
|
||||
formData.value.sortCode = 99
|
||||
formData.value.code = tool.generateString(10)
|
||||
}
|
||||
// 获取机构树并加入顶级
|
||||
roleApi.roleOrgTreeSelector().then((res) => {
|
||||
@@ -93,6 +96,7 @@
|
||||
const formRules = {
|
||||
orgId: [required('请选择所属组织')],
|
||||
name: [required('请输入角色名称')],
|
||||
code: [required('请输入角色编码')],
|
||||
category: [required('请选择角色分类')],
|
||||
sortCode: [required('请选择排序')]
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
import { SearchOutlined } from '@ant-design/icons-vue'
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import ScopeDefineOrg from './scopeDefineOrg.vue'
|
||||
import { userStore } from '@/store/user'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const visible = ref(false)
|
||||
@@ -485,7 +485,7 @@
|
||||
emit('successful')
|
||||
// 刷新权限
|
||||
nextTick(() => {
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
useUserStore().refreshUserLoginUserInfo()
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<script setup name="grantResourceForm">
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { userStore } from '@/store/user'
|
||||
import { useUserStore } from '@/store/user'
|
||||
const spinningLoading = ref(false)
|
||||
const firstShowMap = ref({})
|
||||
const emit = defineEmits({ successful: null })
|
||||
@@ -299,7 +299,7 @@
|
||||
const refreshCache = () => {
|
||||
const menuStore = useMenuStore()
|
||||
menuStore.fetchMenu()
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
useUserStore().refreshUserLoginUserInfo()
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
import userApi from '@/api/sys/userApi'
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import ScopeDefineOrg from './scopeDefineOrg.vue'
|
||||
import { userStore } from '@/store/user'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const visible = ref(false)
|
||||
@@ -487,7 +487,7 @@
|
||||
emit('successful')
|
||||
// 刷新权限
|
||||
nextTick(() => {
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
useUserStore().refreshUserLoginUserInfo()
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
import userApi from '@/api/sys/userApi'
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { userStore } from '@/store/user'
|
||||
import { useUserStore } from '@/store/user'
|
||||
const spinningLoading = ref(false)
|
||||
const firstShowMap = ref({})
|
||||
const emit = defineEmits({ successful: null })
|
||||
@@ -299,7 +299,7 @@
|
||||
const refreshCache = () => {
|
||||
const menuStore = useMenuStore()
|
||||
menuStore.fetchMenu()
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
useUserStore().refreshUserLoginUserInfo()
|
||||
}
|
||||
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
<script setup>
|
||||
import { message } from 'ant-design-vue'
|
||||
import UpdatePassword from './bindForm/updatePassword.vue'
|
||||
// 按需导入图标组件
|
||||
import QqOutlined from '@ant-design/icons-vue/QqOutlined'
|
||||
import WechatOutlined from '@ant-design/icons-vue/WechatOutlined'
|
||||
import AlipayCircleOutlined from '@ant-design/icons-vue/AlipayCircleOutlined'
|
||||
|
||||
const updatePasswordRef = ref()
|
||||
// 获取绑定的情况
|
||||
|
||||
@@ -1,35 +1,104 @@
|
||||
<template>
|
||||
<xn-form-container title="修改密码" :width="550" :visible="visible" :destroy-on-close="true" @close="onClose">
|
||||
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
|
||||
<a-form-item label="旧密码:" name="password" has-feedback>
|
||||
<a-input
|
||||
v-model:value="formState.password"
|
||||
placeholder="请输入原密码"
|
||||
type="password"
|
||||
allow-clear
|
||||
autocomplete="off"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="新密码:" name="newPassword" has-feedback>
|
||||
<a-input
|
||||
v-model:value="formState.newPassword"
|
||||
placeholder="请输入新密码"
|
||||
type="password"
|
||||
allow-clear
|
||||
autocomplete="off"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button class="xn-mr8" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit">保存</a-button>
|
||||
</template>
|
||||
</xn-form-container>
|
||||
<div>
|
||||
<xn-form-container title="修改密码" :width="550" :visible="visible" :destroy-on-close="true" @close="onClose">
|
||||
<a-skeleton active v-if="!updatePasswordConfig" />
|
||||
<a-form v-else ref="formRef" :model="formState" :rules="formRules" layout="vertical">
|
||||
<div v-if="updatePasswordConfig.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'OLD'">
|
||||
<a-form-item label="旧密码:" name="password" has-feedback>
|
||||
<a-input-password
|
||||
v-model:value="formState.password"
|
||||
placeholder="请输入原密码"
|
||||
allow-clear
|
||||
autocomplete="off"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<a-form-item
|
||||
label="手机号:"
|
||||
name="phone"
|
||||
has-feedback
|
||||
v-if="updatePasswordConfig.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'PHONE'"
|
||||
>
|
||||
<a-input v-model:value="formState.phone" placeholder="请输入手机号" allow-clear autocomplete="off" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="邮箱号:"
|
||||
name="email"
|
||||
has-feedback
|
||||
v-if="updatePasswordConfig.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'EMAIL'"
|
||||
>
|
||||
<a-input v-model:value="formState.email" placeholder="请输入邮箱号" allow-clear autocomplete="off" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
name="validCode"
|
||||
v-if="
|
||||
updatePasswordConfig.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'PHONE' ||
|
||||
updatePasswordConfig.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'EMAIL'
|
||||
"
|
||||
>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="17">
|
||||
<a-input v-model:value="formState.validCode" placeholder="验证码">
|
||||
<template #prefix>
|
||||
<mail-outlined class="text-black text-opacity-25" />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<a-button class="xn-wd" @click="getValidCode" :disabled="state.sendBtn">
|
||||
{{ (!state.sendBtn && '获取验证码') || state.time + ' s' }}
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
<a-form-item label="新密码:" name="newPassword" has-feedback>
|
||||
<a-input-password
|
||||
v-model:value="formState.newPassword"
|
||||
placeholder="请输入新密码"
|
||||
allow-clear
|
||||
autocomplete="off"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button class="xn-mr8" @click="onClose">关闭</a-button>
|
||||
<a-button type="primary" :loading="submitLoading" @click="onSubmit">保存</a-button>
|
||||
</template>
|
||||
</xn-form-container>
|
||||
<a-modal
|
||||
v-model:open="captchaVisible"
|
||||
:width="400"
|
||||
title="验证"
|
||||
@cancel="captchaHandleCancel"
|
||||
@ok="captchaHandleOk"
|
||||
destroy-on-close
|
||||
>
|
||||
<a-form ref="updatePasswordFormModalRef" :model="captchaFormModalData" :rules="captchaFormModalRules">
|
||||
<a-form-item name="validCode">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="17">
|
||||
<a-input v-model:value="captchaFormModalData.validCode" placeholder="请输入验证码" size="large">
|
||||
<template #prefix>
|
||||
<verified-outlined class="xn-color-00025" />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<img :src="captchaValidCodeBase64" class="xn-findform-line" @click="getCaptcha" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="updatePassword">
|
||||
import { required } from '@/utils/formRules'
|
||||
import { required, rules } from '@/utils/formRules'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import smCrypto from '@/utils/smCrypto'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({ successful: null })
|
||||
@@ -39,41 +108,248 @@
|
||||
// 表单数据
|
||||
const formState = ref({})
|
||||
const submitLoading = ref(false)
|
||||
const updatePasswordConfig = ref({})
|
||||
|
||||
// 打开抽屉
|
||||
const onOpen = () => {
|
||||
visible.value = true
|
||||
// 获得密码策略配置
|
||||
userCenterApi.userGetUpdatePasswordValidConfig().then((data) => {
|
||||
updatePasswordConfig.value = data
|
||||
})
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
visible.value = false
|
||||
updatePasswordConfig.value = {}
|
||||
formRef.value.resetFields()
|
||||
}
|
||||
// 默认要校验的
|
||||
const rules = {
|
||||
const formRules = {
|
||||
password: [required('请输入原密码')],
|
||||
checkPassword: [required('请再次输入原密码')],
|
||||
newPassword: [required('请输入新密码')]
|
||||
phone: [required('请输入手机号'), rules.phone],
|
||||
phoneValidCode: [required('请输入手机验证码'), rules.number],
|
||||
email: [required('请输入邮箱号'), rules.email],
|
||||
emailValidCode: [required('请输入邮箱验证码'), rules.number],
|
||||
newPassword: [
|
||||
required('请输入新密码'),
|
||||
{
|
||||
validator: (rule, value) => {
|
||||
if (!value) return Promise.resolve()
|
||||
|
||||
// 检查密码长度
|
||||
const minLength = updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_MIN_LENGTH_FOR_B
|
||||
const maxLength = updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_MAX_LENGTH_FOR_B
|
||||
if (value.length < minLength) {
|
||||
return Promise.reject(`密码长度不能小于${minLength}位`)
|
||||
}
|
||||
if (value.length > maxLength) {
|
||||
return Promise.reject(`密码长度不能大于${maxLength}位`)
|
||||
}
|
||||
|
||||
// 检查连续相同字符
|
||||
const maxSameChar =
|
||||
updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_NOT_ALLOW_CONTINUOUS_SAME_CHARACTER_LENGTH_FOR_B
|
||||
let maxCount = 1
|
||||
let currentChar = value[0]
|
||||
let currentCount = 1
|
||||
for (let i = 1; i < value.length; i++) {
|
||||
if (value[i] === currentChar) {
|
||||
currentCount++
|
||||
maxCount = Math.max(maxCount, currentCount)
|
||||
} else {
|
||||
currentChar = value[i]
|
||||
currentCount = 1
|
||||
}
|
||||
}
|
||||
if (maxCount > maxSameChar) {
|
||||
return Promise.reject(`密码中不能包含${maxSameChar}个以上相同字符`)
|
||||
}
|
||||
|
||||
// 检查密码复杂度
|
||||
const complexity = updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_COMPLEXITY_FOR_B
|
||||
const hasNumber = /\d/.test(value)
|
||||
const hasLowerCase = /[a-z]/.test(value)
|
||||
const hasUpperCase = /[A-Z]/.test(value)
|
||||
const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(value)
|
||||
|
||||
switch (complexity) {
|
||||
case 'REG0':
|
||||
break
|
||||
case 'REG1':
|
||||
if (!(hasNumber && (hasLowerCase || hasUpperCase))) {
|
||||
return Promise.reject('密码必须包含数字和字母')
|
||||
}
|
||||
break
|
||||
case 'REG2':
|
||||
if (!(hasNumber && hasUpperCase)) {
|
||||
return Promise.reject('密码必须包含数字和大写字母')
|
||||
}
|
||||
break
|
||||
case 'REG3':
|
||||
if (!(hasNumber && hasUpperCase && hasLowerCase && hasSpecial)) {
|
||||
return Promise.reject('密码必须包含数字、大写字母、小写字母和特殊字符')
|
||||
}
|
||||
break
|
||||
case 'REG4':
|
||||
if ([hasNumber, hasLowerCase || hasUpperCase, hasSpecial].filter(Boolean).length < 2) {
|
||||
return Promise.reject('密码至少包含数字、字母和特殊字符中的两种')
|
||||
}
|
||||
break
|
||||
case 'REG5':
|
||||
if ([hasNumber, hasUpperCase, hasLowerCase, hasSpecial].filter(Boolean).length < 3) {
|
||||
return Promise.reject('密码至少包含数字、大写字母、小写字母和特殊字符的三种')
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let state = ref({
|
||||
time: 60,
|
||||
sendBtn: false
|
||||
})
|
||||
// 获取验证码
|
||||
const getValidCode = () => {
|
||||
formRef.value
|
||||
.validateFields([
|
||||
updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'PHONE' ? 'phone' : 'email'
|
||||
])
|
||||
.then(() => {
|
||||
captchaVisible.value = true
|
||||
getCaptcha()
|
||||
})
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
const onSubmit = async () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then((values) => {
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
userCenterApi
|
||||
.userUpdatePassword(values)
|
||||
.then(() => {
|
||||
formRef.value.resetFields()
|
||||
visible.value = false
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
const cloneFormData = cloneDeep(formState.value)
|
||||
if (updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'OLD') {
|
||||
cloneFormData.password = smCrypto.doSm2Encrypt(formState.value.password)
|
||||
cloneFormData.newPassword = smCrypto.doSm2Encrypt(formState.value.newPassword)
|
||||
userCenterApi
|
||||
.userUpdatePasswordByOld(cloneFormData)
|
||||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
} else if (updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'PHONE') {
|
||||
userCenterApi
|
||||
.userUpdatePasswordByPhone(cloneFormData)
|
||||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
} else if (updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'EMAIL') {
|
||||
userCenterApi
|
||||
.userUpdatePasswordByEmail(cloneFormData)
|
||||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
} else {
|
||||
submitLoading.value = false
|
||||
message.warning('未知的修改密码方式')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const captchaVisible = ref(false)
|
||||
const captchaFormModalData = ref({})
|
||||
const updatePasswordFormModalRef = ref()
|
||||
const captchaFormModalRules = {
|
||||
validCode: [required('图形验证码不能为空')]
|
||||
}
|
||||
const captchaValidCodeBase64 = ref('')
|
||||
// 获得图形验证码
|
||||
const getCaptcha = () => {
|
||||
userCenterApi.userGetPicCaptcha().then((data) => {
|
||||
if (data) {
|
||||
captchaValidCodeBase64.value = data.validCodeBase64
|
||||
formState.value.validCodeReqNo = data.validCodeReqNo
|
||||
}
|
||||
})
|
||||
}
|
||||
// 图形验证码窗口关闭
|
||||
const captchaHandleCancel = () => {
|
||||
captchaVisible.value = false
|
||||
captchaValidCodeBase64.value = ''
|
||||
}
|
||||
// 图形验证码提交
|
||||
const captchaHandleOk = () => {
|
||||
// 验证码
|
||||
updatePasswordFormModalRef.value.validate().then(() => {
|
||||
captchaVisible.value = false
|
||||
// 禁用发送按钮,并设置为倒计时
|
||||
state.value.sendBtn = true
|
||||
const interval = window.setInterval(() => {
|
||||
if (state.value.time-- <= 0) {
|
||||
state.value.time = 60
|
||||
state.value.sendBtn = false
|
||||
window.clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
const hide = message.loading('验证码发送中..', 0)
|
||||
const param = {
|
||||
validCodeReqNo: formState.value.validCodeReqNo,
|
||||
validCode: captchaFormModalData.value.validCode
|
||||
}
|
||||
if (updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'PHONE') {
|
||||
param.phone = formState.value.phone
|
||||
userCenterApi
|
||||
.userUpdatePasswordGetPhoneValidCode(param)
|
||||
.then((validCodeReqNo) => {
|
||||
formState.value.validCodeReqNo = validCodeReqNo
|
||||
setTimeout(hide, 500)
|
||||
})
|
||||
.catch(() => {
|
||||
setTimeout(hide, 100)
|
||||
clearInterval(interval)
|
||||
state.value.sendBtn = false
|
||||
})
|
||||
.finally(() => {
|
||||
captchaFormModalData.value.validCode = ''
|
||||
})
|
||||
} else if (updatePasswordConfig.value.SNOWY_SYS_DEFAULT_PASSWORD_UPDATE_VALID_TYPE_FOR_B === 'EMAIL') {
|
||||
param.email = formState.value.email
|
||||
userCenterApi
|
||||
.userUpdatePasswordGetEmailValidCode(param)
|
||||
.then((validCodeReqNo) => {
|
||||
formState.value.validCodeReqNo = validCodeReqNo
|
||||
setTimeout(hide, 500)
|
||||
})
|
||||
.catch(() => {
|
||||
setTimeout(hide, 100)
|
||||
clearInterval(interval)
|
||||
state.value.sendBtn = false
|
||||
})
|
||||
.finally(() => {
|
||||
captchaFormModalData.value.validCode = ''
|
||||
})
|
||||
} else {
|
||||
message.warning('未知的发送验证码方式')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
|
||||
@@ -28,16 +28,19 @@
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(tree-org-node__content) {
|
||||
background: var(--body-background);
|
||||
background: var(--snowy-background-color);
|
||||
}
|
||||
:deep(.tree-org) {
|
||||
padding-top: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.xn-tree-line {
|
||||
background: var(--card-actions-background);
|
||||
background: var(--snowy-background-color);
|
||||
}
|
||||
.xn-ht500 {
|
||||
height: 500px;
|
||||
}
|
||||
:deep(.zm-tree-handle .zm-tree-handle-item) {
|
||||
background: var(--snowy-background-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,7 +47,9 @@ export default defineConfig(({ command, mode }) => {
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias
|
||||
alias,
|
||||
// 优化依赖解析
|
||||
dedupe: ['vue', 'ant-design-vue']
|
||||
},
|
||||
// 解决警告You are running the esm-bundler build of vue-i18n.
|
||||
define: {
|
||||
@@ -57,19 +59,42 @@ export default defineConfig(({ command, mode }) => {
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
|
||||
},
|
||||
build: {
|
||||
// sourcemap: true,
|
||||
// 生产环境移除console
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
pure_funcs: ['console.log']
|
||||
}
|
||||
},
|
||||
// 禁用 gzip 压缩大小报告,因为压缩大型文件可能会很慢
|
||||
reportCompressedSize: true,
|
||||
// CSS代码分割,设为false将所有CSS合并到一个文件
|
||||
cssCodeSplit: true,
|
||||
// 启用源码映射用于调试
|
||||
sourcemap: command === 'serve',
|
||||
manifest: true,
|
||||
brotliSize: false,
|
||||
assetsInlineLimit: 4096,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// 静态资源分类打包
|
||||
chunkFileNames: 'static/js/[name]-[hash].js',
|
||||
entryFileNames: 'static/js/[name]-[hash].js',
|
||||
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
|
||||
manualChunks: {
|
||||
echarts: ['echarts'],
|
||||
'ant-design-vue': ['ant-design-vue'],
|
||||
vue: ['vue', 'vue-router', 'pinia', 'vue-i18n']
|
||||
// 第三方库分包配置保持不变
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia', 'vue-i18n'],
|
||||
'ant-design-vendor': ['ant-design-vue', '@ant-design/icons-vue', 'lodash-es', 'axios', 'dayjs'],
|
||||
'echarts-vendor': ['echarts', 'echarts-stat'],
|
||||
'editor-vendor': ['@tinymce/tinymce-vue', 'tinymce'],
|
||||
'office-vendor': ['@vue-office/docx', '@vue-office/excel', '@vue-office/pdf']
|
||||
}
|
||||
}
|
||||
},
|
||||
chunkSizeWarningLimit: 1000
|
||||
// 调整chunk大小警告限制
|
||||
chunkSizeWarningLimit: 2000
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
|
||||
Reference in New Issue
Block a user