2025-10-29 10:02:22 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div
|
|
|
|
|
|
:class="classObj"
|
|
|
|
|
|
class="app-wrapper"
|
|
|
|
|
|
:style="{ '--current-color': theme }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="device === 'mobile' && sidebar.opened"
|
|
|
|
|
|
class="drawer-bg"
|
|
|
|
|
|
@click="handleClickOutside"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
|
|
|
|
|
<div
|
|
|
|
|
|
:class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
|
|
|
|
|
|
class="main-container"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div :class="{ 'fixed-header': fixedHeader }">
|
|
|
|
|
|
<navbar />
|
|
|
|
|
|
<tags-view v-if="needTagsView" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<app-main />
|
|
|
|
|
|
<right-panel>
|
|
|
|
|
|
<settings />
|
|
|
|
|
|
</right-panel>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
:title="title"
|
|
|
|
|
|
:visible.sync="showChangePasswordDialog"
|
|
|
|
|
|
width="30%"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
:show-close="false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-form ref="form" :model="user" :rules="rules" label-width="80px">
|
|
|
|
|
|
<el-form-item label="旧密码" prop="oldPassword">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="user.oldPassword"
|
|
|
|
|
|
placeholder="请输入旧密码"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
show-password
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="新密码" prop="newPassword">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="user.newPassword"
|
|
|
|
|
|
placeholder="请输入新密码"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
show-password
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="确认密码" prop="confirmPassword">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="user.confirmPassword"
|
|
|
|
|
|
placeholder="请确认新密码"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
show-password
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
<div slot="footer" class="dialog-footer">
|
|
|
|
|
|
<el-button type="primary" @click="submit">确 定</el-button>
|
|
|
|
|
|
<el-button @click="close">取 消</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import RightPanel from '@/components/RightPanel'
|
|
|
|
|
|
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
|
|
|
|
|
|
import ResizeMixin from './mixin/ResizeHandler'
|
|
|
|
|
|
import { mapState } from 'vuex'
|
|
|
|
|
|
import variables from '@/assets/styles/variables.scss'
|
|
|
|
|
|
import { validateNewPassword } from '@/utils/validate'
|
|
|
|
|
|
import { updateUserPwd, checkPasswordStatus } from '@/api/system/user'
|
|
|
|
|
|
import { handleNoWarningLog } from '@/api/system/log'
|
|
|
|
|
|
import { MessageBox } from 'element-ui'
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'Layout',
|
|
|
|
|
|
data() {
|
|
|
|
|
|
const equalToPassword = (rule, value, callback) => {
|
|
|
|
|
|
if (this.user.newPassword !== value) {
|
|
|
|
|
|
callback(new Error('两次输入的密码不一致'))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
callback()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
|
|
|
showChangePasswordDialog: false, // 控制弹窗显示
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
user: {
|
|
|
|
|
|
oldPassword: undefined,
|
|
|
|
|
|
newPassword: undefined,
|
|
|
|
|
|
confirmPassword: undefined,
|
|
|
|
|
|
},
|
|
|
|
|
|
// 表单校验规则
|
|
|
|
|
|
rules: {
|
|
|
|
|
|
oldPassword: [
|
|
|
|
|
|
{
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
message: '旧密码不能为空',
|
|
|
|
|
|
trigger: 'blur',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
newPassword: [
|
|
|
|
|
|
{
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
message: '新密码不能为空',
|
|
|
|
|
|
trigger: 'blur',
|
|
|
|
|
|
},
|
|
|
|
|
|
{ validator: validateNewPassword, trigger: 'blur' },
|
|
|
|
|
|
],
|
|
|
|
|
|
confirmPassword: [
|
|
|
|
|
|
{
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
message: '确认密码不能为空',
|
|
|
|
|
|
trigger: 'blur',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
validator: equalToPassword,
|
|
|
|
|
|
trigger: 'blur',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
socket: null,
|
|
|
|
|
|
wsUrl: '', //'ws://localhost:18082/ws', // WebSocket 端点
|
|
|
|
|
|
isConnected: false, // 连接状态
|
|
|
|
|
|
reconnectInterval: 5000, // 自动重连时间间隔(毫秒
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
components: {
|
|
|
|
|
|
AppMain,
|
|
|
|
|
|
Navbar,
|
|
|
|
|
|
RightPanel,
|
|
|
|
|
|
Settings,
|
|
|
|
|
|
Sidebar,
|
|
|
|
|
|
TagsView,
|
|
|
|
|
|
},
|
|
|
|
|
|
mixins: [ResizeMixin],
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
...mapState({
|
|
|
|
|
|
theme: (state) => state.settings.theme,
|
|
|
|
|
|
sideTheme: (state) => state.settings.sideTheme,
|
|
|
|
|
|
sidebar: (state) => state.app.sidebar,
|
|
|
|
|
|
device: (state) => state.app.device,
|
|
|
|
|
|
needTagsView: (state) => state.settings.tagsView,
|
|
|
|
|
|
fixedHeader: (state) => state.settings.fixedHeader,
|
|
|
|
|
|
roles: (state) => state.user.roles,
|
|
|
|
|
|
}),
|
|
|
|
|
|
classObj() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
hideSidebar: !this.sidebar.opened,
|
|
|
|
|
|
openSidebar: this.sidebar.opened,
|
|
|
|
|
|
withoutAnimation: this.sidebar.withoutAnimation,
|
|
|
|
|
|
mobile: this.device === 'mobile',
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
variables() {
|
|
|
|
|
|
return variables
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
created() {
|
|
|
|
|
|
this.checkPasswordStatus()
|
|
|
|
|
|
// if (this.roles.includes("audit") || this.roles.includes("systemAdmin")) {
|
|
|
|
|
|
// this.connectWebSocket();
|
|
|
|
|
|
// }
|
|
|
|
|
|
this.handleNoWarningLog()
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
checkPasswordStatus() {
|
|
|
|
|
|
checkPasswordStatus().then((response) => {
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
|
this.showChangePasswordDialog = response.data
|
|
|
|
|
|
this.title = response.msg
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
handleNoWarningLog() {
|
|
|
|
|
|
handleNoWarningLog().then((response) => {})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
handleClickOutside() {
|
|
|
|
|
|
this.$store.dispatch('app/closeSideBar', {
|
|
|
|
|
|
withoutAnimation: false,
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
submit() {
|
|
|
|
|
|
this.$refs['form'].validate((valid) => {
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
|
updateUserPwd(
|
|
|
|
|
|
this.user.oldPassword,
|
|
|
|
|
|
this.user.newPassword,
|
|
|
|
|
|
).then((response) => {
|
|
|
|
|
|
this.showChangePasswordDialog = false
|
|
|
|
|
|
this.$modal.msgSuccess('修改成功')
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
close() {
|
|
|
|
|
|
this.$store.dispatch('LogOut').then(() => {
|
2025-11-06 15:12:09 +08:00
|
|
|
|
location.href =
|
|
|
|
|
|
process.env.VUE_APP_ENV === 'production'
|
|
|
|
|
|
? '/gs-realname/index'
|
|
|
|
|
|
: '/index'
|
2025-10-29 10:02:22 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 连接 WebSocket
|
|
|
|
|
|
connectWebSocket() {
|
|
|
|
|
|
if (this.socket) {
|
|
|
|
|
|
console.log('WebSocket 已连接')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log('WebSocket URL:{}', this.wsUrl)
|
|
|
|
|
|
this.socket = new WebSocket(this.wsUrl)
|
|
|
|
|
|
|
|
|
|
|
|
// 监听 WebSocket 连接成功事件
|
|
|
|
|
|
this.socket.onopen = () => {
|
|
|
|
|
|
console.log('WebSocket 连接成功')
|
|
|
|
|
|
this.isConnected = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 接收消息
|
|
|
|
|
|
this.socket.onmessage = (event) => {
|
|
|
|
|
|
console.log('收到消息:', event.data)
|
|
|
|
|
|
const warning = JSON.parse(event.data)
|
|
|
|
|
|
this.handleWarning(warning)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 监听连接关闭事件
|
|
|
|
|
|
this.socket.onclose = () => {
|
|
|
|
|
|
console.log('WebSocket 连接已关闭')
|
|
|
|
|
|
this.isConnected = false
|
|
|
|
|
|
this.socket = null
|
|
|
|
|
|
// 自动重连
|
|
|
|
|
|
this.reconnectWebSocket()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 监听连接错误事件
|
|
|
|
|
|
this.socket.onerror = (error) => {
|
|
|
|
|
|
console.error('WebSocket 错误:', error)
|
|
|
|
|
|
this.isConnected = false
|
|
|
|
|
|
this.socket = null
|
|
|
|
|
|
// 自动重连
|
|
|
|
|
|
this.reconnectWebSocket()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 自动重连 WebSocket
|
|
|
|
|
|
reconnectWebSocket() {
|
|
|
|
|
|
console.log('尝试重新连接 WebSocket...')
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.connectWebSocket()
|
|
|
|
|
|
}, this.reconnectInterval)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 处理告警信息并显示弹窗
|
|
|
|
|
|
handleWarning(warning) {
|
|
|
|
|
|
console.log(warning)
|
|
|
|
|
|
let warningContent = ''
|
|
|
|
|
|
|
|
|
|
|
|
if (warning.operaUserName) {
|
|
|
|
|
|
warningContent += `<p><strong>操作人:</strong>${warning.operaUserName}</p>`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (warning.warningEvent) {
|
|
|
|
|
|
warningContent += `<p><strong>事件:</strong>${warning.warningEvent}</p>`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (warning.warningIp) {
|
|
|
|
|
|
warningContent += `<p><strong>IP:</strong>${warning.warningIp}</p>`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (warning.operaTime) {
|
|
|
|
|
|
warningContent += `<p><strong>时间:</strong>${warning.operaTime}</p>`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (warningContent) {
|
|
|
|
|
|
MessageBox.alert(warningContent, '告警通知', {
|
|
|
|
|
|
dangerouslyUseHTMLString: true,
|
|
|
|
|
|
confirmButtonText: '确认',
|
|
|
|
|
|
customClass: 'custom-message-box',
|
|
|
|
|
|
callback: () => {
|
|
|
|
|
|
this.notifyBackend(warning.warningId)
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 通知后端告警已处理
|
|
|
|
|
|
notifyBackend(warningId) {
|
|
|
|
|
|
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
|
|
|
|
const message = {
|
|
|
|
|
|
warningId,
|
|
|
|
|
|
}
|
|
|
|
|
|
this.socket.send(warningId)
|
|
|
|
|
|
console.log(`已通知后端处理告警: ${warningId}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
// 页面销毁时关闭 WebSocket 连接
|
|
|
|
|
|
if (this.socket) {
|
|
|
|
|
|
this.socket.close()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
@import '~@/assets/styles/mixin.scss';
|
|
|
|
|
|
@import '~@/assets/styles/variables.scss';
|
|
|
|
|
|
|
|
|
|
|
|
.app-wrapper {
|
|
|
|
|
|
@include clearfix;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
&.mobile.openSidebar {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.drawer-bg {
|
|
|
|
|
|
background: #000;
|
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 999;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-header {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 9;
|
|
|
|
|
|
width: calc(100% - #{$base-sidebar-width});
|
|
|
|
|
|
transition: width 0.28s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hideSidebar .fixed-header {
|
|
|
|
|
|
width: calc(100% - 54px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sidebarHide .fixed-header {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile .fixed-header {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|