Merge branch 'refs/heads/main' into bonus-ai
This commit is contained in:
commit
98edaf421a
|
|
@ -60,7 +60,8 @@
|
||||||
"vue-meta": "2.4.0",
|
"vue-meta": "2.4.0",
|
||||||
"vue-router": "3.4.9",
|
"vue-router": "3.4.9",
|
||||||
"vuedraggable": "2.24.3",
|
"vuedraggable": "2.24.3",
|
||||||
"vuex": "3.6.0"
|
"vuex": "3.6.0",
|
||||||
|
"webstomp-client": "^1.2.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "4.4.6",
|
"@vue/cli-plugin-babel": "4.4.6",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
import ThemePicker from "@/components/ThemePicker";
|
import ThemePicker from "@/components/ThemePicker";
|
||||||
import { mapActions } from 'vuex'
|
import { mapActions } from 'vuex'
|
||||||
import { get } from '@/utils/config'
|
import { get } from '@/utils/config'
|
||||||
|
// import AlertNotification from "@/views/warning/AlertNotification.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export function getSysLogs(data) {
|
||||||
//备份系统日志列表
|
//备份系统日志列表
|
||||||
export function downloadSysLogs(data) {
|
export function downloadSysLogs(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/sys/sysLog/downloadSysLogs',
|
url: '/system/sys/sysLog/downloadSysLogs',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: data,
|
params: data,
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
|
|
@ -92,6 +92,12 @@ export function getLogSize(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function handleNoWarningLog(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/sys/sysLog/logWarn',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,6 @@
|
||||||
<el-form-item label="确认密码" prop="confirmPassword">
|
<el-form-item label="确认密码" prop="confirmPassword">
|
||||||
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
|
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item>
|
|
||||||
<el-button type="primary" size="mini" @click="submit">保存</el-button>
|
|
||||||
<el-button type="danger" size="mini" @click="close">关闭</el-button>
|
|
||||||
</el-form-item>-->
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button type="primary" @click="submit">确 定</el-button>
|
<el-button type="primary" @click="submit">确 定</el-button>
|
||||||
|
|
@ -46,6 +42,8 @@ import { mapState } from 'vuex'
|
||||||
import variables from '@/assets/styles/variables.scss'
|
import variables from '@/assets/styles/variables.scss'
|
||||||
import { validateNewPassword } from '@/utils/validate'
|
import { validateNewPassword } from '@/utils/validate'
|
||||||
import { updateUserPwd, checkPasswordStatus } from '@/api/system/user'
|
import { updateUserPwd, checkPasswordStatus } from '@/api/system/user'
|
||||||
|
import { handleNoWarningLog } from '@/api/system/log'
|
||||||
|
import {MessageBox} from "element-ui";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Layout',
|
name: 'Layout',
|
||||||
|
|
@ -78,7 +76,12 @@ export default {
|
||||||
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
|
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
|
||||||
{ required: true, validator: equalToPassword, trigger: 'blur' }
|
{ required: true, validator: equalToPassword, trigger: 'blur' }
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
socket: null,
|
||||||
|
wsUrl: JSON.parse(localStorage.getItem('systemConfig')).webSocketurl,//'ws://localhost:18082/ws', // WebSocket 端点
|
||||||
|
isConnected: false, // 连接状态
|
||||||
|
reconnectInterval: 5000 // 自动重连时间间隔(毫秒
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -97,7 +100,8 @@ export default {
|
||||||
sidebar: state => state.app.sidebar,
|
sidebar: state => state.app.sidebar,
|
||||||
device: state => state.app.device,
|
device: state => state.app.device,
|
||||||
needTagsView: state => state.settings.tagsView,
|
needTagsView: state => state.settings.tagsView,
|
||||||
fixedHeader: state => state.settings.fixedHeader
|
fixedHeader: state => state.settings.fixedHeader,
|
||||||
|
roles: state => state.user.roles,
|
||||||
}),
|
}),
|
||||||
classObj() {
|
classObj() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -113,6 +117,10 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.checkPasswordStatus()
|
this.checkPasswordStatus()
|
||||||
|
if (this.roles.includes("audit") || this.roles.includes("systemAdmin")) {
|
||||||
|
this.connectWebSocket();
|
||||||
|
}
|
||||||
|
this.handleNoWarningLog()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkPasswordStatus() {
|
checkPasswordStatus() {
|
||||||
|
|
@ -123,6 +131,13 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleNoWarningLog(){
|
||||||
|
handleNoWarningLog().then(response => {
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
handleClickOutside() {
|
handleClickOutside() {
|
||||||
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||||
},
|
},
|
||||||
|
|
@ -140,7 +155,112 @@ export default {
|
||||||
this.$store.dispatch('LogOut').then(() => {
|
this.$store.dispatch('LogOut').then(() => {
|
||||||
location.href = '/index';
|
location.href = '/index';
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 连接 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>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@ import errorCode from '@/utils/errorCode'
|
||||||
import { tansParams, blobValidate } from '@/utils/bonus'
|
import { tansParams, blobValidate } from '@/utils/bonus'
|
||||||
import cache from '@/plugins/cache'
|
import cache from '@/plugins/cache'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import { encryptCBC, decryptCBC } from '@/utils/aescbc'
|
|
||||||
import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm'
|
import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm'
|
||||||
const systemConfig = {
|
const systemConfig = JSON.parse(localStorage.getItem('systemConfig')) || {
|
||||||
requestConfig: { encryptRequest: false, checkIntegrity: false, encryptResponse: false }
|
requestConfig: { encryptRequest: false, checkIntegrity: false, encryptResponse: false }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -66,6 +65,8 @@ service.interceptors.request.use(config => {
|
||||||
if (contentType.includes('application/json') && typeof data !== 'undefined') {
|
if (contentType.includes('application/json') && typeof data !== 'undefined') {
|
||||||
// 加密数据
|
// 加密数据
|
||||||
if (systemConfig.requestConfig.encryptRequest && encryptRequest) {
|
if (systemConfig.requestConfig.encryptRequest && encryptRequest) {
|
||||||
|
console.log(data);
|
||||||
|
console.log(hashWithSM3AndSalt(data));
|
||||||
config.data = encryptWithSM4(data+"|"+hashWithSM3AndSalt(data))
|
config.data = encryptWithSM4(data+"|"+hashWithSM3AndSalt(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,7 @@ export function decryptWithSM2(encryptedText) {
|
||||||
* @returns {string} 加密后的密文(Hex 编码格式)
|
* @returns {string} 加密后的密文(Hex 编码格式)
|
||||||
*/
|
*/
|
||||||
export function encryptWithSM4(plainText) {
|
export function encryptWithSM4(plainText) {
|
||||||
const salt =SM_CONFIG.SM4_SALT
|
return sm4.encrypt(plainText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5',iv:SM_CONFIG.SM4_SALT});
|
||||||
return sm4.encrypt(plainText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5',iv:salt})+salt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,9 +45,6 @@ export function encryptWithSM4(plainText) {
|
||||||
* @returns {string} 解密后的明文
|
* @returns {string} 解密后的明文
|
||||||
*/
|
*/
|
||||||
export function decryptWithSM4(cipherText){
|
export function decryptWithSM4(cipherText){
|
||||||
const length = cipherText.length;
|
return SM4.decrypt(cipherText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5' ,iv:SM_CONFIG.SM4_SALT});
|
||||||
const salt = length > 32 ? cipherText.substring(length - 32) : cipherText;
|
|
||||||
const originalHex = length > 32 ? cipherText.substring(0, length - 32) : '';
|
|
||||||
return SM4.decrypt(originalHex, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5' ,iv:salt});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,16 +151,16 @@
|
||||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||||
<el-form-item label="参数名称" prop="configName">
|
<el-form-item label="参数名称" prop="configName">
|
||||||
<el-input v-model="form.configName" placeholder="请输入参数名称" />
|
<el-input v-model="form.configName" placeholder="请输入参数名称" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="参数键名" prop="configKey">
|
<el-form-item label="参数键名" prop="configKey">
|
||||||
<el-input v-model="form.configKey" placeholder="请输入参数键名" />
|
<el-input v-model="form.configKey" placeholder="请输入参数键名" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="参数键值" prop="configValue">
|
<el-form-item label="参数键值" prop="configValue">
|
||||||
<el-input v-model="form.configValue" placeholder="请输入参数键值" />
|
<el-input v-model="form.configValue" placeholder="请输入参数键值" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="系统内置" prop="configType">
|
<el-form-item label="系统内置" prop="configType">
|
||||||
<el-radio-group v-model="form.configType">
|
<el-radio-group v-model="form.configType" disabled>
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in dict.type.sys_yes_no"
|
v-for="dict in dict.type.sys_yes_no"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
|
|
@ -300,6 +300,11 @@ export default {
|
||||||
submitForm: function() {
|
submitForm: function() {
|
||||||
this.$refs["form"].validate(valid => {
|
this.$refs["form"].validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
if (this.form.configKey === "sys.visit.tokentime" && (this.form.configValue <=0 || this.form.configValue > 30 ))
|
||||||
|
{
|
||||||
|
this.$modal.msgError("系统访问token有效期必须在0-30分钟之间");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.form.configId != undefined) {
|
if (this.form.configId != undefined) {
|
||||||
updateConfig(this.form).then(response => {
|
updateConfig(this.form).then(response => {
|
||||||
this.$modal.msgSuccess("修改成功");
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@
|
||||||
<el-button style="margin-left: 20px" class="filter-item" @click="resetFilter">
|
<el-button style="margin-left: 20px" class="filter-item" @click="resetFilter">
|
||||||
重置
|
重置
|
||||||
</el-button>
|
</el-button>
|
||||||
<!-- <el-button @click="handleBackups" class="filter-item" style="margin-left: 20px" type="warning">
|
<el-button @click="handleBackups" class="filter-item" style="margin-left: 20px" type="warning">
|
||||||
备份
|
备份
|
||||||
</el-button> -->
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@
|
||||||
<el-button style="margin-left: 20px" class="filter-item" @click="resetFilter">
|
<el-button style="margin-left: 20px" class="filter-item" @click="resetFilter">
|
||||||
重置
|
重置
|
||||||
</el-button>
|
</el-button>
|
||||||
<!-- <el-button @click="handleBackups" class="filter-item" style="margin-left: 20px" type="warning">
|
<el-button @click="handleBackups" class="filter-item" style="margin-left: 20px" type="warning">
|
||||||
备份
|
备份
|
||||||
</el-button> -->
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
<el-radio-group v-model="listQuery.type" class="toptype">
|
<el-radio-group v-model="listQuery.type" class="toptype">
|
||||||
<el-radio label="1">日志类型</el-radio>
|
<el-radio label="1">日志类型</el-radio>
|
||||||
<el-radio label="2">操作类型</el-radio>
|
<el-radio label="2">操作类型</el-radio>
|
||||||
|
<el-radio label="3">操作人</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<el-button style="margin-left: 20px" class="filter-item" type="primary" @click="handleFilter">
|
<el-button style="margin-left: 20px" class="filter-item" type="primary" @click="handleFilter">
|
||||||
查询
|
查询
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
/>
|
/>
|
||||||
<span style="margin-left: 10px;">MB</span>
|
<span style="margin-left: 10px;">GB</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@
|
||||||
<el-button style="margin-left: 20px" class="filter-item" @click="resetFilter">
|
<el-button style="margin-left: 20px" class="filter-item" @click="resetFilter">
|
||||||
重置
|
重置
|
||||||
</el-button>
|
</el-button>
|
||||||
<!-- <el-button @click="handleBackups" class="filter-item" style="margin-left: 20px" type="warning">
|
<el-button @click="handleBackups" class="filter-item" style="margin-left: 20px" type="warning">
|
||||||
备份
|
备份
|
||||||
</el-button> -->
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
|
|
@ -125,7 +125,7 @@ export default {
|
||||||
listLoading: false,
|
listLoading: false,
|
||||||
tableHeight: 650,
|
tableHeight: 650,
|
||||||
operateList: operateList,
|
operateList: operateList,
|
||||||
timeList:[{id:1,name:'时间'},{id:2,name:'操作人'},{id:3,name:'操作模块'},{id:4,name:'ip'},],
|
timeList:[{id:1,name:'时间'},{id:2,name:'操作人'},{id:3,name:'操作模块'},{id:4,name:'ip'},{id:5,name:'操作类型'},],
|
||||||
sortList:[{id:1,name:'倒序'},{id:2,name:'升序'}],
|
sortList:[{id:1,name:'倒序'},{id:2,name:'升序'}],
|
||||||
listQuery: {
|
listQuery: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange" :selectable="checkSelectable" :row-class-name="getRowClassName">
|
||||||
<el-table-column type="selection" min-width="55" align="center"/>
|
<el-table-column type="selection" min-width="55" align="center"/>
|
||||||
<el-table-column label="角色编号" prop="roleId" min-width="120" align="center"/>
|
<el-table-column label="角色编号" prop="roleId" min-width="120" align="center"/>
|
||||||
<el-table-column label="角色名称" align="center" prop="roleName" :show-overflow-tooltip="true" min-width="150"/>
|
<el-table-column label="角色名称" align="center" prop="roleName" :show-overflow-tooltip="true" min-width="150"/>
|
||||||
|
|
@ -123,7 +123,7 @@
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope" v-if="scope.row.roleId !== 1">
|
<template slot-scope="scope" v-if="scope.row.roleId !== 1 && scope.row.isBuiltIn !== '0'">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -476,10 +476,15 @@ export default {
|
||||||
},
|
},
|
||||||
// 多选框选中数据
|
// 多选框选中数据
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
this.ids = selection.map(item => item.roleId)
|
// 过滤掉不可选的行(虽然界面上不可选,但为了安全起见还是过滤一次)
|
||||||
this.single = selection.length != 1
|
const validSelection = selection.filter(row => this.checkSelectable(row));
|
||||||
this.multiple = !selection.length
|
|
||||||
},
|
// 更新选中的roleId数组
|
||||||
|
this.ids = validSelection.map(item => item.roleId);
|
||||||
|
// 更新单选和多选状态
|
||||||
|
this.single = validSelection.length !== 1;
|
||||||
|
this.multiple = !validSelection.length;
|
||||||
|
},
|
||||||
// 更多操作触发
|
// 更多操作触发
|
||||||
handleCommand(command, row) {
|
handleCommand(command, row) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
|
@ -626,7 +631,33 @@ export default {
|
||||||
this.download('system/role/export', {
|
this.download('system/role/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `role_${new Date().getTime()}.xlsx`)
|
}, `role_${new Date().getTime()}.xlsx`)
|
||||||
}
|
},
|
||||||
|
// 检查行是否可选
|
||||||
|
checkSelectable(row) {
|
||||||
|
return !(row.roleId === 1 || row.isBuiltIn === '0');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getRowClassName(row) {
|
||||||
|
return !this.checkSelectable(row) ? 'disabled-row' : '';
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.disabled-row {
|
||||||
|
background-color: #f5f7fa !important;
|
||||||
|
color: #909399;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-row .el-checkbox__input {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-row:hover td {
|
||||||
|
background-color: #f5f7fa !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<h4 class="form-header h4">角色信息</h4>
|
<h4 class="form-header h4">角色信息</h4>
|
||||||
<el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="table" @selection-change="handleSelectionChange" :data="roles.slice((pageNum-1)*pageSize,pageNum*pageSize)">
|
<el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="table" @selection-change="handleSelectionChange" :data="filteredRoles.slice((pageNum - 1) * pageSize, pageNum * pageSize)">
|
||||||
<el-table-column label="序号" type="index" align="center">
|
<el-table-column label="序号" type="index" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>
|
<span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>
|
||||||
|
|
@ -113,5 +113,12 @@ export default {
|
||||||
this.$tab.closeOpenPage(obj);
|
this.$tab.closeOpenPage(obj);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
filteredRoles() {
|
||||||
|
// 超级管理员不允许分配给其他人
|
||||||
|
return this.roles.filter(role => role.roleId !== 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange" :selectable="checkSelectable" :row-class-name="getRowClassName">
|
||||||
<el-table-column type="selection" width="50" align="center"/>
|
<el-table-column type="selection" width="50" align="center"/>
|
||||||
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible"/>
|
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible"/>
|
||||||
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible"
|
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible"
|
||||||
|
|
@ -97,7 +97,11 @@
|
||||||
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber"
|
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber"
|
||||||
v-if="columns[4].visible"
|
v-if="columns[4].visible"
|
||||||
width="120"
|
width="120"
|
||||||
/>
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ hidePhone(scope.row.phonenumber) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
|
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
|
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
|
||||||
|
|
@ -123,7 +127,7 @@
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope" v-if="scope.row.userId !== 1">
|
<template slot-scope="scope" v-if="!hasSystemOrAuditrRole(scope.row.roles) && scope.row.userId !== 1 && scope.row.isBuiltIn !== '0'">
|
||||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="confirmPassword(scope.row)"
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="confirmPassword(scope.row)"
|
||||||
v-hasPermi="['system:user:edit']"
|
v-hasPermi="['system:user:edit']"
|
||||||
>修改
|
>修改
|
||||||
|
|
@ -244,7 +248,7 @@
|
||||||
:key="item.roleId"
|
:key="item.roleId"
|
||||||
:label="item.roleName"
|
:label="item.roleName"
|
||||||
:value="item.roleId"
|
:value="item.roleId"
|
||||||
:disabled="item.status == 1"
|
:disabled="item.status == 1 || item.roleId === 1"
|
||||||
></el-option>
|
></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -470,6 +474,11 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/* 手机号码脱敏 */
|
||||||
|
hidePhone(phone) {
|
||||||
|
if (!phone) return '';
|
||||||
|
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||||
|
},
|
||||||
/* 表单登录权限自定义校验 */
|
/* 表单登录权限自定义校验 */
|
||||||
validateLoginType(rule, value, callback) {
|
validateLoginType(rule, value, callback) {
|
||||||
if (this.loginTypeArr.length > 0) {
|
if (this.loginTypeArr.length > 0) {
|
||||||
|
|
@ -575,15 +584,20 @@ export default {
|
||||||
},
|
},
|
||||||
// 多选框选中数据
|
// 多选框选中数据
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
this.ids = selection.map(item => item.userId)
|
// 过滤掉不可选的行(虽然界面上不可选,但为了安全起见还是过滤一次)
|
||||||
this.single = selection.length != 1
|
const validSelection = selection.filter(row => this.checkSelectable(row));
|
||||||
this.multiple = !selection.length
|
|
||||||
|
// 更新选中的roleId数组
|
||||||
|
this.ids = validSelection.map(item => item.userId);
|
||||||
|
// 更新单选和多选状态
|
||||||
|
this.single = validSelection.length !== 1;
|
||||||
|
this.multiple = !validSelection.length;
|
||||||
},
|
},
|
||||||
// 更多操作触发
|
// 更多操作触发
|
||||||
handleCommand(command, row) {
|
handleCommand(command, row) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 'handleResetPwd':
|
case 'handleResetPwd':
|
||||||
this.handleResetPwd(row)
|
this.confirmResetPwd(row)
|
||||||
break
|
break
|
||||||
case 'handleAuthRole':
|
case 'handleAuthRole':
|
||||||
this.handleAuthRole(row)
|
this.handleAuthRole(row)
|
||||||
|
|
@ -653,6 +667,32 @@ export default {
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
confirmResetPwd(row){
|
||||||
|
this.$prompt('请输入密码,鉴别用户', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
inputPattern: /^.{8,16}$/,
|
||||||
|
inputErrorMessage: '用户密码长度必须介于 8 和 16 之间',
|
||||||
|
inputValidator: (value) => {
|
||||||
|
// 调用 validateNewPassword 校验
|
||||||
|
const errorMessage=function(error) {
|
||||||
|
if (error) {
|
||||||
|
return error.message;
|
||||||
|
} else {
|
||||||
|
console.log('验证通过');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
validateNewPassword(null, value, errorMessage);
|
||||||
|
}
|
||||||
|
}).then(({ value }) => {
|
||||||
|
confirmPassword(value).then(response => {
|
||||||
|
this.$modal.msgSuccess('验证成功')
|
||||||
|
this.handleResetPwd(row)
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
/** 重置密码按钮操作 */
|
/** 重置密码按钮操作 */
|
||||||
handleResetPwd(row) {
|
handleResetPwd(row) {
|
||||||
this.$prompt('请输入"' + row.userName + '"的新密码', '提示', {
|
this.$prompt('请输入"' + row.userName + '"的新密码', '提示', {
|
||||||
|
|
@ -747,7 +787,41 @@ export default {
|
||||||
// 提交上传文件
|
// 提交上传文件
|
||||||
submitFileForm() {
|
submitFileForm() {
|
||||||
this.$refs.upload.submit()
|
this.$refs.upload.submit()
|
||||||
}
|
},
|
||||||
|
|
||||||
|
hasSystemOrAuditrRole(roles) {
|
||||||
|
if (!roles || !Array.isArray(roles)) {
|
||||||
|
return false; // 如果 roles 为空或不是数组,返回 false
|
||||||
|
}
|
||||||
|
return roles.some(role => role.roleKey === 'systemAdmin' || role.roleKey === 'audit'); // 检查是否存在 roleKey 为 admin 的角色
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查行是否可选
|
||||||
|
checkSelectable(row) {
|
||||||
|
return !(row.userId === 1 || row.isBuiltIn === '0' || this.hasSystemOrAuditrRole(row.roles));
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getRowClassName(row) {
|
||||||
|
return !this.checkSelectable(row) ? 'disabled-row' : '';
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.disabled-row {
|
||||||
|
background-color: #f5f7fa !important;
|
||||||
|
color: #909399;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-row .el-checkbox__input {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-row:hover td {
|
||||||
|
background-color: #f5f7fa !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<h2>告警系统</h2>
|
||||||
|
<p v-if="isConnected">WebSocket 已连接</p>
|
||||||
|
<p v-else>WebSocket 连接中...</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { MessageBox } from 'element-ui';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
socket: null,
|
||||||
|
wsUrl: 'ws://localhost:18082/ws', // WebSocket 端点
|
||||||
|
isConnected: false, // 连接状态
|
||||||
|
reconnectInterval: 5000 // 自动重连时间间隔(毫秒)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 页面加载时自动连接 WebSocket
|
||||||
|
this.connectWebSocket();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 连接 WebSocket
|
||||||
|
connectWebSocket() {
|
||||||
|
if (this.socket) {
|
||||||
|
console.log("WebSocket 已连接");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const { warningEvent, warningContent, warningGrade, warningIp, warningTime } = warning;
|
||||||
|
const formattedTime = new Date(warningTime).toLocaleString();
|
||||||
|
|
||||||
|
// 弹出告警信息
|
||||||
|
MessageBox.alert(
|
||||||
|
`
|
||||||
|
<p><strong>事件:</strong>${warningEvent}</p>
|
||||||
|
<p><strong>内容:</strong>${warningContent}</p>
|
||||||
|
<p><strong>等级:</strong>${warningGrade}</p>
|
||||||
|
<p><strong>IP:</strong>${warningIp}</p>
|
||||||
|
<p><strong>时间:</strong>${formattedTime}</p>
|
||||||
|
`,
|
||||||
|
'告警通知',
|
||||||
|
{
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
callback: () => {
|
||||||
|
this.notifyBackend(warning.warningId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 通知后端告警已处理
|
||||||
|
notifyBackend(warningId) {
|
||||||
|
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||||
|
const message = {
|
||||||
|
warningId,
|
||||||
|
status: '1' // 1 表示已处理
|
||||||
|
};
|
||||||
|
this.socket.send(JSON.stringify(message));
|
||||||
|
console.log(`已通知后端处理告警: ${warningId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 页面销毁时关闭 WebSocket 连接
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#app {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue