优化页面

This commit is contained in:
lj124 2026-04-17 20:42:21 +08:00
parent 146b071737
commit 47032dd19d
7 changed files with 1980 additions and 1624 deletions

41
package-lock.json generated
View File

@ -23,7 +23,8 @@
"vue": "^3.3.11",
"vue-cookies": "^1.8.3",
"vue-qr": "^4.0.9",
"vue-router": "^4.2.5"
"vue-router": "^4.2.5",
"vue-virtual-scroller": "^2.0.0-beta.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.6.2",
@ -1687,6 +1688,12 @@
"node": ">=10"
}
},
"node_modules/mitt": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==",
"license": "MIT"
},
"node_modules/mpd-parser": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.3.0.tgz",
@ -2269,6 +2276,15 @@
"resolved": "https://registry.npmjs.org/vue-cookies/-/vue-cookies-1.8.3.tgz",
"integrity": "sha512-VBRsyRMVdahBgFfh389TMHPmDdr4URDJNMk4FKSCfuNITs7+jitBDhwyL4RJd3WUsfOYNNjPAkfbehyH9AFuoA=="
},
"node_modules/vue-observe-visibility": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz",
"integrity": "sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-qr": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/vue-qr/-/vue-qr-4.0.9.tgz",
@ -2280,6 +2296,15 @@
"string-split-by": "^1.0.0"
}
},
"node_modules/vue-resize": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
"integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-router": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
@ -2294,6 +2319,20 @@
"vue": "^3.2.0"
}
},
"node_modules/vue-virtual-scroller": {
"version": "2.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-2.0.0-beta.8.tgz",
"integrity": "sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==",
"license": "MIT",
"dependencies": {
"mitt": "^2.1.0",
"vue-observe-visibility": "^2.0.0-alpha.1",
"vue-resize": "^2.0.0-alpha.1"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",

View File

@ -24,7 +24,8 @@
"vue": "^3.3.11",
"vue-cookies": "^1.8.3",
"vue-qr": "^4.0.9",
"vue-router": "^4.2.5"
"vue-router": "^4.2.5",
"vue-virtual-scroller": "^2.0.0-beta.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.6.2",

View File

@ -1,6 +1,7 @@
<script>
import axios from "axios";
import { inject } from "vue";
import { ElMessage } from "element-plus";
import { Refresh, Plus, Edit, Delete, View } from "@element-plus/icons-vue";
import { getCIDLogListService } from "@/api/cid.js";
import { runCIDService } from "@/api/cid.js";
import { addCIDService } from "@/api/cid.js";
@ -8,8 +9,14 @@ import { deleteCIDService } from "@/api/cid.js";
import { updateCIDService } from "@/api/cid.js";
import router from "@/router/index.js";
export default {
components: {
Refresh,
Plus,
Edit,
Delete,
View
},
data() {
return {
ip: "",
@ -32,6 +39,7 @@ export default {
script: "",
end: "",
error: "",
RunTime: ""
},
};
},
@ -50,9 +58,9 @@ export default {
try {
this.tokenData.id = parseInt(this.tokenData.id);
result = await getCIDLogListService(this.tokenData);
this.loading = false;
} catch (e) {
console.log(e);
} finally {
this.loading = false;
}
let data = result.data;
@ -62,8 +70,7 @@ export default {
this.tableData = data;
for(let i = 0;i<this.tableData.length;i++) {
this.tableData[i].UpdatedAt = this.formattedTime(this.tableData[i].UpdatedAt);
this.tableData[i].CreatedAt = this.formattedTime(this.tableData[i].CreatedAt)
//console.log('this.ConfigFileList:',this.ConfigFileList);
this.tableData[i].CreatedAt = this.formattedTime(this.tableData[i].CreatedAt);
}
this.handleCurrentChange(1);
},
@ -83,6 +90,7 @@ export default {
},
handleSizeChange(val) {
this.pageSize = val;
this.handleCurrentChange(this.currentPage);
},
handleCurrentChange(val) {
this.currentPage = val;
@ -104,9 +112,9 @@ export default {
try {
var d_re = await runCIDService(run_data);
if (d_re.code == 0) {
alert("操作成功");
ElMessage.success("操作成功");
} else {
alert("操作失败");
ElMessage.error("操作失败");
}
} catch (e) {
console.log(e);
@ -122,11 +130,10 @@ export default {
try {
var d_re = await deleteCIDService(delete_data);
if (d_re.code == 0) {
alert("删除成功");
//
this.getDeviceList();
ElMessage.success("删除成功");
this.getCIDLogList();
} else {
alert("操作失败");
ElMessage.error("操作失败");
}
} catch (e) {
console.log(e);
@ -134,12 +141,6 @@ export default {
},
async updateButtonLogCID(index) {
this.updateDialogVisible = true;
// var id = this.tableData[index].ID;
// this.updateForm.create_time = this.tableData[index].CreatedAt;
// this.updateForm.script = this.tableData[index].Script;
// this.updateForm.end = this.tableData[index].Log;
// this.updateForm.error = this.tableData[index].Error;
// this.updateForm.RunTime = this.tableData[index].RunTime;
var id = this.currentPageData[index].ID;
this.updateForm.create_time = this.currentPageData[index].CreatedAt;
this.updateForm.script = this.currentPageData[index].Script;
@ -152,7 +153,6 @@ export default {
const response = await axios.get("https://ipinfo.io/json");
this.ip = response.data.ip;
localStorage.setItem("ip", this.ip);
//console.log(response);
} catch (error) {
console.error(error);
}
@ -182,7 +182,6 @@ export default {
if (localStorage.getItem("token") === null) {
router.push("/login");
}
// console.log("mounted");
this.getIpClient();
this.getCIDLogList();
},
@ -190,133 +189,76 @@ export default {
</script>
<template>
<div>
<el-container style="min-height: calc(100vh - 40px); background: #fff; border-radius: 4px;">
<el-header style="font-size: 40px; background-color: rgb(238, 241, 246)"
>集成部署项目日志{{ tokenData.id }}</el-header
>
<el-container>
<el-main>
<!-- 表单 -->
<el-form :inline="true" :model="tokenData" class="demo-form-inline">
<el-form-item>
<div class="page-container">
<el-card class="page-card" shadow="never">
<template #header>
<div class="card-header">
<div class="header-left">
<span class="page-title">集成部署项目日志 {{ tokenData.id }}</span>
</div>
<div class="header-actions">
<el-button @click="getCIDLogList()" :icon="Refresh">
刷新
</el-button>
</div>
</div>
</template>
<div class="table-wrapper">
<el-table
:data="currentPageData"
style="width: 100%"
v-loading="loading"
element-loading-text="数据加载中..."
element-loading-background="rgba(240, 242, 245, 0.8)"
stripe
:row-class-name="tableRowClassName"
>
<el-table-column prop="ID" label="ID" width="80"></el-table-column>
<el-table-column
prop="CreatedAt"
label="创建时间"
min-width="160"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="Script"
label="执行脚本"
min-width="180"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="Log"
label="运行日志"
min-width="180"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="Error"
label="错误信息"
min-width="250"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="RunTime"
label="运行时间(s)"
width="100"
></el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="scope">
<el-button
class="el-button--danger"
type="primary"
@click="getCIDLogList()"
:disabled="loading"
>刷新</el-button
size="small"
@click.prevent="updateButtonLogCID(scope.$index)"
:icon="View"
>
</el-form-item>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<el-form-item>
<el-dialog
v-model="updateDialogVisible"
title="查看集成项目日志"
width="50%"
:before-close="handleClose"
>
<!-- 内容主体区域 -->
<el-form
ref="addFormRef"
:model="updateForm"
:rules="updateFormRules"
label-width="70px"
>
<el-form-item label="创建时间" prop="create_time">
<el-input
v-model="updateForm.create_time"
autocomplete="on"
style="width: 600px"
></el-input>
</el-form-item>
<el-col>
<el-form-item label="执行时间" prop="run_time">
<el-input
v-model="updateForm.RunTime"
autocomplete="on"
style="width: 100px"
></el-input>
</el-form-item>
</el-col>
<el-form-item label="脚本内容" prop="script">
<el-input type="textarea" v-model="updateForm.script" style="width: 700px" :autosize="{ minRows: 4, maxRows: 8 }" />
</el-form-item>
<el-form-item label="执行结果" prop="end">
<el-input type="textarea" v-model="updateForm.end" style="width: 700px" :autosize="{ minRows: 4, maxRows: 8 }" />
</el-form-item>
<el-col>
<el-form-item label="错误内容" prop="error">
<el-input type="textarea" v-model="updateForm.error" style="width: 700px" :autosize="{ minRows: 4, maxRows: 8 }" />
</el-form-item>
</el-col>
</el-form>
<!-- 底部区域 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="updateDialogVisible = false"
>关闭</el-button
>
</span>
</template>
</el-dialog>
</el-form-item>
</el-form>
<!-- 表格 :row-style="this.tableRowClassName"-->
<el-table
:data="currentPageData"
width="100%"
v-loading="loading"
element-loading-text="数据加载中..."
>
:row-style="this.tableRowClassName"
<el-table-column prop="ID" label="id" width="80"></el-table-column>
<el-table-column
prop="CreatedAt"
label="创建时间"
show-overflow-tooltip
width="200"
></el-table-column>
<el-table-column
prop="Script"
label="执行脚本"
width="180"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="Log"
label="运行日志"
width="180"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="Error"
label="错误信息"
width="250"
></el-table-column>
<el-table-column
prop="RunTime"
label="运行时间(s)"
width="80"
></el-table-column>
<el-table-column label="操作" width="350">
<template #default="scope">
<el-button
type="primary"
size="mini"
@click.prevent="updateButtonLogCID(scope.$index)"
>详情</el-button
>
<!-- <el-button type="danger" size="mini">删除</el-button> -->
</template>
</el-table-column>
</el-table>
<br />
<!-- 分页条 -->
<div class="pagination-wrapper">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@ -324,15 +266,148 @@ export default {
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.length">
:total="tableData.length"
>
</el-pagination>
</el-main>
</el-container>
</el-container>
</div>
</div>
</el-card>
<!-- 查看日志对话框 -->
<el-dialog
v-model="updateDialogVisible"
title="查看集成项目日志"
width="70%"
:close-on-click-modal="false"
top="5vh"
>
<el-form
ref="addFormRef"
:model="updateForm"
label-width="100px"
class="form-dialog"
>
<el-row gutter="20">
<el-col :span="12">
<el-form-item label="创建时间">
<el-input
v-model="updateForm.create_time"
autocomplete="on"
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行时间">
<el-input
v-model="updateForm.RunTime"
autocomplete="on"
disabled
></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="脚本内容">
<el-input type="textarea" v-model="updateForm.script" :rows="4" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="执行结果">
<el-input type="textarea" v-model="updateForm.end" :rows="4" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="错误内容">
<el-input type="textarea" v-model="updateForm.error" :rows="4" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部区域 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="updateDialogVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style>
.blueRowbg {
background: "#488aff";
<style scoped>
.page-container {
width: 100%;
height: 100%;
}
</style>
.page-card {
height: 100%;
display: flex;
flex-direction: column;
border: none;
border-radius: 8px;
}
.page-card :deep(.el-card__body) {
flex: 1;
overflow: hidden;
padding: 20px;
display: flex;
flex-direction: column;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
display: flex;
align-items: center;
}
.page-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
.table-wrapper {
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
}
.table-wrapper :deep(.el-table) {
border-radius: 4px;
}
.pagination-wrapper {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.form-dialog {
padding: 10px 0;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
<script>
import router from "@/router/index.js";
import { autoResizerProps, ElMessage } from "element-plus";
import { ElMessage } from "element-plus";
import CryptoJS from "crypto-js";
import { Refresh, Plus, Edit, Delete, Document } from "@element-plus/icons-vue";
import { getConfigFileListService } from "@/api/file.js";
import { addConfigFileService } from "@/api/file.js";
@ -10,6 +10,13 @@ import { deleteConfigFileService } from "@/api/file.js";
import { updateConfigFileService } from "@/api/file.js";
export default {
components: {
Refresh,
Plus,
Edit,
Delete,
Document
},
data() {
return {
ip: "",
@ -22,13 +29,13 @@ export default {
ConfigFileCurrentPageData: [],
pageSize: 10,
currentPage: 1,
upload_file: null, //
file_md5: "", //md5
upload_file: null,
file_md5: "",
addForm: {
file_name: "",
file_path: "",
},
loading: false, //
loading: false,
server_list: [
{ label: "js.ljsea.top", value: "js.ljsea.top" },
{ label: "tx.ljsea.top", value: "tx.ljsea.top" },
@ -36,7 +43,6 @@ export default {
{ label: "as.ljsea.top", value: "as.ljsea.top" },
{ label: 'al.ljsea.top', value: "al.ljsea.top" }
],
role: "",
tokenData: {
token: localStorage.getItem("token"),
@ -50,14 +56,11 @@ export default {
};
},
// methods
//
methods: {
async getConfigFileList() {
this.loading = true;
let result = {};
try {
//search_id
let req = {
token: this.tokenData.token,
type: "all",
@ -67,7 +70,6 @@ export default {
if (data !== undefined && data !== null) {
this.tableData = data;
}
} catch (e) {
console.log(e);
ElMessage.error("获取文件列表失败");
@ -76,8 +78,7 @@ export default {
}
for(let i = 0;i<this.tableData.length;i++) {
this.tableData[i].UpdatedAt = this.formattedTime(this.tableData[i].UpdatedAt);
this.tableData[i].CreatedAt = this.formattedTime(this.tableData[i].CreatedAt)
//console.log('this.ConfigFileList:',this.ConfigFileList);
this.tableData[i].CreatedAt = this.formattedTime(this.tableData[i].CreatedAt);
}
this.currentPageData();
},
@ -103,6 +104,10 @@ export default {
ElMessage.success("添加成功");
this.getConfigFileList();
this.addConfigFileVisible = false;
this.addForm = {
file_name: "",
file_path: "",
};
} else {
ElMessage.error("添加失败");
}
@ -114,7 +119,6 @@ export default {
}
},
async deleteConfigFile(index) {
//
let isDelete = confirm("是否删除?");
if (!isDelete) {
return;
@ -156,33 +160,6 @@ export default {
this.ConfigFileUpdateForm = data[0];
this.updateDialogVisible = true;
},
handleAvatarFileUpload(e) {
this.avatar_file = e.target.files[0];
//
if (!this.avatar_file.type.startsWith("image/")) {
ElMessage.error("请选择图片文件");
this.avatar_file = null;
return;
}
this.uploadAvatarFile();
},
readFileAndCalculateMD5() {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const wordArray = CryptoJS.lib.WordArray.create(event.target.result);
const md5Hash = CryptoJS.MD5(wordArray);
const md5Str = md5Hash.toString(CryptoJS.enc.Hex);
//console.log("onload: " + md5Str);
this.file_md5 = md5Str;
resolve(md5Str);
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsArrayBuffer(this.avatar_file);
});
},
formattedTime(isoTime) {
const date = new Date(isoTime);
const year = date.getFullYear();
@ -194,51 +171,6 @@ export default {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
async uploadFile() {
if (this.avatar_file == null) {
alert("请先选择要上传的文件");
return;
}
try {
let result = {};
this.file_md5 = await this.readFileAndCalculateMD5(this.avatar_file);
//console.log("md5:",this.file_md5);
let md5_result = await GetFileInfoByMd5Service({
md5: this.file_md5,
token: this.tokenData.token,
type: 1,
});
if (md5_result.code === 0) {
result = md5_result;
} else {
let formData = new FormData();
formData.append("file", this.avatar_file);
//console.log("add file: " + this.file);
formData.append("upload_type", "1");
formData.append("md5", this.file_md5);
formData.append("auth_type", "public");
//console.log("formData:",formData);
result = await UploadFileService(formData, this.tokenData.token);
if (result.code !== 0) {
ElMessage.error("上传文件失败,请稍后再试");
return;
}
}
let resp_data = result.data;
//console.log("resp:",resp_data);
let url = "https://gep.ljsea.top/tool/file/" + resp_data.FileStoreName;
this.UserUpdateForm.avatar = url;
//
await this.updateUserInfo();
} catch (error) {
ElMessage.error("上传文件时出现网络错误,请稍后再试");
console.error(error);
}
},
async updateConfigFileInfo() {
this.loading = true;
let result = {};
@ -248,9 +180,6 @@ export default {
id: this.ConfigFileUpdateForm.id,
content: this.ConfigFileUpdateForm.content,
};
for (var key in this.UserUpdateForm) {
req[key] = this.UserUpdateForm[key];
}
result = await updateConfigFileService(req);
if (result.code === 0) {
ElMessage.success("更新成功");
@ -265,7 +194,6 @@ export default {
this.loading = false;
}
},
onSubmit() {
getConfigFileList();
},
@ -276,32 +204,19 @@ export default {
);
},
handleSizeChange(val) {
//console.log(` ${val} `);
this.pageSize = val;
this.currentPageData();
},
handleCurrentChange(val) {
//console.log(`: ${val}`);
this.currentPage = val;
this.currentPageData();
},
async displayMyInfo() {
await this.getMyUserInfo(this.tokenData.user_id);
this.updateDialogVisible = true;
},
async displayUserInfo(id) {
await this.getMyUserInfo(id);
this.updateDialogVisible = true;
},
handleMenuSelect(val) {
router.push(val);
},
toVideoList() {
router.push("/videoList");
},
//
tableRowClassName({ row, rowIndex }) {
if (row.human === 1) {
return {
@ -313,244 +228,246 @@ export default {
},
},
//
//
async mounted() {
let now = new Date();
if (localStorage.getItem("token") === null) {
router.push("/login");
}
await this.getConfigFileList();
//await this.getMyUserInfo(localStorage.getItem("userId"));
},
};
</script>
<template>
<div>
<el-container style="min-height: calc(100vh - 40px); background: #fff; border-radius: 4px;">
<el-header style="font-size: 28px; font-weight: 500; background-color: #f5f7fa; display: flex; align-items: center; padding: 0 20px; border-bottom: 1px solid #e4e7ed;"
>配置文件管理</el-header
>
<el-container>
<el-main>
<el-form :inline="true" :model="tokenData" class="demo-form-inline">
<el-form-item>
<el-dialog
v-model="updateDialogVisible"
title="编辑配置文件"
width="70%"
:before-close="handleClose"
top="3vh"
:close-on-click-modal="false"
>
<!-- 内容主体区域 -->
<el-form
ref="updateFormRef"
:model="ConfigFileUpdateForm"
:rules="UserUpdateFormRules"
label-width="90px"
>
<!-- row -->
<el-row gutter="20">
<el-col :span="12">
<el-form-item label="ID">
<el-input
v-model="ConfigFileUpdateForm.id"
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="文件名称">
<el-input
v-model="ConfigFileUpdateForm.file_name"
disabled
></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="文件路径">
<el-input
v-model="ConfigFileUpdateForm.file_path"
disabled
></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="文件内容" prop="device_status">
<el-input
type="textarea"
v-model="ConfigFileUpdateForm.content"
style="font-family: 'Consolas', 'Monaco', monospace; font-size: 14px; line-height: 1.6; min-height: 650px; width: 800px;"
:autosize="{ minRows: 20, maxRows: 60 }"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部区域 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="updateDialogVisible = false"
>取消</el-button
>
<el-button type="primary" @click="updateConfigFileInfo()"
>确定</el-button
>
</span>
</template>
</el-dialog>
</el-form-item>
<!-- 表单 -->
<el-form :inline="true" :model="tokenData" class="demo-form-inline">
<el-form-item>
<el-button
type="primary"
@click="getConfigFileList()"
>刷新列表</el-button
>
</el-form-item>
<el-form-item>
<el-button
type="success"
@click="addConfigFileV()"
>添加配置文件</el-button
>
</el-form-item>
<!-- 选择服务器 -->
<el-form-item>
<el-select
v-model="tokenData.server"
filterable allow-create
@change="handleServerChange"
>
<el-option v-for="item in server_list" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
</el-form>
<el-dialog
v-model="addConfigFileVisible"
title="添加配置文件"
width="600px"
:before-close="handleClose"
top="15vh"
>
<!-- 内容主体区域 -->
<el-form
ref="addFormRef"
:model="addForm"
:rules="addFormRules"
label-width="90px"
style="padding: 20px 10px;"
<div class="page-container">
<el-card class="page-card" shadow="never">
<template #header>
<div class="card-header">
<div class="header-left">
<span class="page-title">配置文件管理</span>
</div>
<div class="header-actions">
<el-select
v-model="tokenData.server"
filterable
allow-create
@change="handleServerChange"
style="width: 200px;"
placeholder="选择服务器"
>
<el-form-item label="文件名称" prop="file_name">
<el-input
v-model="addForm.file_name"
autocomplete="on"
placeholder="请输入文件名称"
></el-input>
</el-form-item>
<el-form-item label="文件路径" prop="file_path">
<el-input
v-model="addForm.file_path"
autocomplete="on"
placeholder="请输入文件完整路径"
></el-input>
</el-form-item>
</el-form>
<!-- 底部区域 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="addConfigFileVisible = false"
>取消</el-button
>
<el-button type="primary" @click="addConfigFile()"
>确定</el-button
>
</span>
</template>
</el-dialog>
<el-option v-for="item in server_list" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<el-button type="primary" @click="addConfigFileV()" :icon="Plus">
添加配置文件
</el-button>
<el-button @click="getConfigFileList()" :icon="Refresh">
刷新列表
</el-button>
</div>
</div>
</template>
<!-- 表格 :row-style="this.tableRowClassName"-->
<el-table
:data="ConfigFileCurrentPageData"
width="100%"
v-loading="loading"
element-loading-text="加载中..."
element-loading-background="rgba(240, 242, 245, 0.8)"
border
stripe
style="margin-top: 20px;"
>
:row-style="this.tableRowClassName"
<el-table-column prop="ID" label="id" width="80"></el-table-column>
<el-table-column
prop="FileName"
label="文件名称"
width="120"
></el-table-column>
<el-table-column
prop="FilePath"
label="文件路径"
width="180"
></el-table-column>
<el-table-column
prop="CreatedAt"
label="创建时间"
width="160"
></el-table-column>
<el-table-column
prop="UpdatedAt"
label="上次更新"
width="160"
></el-table-column>
<el-table-column
prop="AuthID"
label="创建用户ID"
width="100"
></el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button
type="primary"
size="mini"
@click.prevent="updateConfigFile(scope.$index)"
>编辑</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="deleteConfigFile(scope.$index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 分页条 -->
<!-- Pagination 分页 -->
<div class="table-wrapper">
<el-table
:data="ConfigFileCurrentPageData"
style="width: 100%"
v-loading="loading"
element-loading-text="加载中..."
element-loading-background="rgba(240, 242, 245, 0.8)"
stripe
:row-class-name="tableRowClassName"
>
<el-table-column prop="ID" label="ID" width="80"></el-table-column>
<el-table-column prop="FileName" label="文件名称" min-width="150"></el-table-column>
<el-table-column prop="FilePath" label="文件路径" min-width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="CreatedAt" label="创建时间" min-width="160"></el-table-column>
<el-table-column prop="UpdatedAt" label="上次更新" min-width="160"></el-table-column>
<el-table-column prop="AuthID" label="创建用户ID" width="120"></el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click.prevent="updateConfigFile(scope.$index)" :icon="Edit">
编辑
</el-button>
<el-button type="danger" size="small" @click.prevent="deleteConfigFile(scope.$index)" :icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-wrapper">
<el-pagination
background
layout="total,sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 50, 100]"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:total="10"
:total="tableData.length"
></el-pagination>
</el-main>
</el-container>
</el-container>
</div>
</div>
</el-card>
<el-dialog
v-model="addConfigFileVisible"
title="添加配置文件"
width="500px"
:close-on-click-modal="false"
top="15vh"
>
<el-form
ref="addFormRef"
:model="addForm"
label-width="90px"
class="form-dialog"
>
<el-form-item label="文件名称">
<el-input
v-model="addForm.file_name"
autocomplete="on"
placeholder="请输入文件名称"
></el-input>
</el-form-item>
<el-form-item label="文件路径">
<el-input
v-model="addForm.file_path"
autocomplete="on"
placeholder="请输入文件完整路径"
></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="addConfigFileVisible = false">取消</el-button>
<el-button type="primary" @click="addConfigFile()">确定</el-button>
</div>
</template>
</el-dialog>
<el-dialog
v-model="updateDialogVisible"
title="编辑配置文件"
width="70%"
top="3vh"
:close-on-click-modal="false"
>
<el-form
ref="updateFormRef"
:model="ConfigFileUpdateForm"
label-width="90px"
class="form-dialog"
>
<el-row gutter="20">
<el-col :span="12">
<el-form-item label="ID">
<el-input v-model="ConfigFileUpdateForm.id" disabled></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="文件名称">
<el-input v-model="ConfigFileUpdateForm.file_name" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="文件路径">
<el-input v-model="ConfigFileUpdateForm.file_path" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="文件内容">
<el-input
type="textarea"
v-model="ConfigFileUpdateForm.content"
style="font-family: 'Consolas', 'Monaco', monospace; font-size: 14px; line-height: 1.6;"
:autosize="{ minRows: 20, maxRows: 40 }"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="updateDialogVisible = false">取消</el-button>
<el-button type="primary" @click="updateConfigFileInfo()">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped>
.blueRowbg {
background: "#488aff";
.page-container {
width: 100%;
height: 100%;
}
</style>
.page-card {
height: 100%;
display: flex;
flex-direction: column;
border: none;
border-radius: 8px;
}
.page-card :deep(.el-card__body) {
flex: 1;
overflow: hidden;
padding: 20px;
display: flex;
flex-direction: column;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
display: flex;
align-items: center;
}
.page-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
.table-wrapper {
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
}
.table-wrapper :deep(.el-table) {
border-radius: 4px;
}
.pagination-wrapper {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.form-dialog {
padding: 10px 0;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
</style>

View File

@ -1,82 +1,75 @@
<template>
<div style="padding: 10px; margin-bottom: 50px">
<el-button
type="primary"
size="mini"
@click.prevent="handleMenuSelect('/User')"
>用户</el-button
>
<el-row v-loading="loading" element-loading-text="正在连接....">
<el-col :span="16">
<div
style="
width: 800px;
margin: 0 auto;
background-color: white;
border-radius: 5px;
box-shadow: 0 0 10px #ccc;
"
>
<div style="text-align: center; line-height: 50px">
{{ chatUser }}
<div class="page-container">
<el-card class="page-card" shadow="never">
<template #header>
<div class="card-header">
<div class="header-left">
<span class="page-title">聊天窗口 - {{ chatUser }}</span>
</div>
<div
style="height: 350px; overflow: auto; border-top: 1px solid #ccc"
v-html="content"
></div>
<div style="height: 200px">
<textarea
v-model="text"
style="
height: 160px;
width: 100%;
padding: 20px;
border: none;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
outline: none;
"
></textarea>
<div style="text-align: right; padding-right: 10px">
<el-button type="primary" size="mini" @click="send"
>发送</el-button
>
</div>
<div class="header-actions">
<el-button type="primary" size="small" @click.prevent="handleMenuSelect('/user')" :icon="User">
返回用户列表
</el-button>
</div>
</div>
</el-col>
</el-row>
</template>
<div class="chat-container" v-loading="loading" element-loading-text="正在连接...">
<div class="chat-header">
<div class="chat-user-info">
<el-avatar size="small" :src="circleUrl" style="margin-right: 8px;"></el-avatar>
<span class="chat-user-name">{{ chatUser }}</span>
</div>
</div>
<div class="chat-messages" v-html="content"></div>
<div class="chat-input-area">
<el-input
v-model="text"
type="textarea"
:rows="4"
placeholder="请输入消息..."
resize="none"
></el-input>
<div class="chat-actions">
<el-button type="primary" @click="send">
发送
</el-button>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script>
import { ref, onMounted, inject, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted } from "vue";
import { User } from "@element-plus/icons-vue";
import { getImKeyService } from "@/api/im.js";
import router from "@/router/index.js";
import * as crypto from "crypto";
import CryptoJS from "crypto-js";
import { ElLoading } from "element-plus";
import { ElMessage } from 'element-plus';
import Cookies from "js-cookie";
export default {
components: {
User
},
data() {
return {
circleUrl:
"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
user: {},
isCollapse: false,
users: [{ username: "admin" }, { username: "zhang" }],
chatUser: "2002",
circleUrl: "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
chatUser: "",
text: "",
messages: [],
session: "",
loading: ref(true),
loading: true,
imKey: "testimkey",
socket: null,
content: "",
to_user_id: 0,
to_user_name: "",
cnt: 0,
intervalId: null,
timerId: null, // ID
tokenData: {
token: localStorage.getItem("token"),
ip: localStorage.getItem("ip"),
@ -84,31 +77,27 @@ export default {
username: localStorage.getItem("username"),
to_user_id: this.to_user_id,
},
intervalId: ref(null),
timerId: null, // ID
};
},
methods: {
async send() {
if (!this.text) {
this.$message({ type: "warning", message: "请输入内容" });
ElMessage({ type: "warning", message: "请输入内容" });
} else {
if (typeof WebSocket == "undefined") {
console.log("您的浏览器不支持WebSocket");
ElMessage.error("您的浏览器不支持WebSocket");
} else {
var aesEnc = await this.aesEncrypt(this.text, this.imKey, this.imKey);
let data =
'{"type":"msg","data":"' +
aesEnc +
'","to_user_id":' +
this.to_user_id +
',"from_user_id":' +
this.tokenData.userId +
',"session":"' +
this.session +
'"}';
this.socket.send(data); // json
this.messages.push(JSON.parse(data));
let data = {
type: "msg",
data: aesEnc,
to_user_id: this.to_user_id,
from_user_id: this.tokenData.userId,
session: this.session
};
this.socket.send(JSON.stringify(data)); // json
this.messages.push(data);
//
this.createContent(null, this.tokenData.username, this.text);
this.text = "";
@ -143,11 +132,7 @@ export default {
if (this.cnt > 10) {
//
this.stopInterval();
//alert("");
ElMessage({
message: "连接失败,请重试!",
type: "error",
})
ElMessage.error("连接失败,请重试!");
router.push("/user");
}
return;
@ -164,7 +149,7 @@ export default {
data.expire
);
this.session = data.im_session;
this.loading=false
this.loading = false;
//
this.stopInterval();
this.connectWebSocket();
@ -173,7 +158,6 @@ export default {
if (this.cnt > 30) {
//
this.stopInterval();
//confirm("");
ElMessage.error("连接失败,请重试!");
router.push("/user");
}
@ -188,36 +172,40 @@ export default {
//
if (nowUser) {
// nowUser 绿
html =
'<div class="el-row" style="padding: 5px 0">\n' +
' <div class="el-col el-col-22" style="text-align: right; padding-right: 10px">\n' +
' <div class="tip left">' +
text +
"</div>\n" +
" </div>\n" +
' <div class="el-col el-col-2">\n' +
' <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +
' <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" style="object-fit: cover;">\n' +
" </span>\n" +
" </div>\n" +
"</div>";
html = `
<div class="message-row message-row-right">
<div class="message-content message-content-right">
${text}
</div>
<div class="message-avatar">
<img src="${this.circleUrl}" alt="Avatar">
</div>
</div>
`;
} else if (remoteUser) {
// remoteUser
html =
'<div class="el-row" style="padding: 5px 0">\n' +
' <div class="el-col el-col-2" style="text-align: right">\n' +
' <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +
' <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" style="object-fit: cover;">\n' +
" </span>\n" +
" </div>\n" +
' <div class="el-col el-col-22" style="text-align: left; padding-left: 10px">\n' +
' <div class="tip right">' +
text +
"</div>\n" +
" </div>\n" +
"</div>";
html = `
<div class="message-row message-row-left">
<div class="message-avatar">
<img src="${this.circleUrl}" alt="Avatar">
</div>
<div class="message-content message-content-left">
${text}
</div>
</div>
`;
}
this.content += html;
//
this.scrollToBottom();
},
scrollToBottom() {
setTimeout(() => {
const chatMessages = document.querySelector('.chat-messages');
if (chatMessages) {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
}, 100);
},
aesEncrypt(text, password, iv) {
// CryptoJS使WordArrayBufferIVWordArray
@ -246,9 +234,6 @@ export default {
const ivData = CryptoJS.enc.Utf8.parse(iv);
// Base64
// CryptoJS.enc.Base64.parse WordArrayBase64
// Base64使 CryptoJS.enc.Base64.parse(CryptoJS.enc.Base64.stringify(someWordArray))
// 使encryptedDataBase64
try {
const decrypted = CryptoJS.AES.decrypt(encryptedData, key, {
iv: ivData,
@ -275,15 +260,15 @@ export default {
let _this = this;
if (typeof WebSocket == "undefined") {
console.log("浏览器不支持WebSocket");
ElMessage.error("浏览器不支持WebSocket");
} else {
console.log("浏览器支持WebSocket");
let socketUrl =
let socketUrl =
"wss://tx.ljsea.top/im/ws?to_user_id=" +
this.to_user_id +
"&token=" +
this.tokenData.token;
// console.log("socketUrl:", socketUrl);
if (this.socket != null) {
this.socket.close();
this.socket = null;
@ -292,31 +277,22 @@ export default {
this.socket = new WebSocket(socketUrl);
//
this.socket.onopen = function () {
this.loading=false
//alert("");
ElMessage({
message: "连接成功",
type: "success",
})
_this.loading = false;
ElMessage.success("连接成功");
};
this.socket.onerror = (error) => {
console.error("WebSocket Error:", error);
_this.loading = false;
ElMessage.error("连接出错");
};
//
this.socket.onclose = function () {
//alert("!");
ElMessage({
message: "连接已关闭",
type: "error",
})
ElMessage.error("连接已关闭");
router.push("/user");
};
//
this.socket.onmessage = async function (msg) {
//console.log("====" + msg.data);
let data = JSON.parse(msg.data); // json
//console.log("====" + data);
// json
let data = JSON.parse(msg.data); // json
if (data.type == "msg") { //
data.data = await _this.decryptAES(
data.data,
@ -324,15 +300,10 @@ export default {
_this.imKey
);
_this.messages.push(data);
//console.log("====" + msg.data);
//
_this.createContent(_this.to_user_name, null, data.data);
}else if (data.type == "offline"){
//alert("线");
ElMessage({
message: "对方已下线",
type: "error",
})
} else if (data.type == "offline") {
ElMessage.error("对方已下线");
_this.socket.close();
router.push("/user");
}
@ -344,38 +315,191 @@ export default {
mounted() {
this.to_user_id = localStorage.getItem("to_user_id");
this.to_user_name = localStorage.getItem("to_user_name");
this.chatUser = this.to_user_name;
//console.log("to_user_id:", this.to_user_id, this.to_user_name);
this.chatUser = this.to_user_name || "未知用户";
this.startInterval();
//setTimeout(() => this.loading=false, 3000);
//this.connectWebSocket();
},
// ,
onUnmounted() {
this.stopInterval();
this.socket.close();
if (this.socket) {
this.socket.close();
}
this.loading = false;
},
};
</script>
<style>
.tip {
color: white;
text-align: center;
border-radius: 10px;
font-family: sans-serif;
padding: 10px;
width: auto;
display: inline-block !important;
display: inline;
<style scoped>
.page-container {
width: 100%;
height: 100%;
}
.right {
background-color: deepskyblue;
.page-card {
height: 100%;
display: flex;
flex-direction: column;
border: none;
border-radius: 8px;
}
.left {
background-color: forestgreen;
.page-card :deep(.el-card__body) {
flex: 1;
overflow: hidden;
padding: 20px;
display: flex;
flex-direction: column;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
display: flex;
align-items: center;
}
.page-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
background: #f5f7fa;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.chat-header {
background: #ffffff;
padding: 16px 20px;
border-bottom: 1px solid #e4e7ed;
display: flex;
align-items: center;
}
.chat-user-info {
display: flex;
align-items: center;
}
.chat-user-name {
font-size: 16px;
font-weight: 500;
color: #303133;
}
.chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
}
.message-row {
display: flex;
align-items: flex-end;
gap: 12px;
}
.message-row-left {
justify-content: flex-start;
}
.message-row-right {
justify-content: flex-end;
}
.message-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.message-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.message-content {
max-width: 70%;
padding: 10px 14px;
border-radius: 18px;
word-wrap: break-word;
line-height: 1.4;
}
.message-content-left {
background: #ffffff;
border-bottom-left-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.message-content-right {
background: #409eff;
color: #ffffff;
border-bottom-right-radius: 4px;
box-shadow: 0 1px 2px rgba(64, 158, 255, 0.3);
}
.chat-input-area {
background: #ffffff;
padding: 16px;
border-top: 1px solid #e4e7ed;
display: flex;
flex-direction: column;
gap: 12px;
}
.chat-input-area :deep(.el-textarea) {
width: 100%;
}
.chat-input-area :deep(.el-textarea__inner) {
border-radius: 8px;
resize: none;
}
.chat-actions {
display: flex;
justify-content: flex-end;
}
/* 滚动条样式 */
.chat-messages::-webkit-scrollbar {
width: 6px;
}
.chat-messages::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.chat-messages::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.chat-messages::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>

View File

@ -1,7 +1,7 @@
<script>
import router from "@/router/index.js";
import { ElMessage } from "element-plus";
import { Refresh, Plus, Edit, Delete, Monitor, Operation } from "@element-plus/icons-vue";
import { getConfigShellListService } from "@/api/shell.js";
import { addConfigShellService } from "@/api/shell.js";
@ -12,6 +12,14 @@ import { UpdateMonitorDeviceInfoService } from "@/api/tool.js";
import { DelMonitorDeviceInfoService } from "@/api/tool.js";
export default {
components: {
Refresh,
Plus,
Edit,
Delete,
Monitor,
Operation
},
data() {
return {
ip: "",
@ -26,8 +34,8 @@ export default {
ConfigFileCurrentPageData: [],
pageSize: 10,
currentPage: 1,
upload_file: null, //
file_md5: "", //md5
upload_file: null,
file_md5: "",
addForm: {
shell_name: "",
shel_content: "",
@ -36,7 +44,6 @@ export default {
serverList: [
{ label: "家里服务器", value: "home_server" },
{ label: "腾讯服务器", value: "tx_vp_server" },
// { label: "", value: "aliyun_vp_server" },
{ label:"azure服务器", value:"azure_vp_server" },
{ label: '阿里云成都服务器', value: "aliyun_chengdu_vp_server"}
],
@ -44,7 +51,6 @@ export default {
monitor_add_update_visible: false,
monitor_update_add: {},
role: "",
tokenData: {
token: localStorage.getItem("token"),
@ -58,14 +64,11 @@ export default {
};
},
// methods
//
methods: {
async getConfigFileList() {
let result = {};
this.loading = true;
try {
//search_id
let req = {
token: this.tokenData.token,
type: "all",
@ -82,10 +85,8 @@ export default {
}
for(let i = 0;i<this.tableData.length;i++) {
this.tableData[i].UpdatedAt = this.formattedTime(this.tableData[i].UpdatedAt);
this.tableData[i].CreatedAt = this.formattedTime(this.tableData[i].CreatedAt)
//console.log('this.ConfigFileList:',this.ConfigFileList);
this.tableData[i].CreatedAt = this.formattedTime(this.tableData[i].CreatedAt);
}
this.currentPageData();
},
addConfigFileV() {
@ -110,8 +111,12 @@ export default {
ElMessage.success("添加成功");
this.getConfigFileList();
this.addConfigFileVisible = false;
this.addForm = {
shell_name: "",
shell_content: "",
server: ""
};
} else {
//alert("");
ElMessage.error("添加失败");
}
} catch (e) {
@ -121,10 +126,6 @@ export default {
async updateConfigShellInfo() {
let result = {};
try {
let d={}
// for (var key in this.ConfigShellUpdateForm) {
// d[key] = this.ConfigShellUpdateForm[key];
// }
let req = {
token: this.tokenData.token,
shells: [{"id":this.ConfigShellUpdateForm.ID,"shell_result":this.ConfigShellUpdateForm.ShellResult,"status":this.ConfigShellUpdateForm.Status}],
@ -141,7 +142,6 @@ export default {
}
},
async deleteConfigFile(index) {
//
let isDelete = confirm("是否删除?");
if (!isDelete) {
return;
@ -153,7 +153,6 @@ export default {
token: this.tokenData.token,
shells: [{"id":this.ConfigFileCurrentPageData[index].ID}],
};
console.log("req:", req);
result = await deleteConfigShellService(req);
if (result.code == 0) {
ElMessage.success("删除成功");
@ -177,16 +176,7 @@ export default {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
async updateConfigShell(index) {
//console.log("index:", index);
let cf = this.ConfigFileCurrentPageData[index];
// console.log("cf:", cf);
// let req = {
// token: this.tokenData.token,
// id: cf.ID,
// type: "one",
// };
// let result = await getConfigShellListService(req);
// let data = result.data;
this.ConfigShellUpdateForm = cf;
this.updateDialogVisible = true;
},
@ -201,9 +191,7 @@ export default {
this.addForm.server = cf.Server;
await this.addConfigFile();
},
onSubmit() {
getConfigFileList();
},
@ -214,31 +202,13 @@ export default {
);
},
handleSizeChange(val) {
//console.log(` ${val} `);
this.pageSize = val;
this.currentPageData();
},
handleCurrentChange(val) {
//console.log(`: ${val}`);
this.currentPage = val;
this.currentPageData();
},
async displayMyInfo() {
await this.getMyUserInfo(this.tokenData.user_id);
this.updateDialogVisible = true;
},
async displayUserInfo(id) {
await this.getMyUserInfo(id);
this.updateDialogVisible = true;
},
handleMenuSelect(val) {
router.push(val);
},
toVideoList() {
router.push("/videoList");
},
async getMonitorDeviceList(){
try {
let req = {
@ -248,8 +218,7 @@ export default {
let result = await GetMonitorDeviceInfoService(req);
if (result.code === 0) {
this.monitor_list = result.data;
console.log("monitor_list:", this.monitor_list);
this.dialogVisible = true; //
this.dialogVisible = true;
} else {
ElMessage.error("获取设备列表失败");
}
@ -259,15 +228,14 @@ export default {
},
updateMonitorShow(index) {
this.monitor_add_update_visible = true;
this.monitor_update_add = { ...this.monitor_list[index] }; //
this.monitor_update_add = { ...this.monitor_list[index] };
},
//
async addUpdateMonitor(){
let req = {
"token": this.tokenData.token,
"devices": [this.monitor_update_add],
};
req.devices[0].expire =parseInt(req.devices[0].expire);
req.devices[0].expire = parseInt(req.devices[0].expire);
try{
let result = await UpdateMonitorDeviceInfoService(req);
if (result.code === 0) {
@ -276,12 +244,10 @@ export default {
} else {
ElMessage.error("设备信息更新失败");
}
}catch (e) {
console.log(e);
}
},
async DelMonitor(index) {
let isDelete = confirm("是否删除?");
if (!isDelete) {
@ -292,7 +258,6 @@ export default {
devices: [this.monitor_list[index]],
};
try {
let result = await DelMonitorDeviceInfoService(req);
if (result.code === 0) {
ElMessage.success("设备删除成功");
@ -304,420 +269,327 @@ export default {
console.log(e);
}
},
//
tableRowClassName({ row, rowIndex }) {
switch (row.Status) {
case 0:
return 'rgba(243, 243, 248, 0.1)'; //
case 1:
return 'rgba(0, 0, 255, 0.5)'; //
case 2:
return 'rgba(0, 0, 255, 0.9)'; //
default:
return 'transparent'; //
}
case 0:
return 'rgba(243, 243, 248, 0.1)';
case 1:
return 'rgba(0, 0, 255, 0.5)';
case 2:
return 'rgba(0, 0, 255, 0.9)';
default:
return 'transparent';
}
},
},
//
//
async mounted() {
let now = new Date();
if (localStorage.getItem("token") === null) {
router.push("/login");
}
await this.getConfigFileList();
//await this.getMyUserInfo(localStorage.getItem("userId"));
},
};
</script>
<template>
<div>
<el-container style="min-height: calc(100vh - 40px); background: #fff; border-radius: 4px;">
<el-header style="font-size: 40px; background-color: rgb(238, 241, 246)"
>命令分发</el-header
>
<el-container>
<el-main>
<el-form :inline="true" :model="tokenData" class="demo-form-inline">
<el-form-item>
<el-dialog
v-model="updateDialogVisible"
title="编辑配置"
width="60%"
:before-close="handleClose"
>
<!-- 内容主体区域 -->
<el-form
ref="updateFormRef"
:model="ConfigShellUpdateForm"
:rules="UserUpdateFormRules"
label-width="70px"
>
<!-- row -->
<el-row>
<el-col :span="12">
<el-form-item label="ID">
<el-input
v-model="ConfigShellUpdateForm.ID"
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="名称">
<el-input
v-model="ConfigShellUpdateForm.ShellName"
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="32">
<el-form-item label="内容">
<el-input
v-model="ConfigShellUpdateForm.ShellContent"
disabled
type="textarea"
style="width: 512px"
></el-input>
</el-form-item>
</el-col>
<el-col>
<el-form-item label="运行结果" prop="shell_result">
<el-input
type="textarea"
v-model="ConfigShellUpdateForm.ShellResult"
style="width: 600px"
:autosize="{ minRows: 4, maxRows: 8 }"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="page-container">
<el-card class="page-card" shadow="never">
<template #header>
<div class="card-header">
<div class="header-left">
<span class="page-title">命令分发</span>
</div>
<div class="header-actions">
<el-button type="primary" @click="addConfigFileV()" :icon="Plus">
创建命令
</el-button>
<el-button type="primary" @click="getMonitorDeviceList()" :icon="Monitor">
设备监控
</el-button>
<el-button @click="getConfigFileList()" :icon="Refresh">
刷新
</el-button>
</div>
</div>
</template>
<!-- 底部区域 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="updateDialogVisible = false"
>取消</el-button
>
<el-button type="primary" @click="updateConfigShellInfo()"
>确定</el-button
>
</span>
</template>
</el-dialog>
</el-form-item>
<!-- 表单 -->
<el-form :inline="true" :model="tokenData" class="demo-form-inline">
<el-form-item>
<el-button
class="el-button--danger"
type="primary"
:disabled="loading"
@click="getConfigFileList()"
>查询</el-button
>
</el-form-item>
<el-form-item>
<el-button
class="el-button--danger"
type="primary"
@click="addConfigFileV()"
>创建命令</el-button
>
</el-form-item>
<el-form-item>
<el-button
class="el-button--danger"
type="primary"
@click="getMonitorDeviceList()"
>设备监控</el-button
>
</el-form-item>
</el-form>
</el-form>
<el-dialog
v-model="addConfigFileVisible"
title="创建命令"
width="50%"
:before-close="handleClose"
>
<!-- 内容主体区域 -->
<el-form
ref="addFormRef"
:model="addForm"
:rules="addFormRules"
label-width="70px"
>
<el-row>
<el-form-item label="名称" prop="shell_name">
<el-input
v-model="addForm.shell_name"
autocomplete="on"
></el-input>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="内容" prop="file_path">
<el-input
type="textarea"
v-model="addForm.shell_content"
autocomplete="on"
style="width: 600px"
:autosize="{ minRows: 4, maxRows: 8 }"
></el-input>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="服务器" prop="server">
<el-select v-model="addForm.server" filterable allow-create>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
</el-row>
</el-form>
<!-- 底部区域 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="addConfigFileVisible = false"
>取消</el-button
>
<el-button type="primary" @click="addConfigFile()"
>确定</el-button
>
</span>
</template>
</el-dialog>
<el-dialog
v-model="monitor_add_update_visible"
title="编辑监控"
width="50%"
@close="handleClose">
<el-form
ref="monitorUpdateFormRef"
:model="monitor_update_add"
:rules="UserUpdateFormRules"
label-width="70px"
>
<el-row>
<el-form-item label="设备" prop="id">
<el-input
v-model="monitor_update_add.id"
></el-input>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="状态" prop="status">
<el-select v-model="monitor_update_add.status">
<el-option label="在线" value="1"></el-option>
<el-option label="离线" value="0"></el-option>
</el-select>
</el-form-item>
</el-row>
<el-row>
<el-form-item label="过期时间" prop="expire">
<el-input
v-model="monitor_update_add.expire"
oninput="this.value = this.value.replace(/[^0-9]/g, '')"
></el-input>
</el-form-item>
</el-row>
</el-form>
<!-- 底部区域 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="monitor_add_update_visible = false"
>取消</el-button
>
<el-button type="primary" @click="addUpdateMonitor()"
>确定</el-button
>
</span>
</template>
</el-dialog>
<el-dialog
v-model="dialogVisible"
title="设备状态监控"
width="50%"
@close="handleClose"
>
<el-button type="primary" @click="monitor_add_update_visible = true">添加</el-button>
<el-table
:data="monitor_list"
stripe
width="90%"
fit
>
<el-table-column prop="id" label="设备" width="150"></el-table-column>
<el-table-column prop="status" label="状态" width="150">
<template #default="scope">
<el-tag
:type="scope.row.status === '1' ? 'success' : 'danger'"
>{{ scope.row.status === "1" ? '在线' : '离线' }}</el-tag
>
</template>
</el-table-column>
<el-table-column prop="expire" label="过期时间" width="150">
</el-table-column>
<el-table-column label="操作" width="270">
<template #default="scope">
<el-button
type="primary"
size="mini"
@click.prevent="updateMonitorShow(scope.$index)"
>编辑</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="DelMonitor(scope.$index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
</template>
</el-dialog>
<!-- 表格 :row-style="this.tableRowClassName"-->
<el-table :data="ConfigFileCurrentPageData" width="100%" v-loading="loading">
:row-style="tableRowClassName"
<el-table-column prop="ID" label="id" width="80"></el-table-column>
<el-table-column
prop="ShellName"
label="命令名称"
width="120"
></el-table-column>
<el-table-column
prop="ShellContent"
label="命令内容"
show-overflow-tooltip
width="120"
></el-table-column>
<el-table-column
prop="Status"
label="状态"
width="100"
>
<template #default="scope">
<el-tag
v-if="scope.row.Status === 0"
>任务提交</el-tag
>
<el-tag
v-if="scope.row.Status === 1"
type="warning"
>正在运行</el-tag>
<el-tag
v-if="scope.row.Status === 2"
type="success"
>执行成功</el-tag>
<el-tag
v-if="scope.row.Status === 3"
type="danger"
>执行出错</el-tag>
</template>
</el-table-column>
<el-table-column
prop="ShellRuntime"
label="SR"
width="50"
>
<template #default="scope">
<!-- 转为秒两位小数-->
{{ Math.round(scope.row.ShellRuntime * 100) / 100 }}
<div class="table-wrapper">
<el-table :data="ConfigFileCurrentPageData" style="width: 100%" v-loading="loading"
element-loading-text="加载中..."
element-loading-background="rgba(240, 242, 245, 0.8)"
stripe
:row-class-name="tableRowClassName">
<el-table-column prop="ID" label="ID" width="80"></el-table-column>
<el-table-column prop="ShellName" label="命令名称" min-width="150" show-overflow-tooltip></el-table-column>
<el-table-column prop="ShellContent" label="命令内容" min-width="200" show-overflow-tooltip></el-table-column>
<el-table-column prop="Status" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.Status === 0 ? 'info' : scope.row.Status === 1 ? 'warning' : 'success'">
{{ scope.row.Status === 0 ? '待执行' : scope.row.Status === 1 ? '执行中' : '已完成' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="ShellDuration"
label="SD"
width="50"
>
<template #default="scope">
<!-- 转为秒两位小数-->
{{ Math.round(scope.row.ShellDuration * 100) / 100 }}
<el-table-column prop="CreatedAt" label="创建时间" min-width="160"></el-table-column>
<el-table-column prop="UpdatedAt" label="更新时间" min-width="160"></el-table-column>
<el-table-column label="操作" width="250" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click.prevent="updateConfigShell(scope.$index)" :icon="Edit">
编辑
</el-button>
<el-button type="danger" size="small" @click.prevent="deleteConfigFile(scope.$index)" :icon="Delete">
删除
</el-button>
<el-button type="success" size="small" @click.prevent="createAgain(scope.$index)" :icon="Operation">
再次执行
</el-button>
</template>
</el-table-column>
<el-table-column
prop="Server"
label="服务器"
width="100"
></el-table-column>
<el-table-column
prop="CreatedAt"
label="创建时间"
width="160"
></el-table-column>
<el-table-column
prop="UpdatedAt"
label="上次更新"
width="160"
></el-table-column>
<!-- <el-table-column
prop="AuthID"
label="创建用户ID"
width="40"
></el-table-column> -->
<el-table-column label="操作" width="270">
<template #default="scope">
<el-button
type="primary"
size="mini"
@click.prevent="updateConfigShell(scope.$index)"
>编辑</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="deleteConfigFile(scope.$index)"
>删除</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="createAgain(scope.$index)"
>再次执行</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 分页条 -->
<!-- Pagination 分页 -->
</el-table>
<div class="pagination-wrapper">
<el-pagination
background
layout="total,sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 50, 100]"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:total="tableData.length"
></el-pagination>
</el-main>
</el-container>
</el-container>
</div>
</div>
</el-card>
<!-- 编辑命令对话框 -->
<el-dialog
v-model="updateDialogVisible"
title="编辑配置"
width="60%"
:close-on-click-modal="false"
top="5vh"
>
<el-form
ref="updateFormRef"
:model="ConfigShellUpdateForm"
:rules="UserUpdateFormRules"
label-width="80px"
class="form-dialog"
>
<el-row gutter="20">
<el-col :span="12">
<el-form-item label="ID">
<el-input v-model="ConfigShellUpdateForm.ID" disabled></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="名称">
<el-input v-model="ConfigShellUpdateForm.ShellName" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="内容">
<el-input v-model="ConfigShellUpdateForm.ShellContent" disabled type="textarea" :rows="3"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="运行结果">
<el-input type="textarea" v-model="ConfigShellUpdateForm.ShellResult" :rows="6"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="updateDialogVisible = false">取消</el-button>
<el-button type="primary" @click="updateConfigShellInfo()">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 创建命令对话框 -->
<el-dialog
v-model="addConfigFileVisible"
title="创建命令"
width="50%"
:close-on-click-modal="false"
top="5vh"
>
<el-form
ref="addFormRef"
:model="addForm"
:rules="addFormRules"
label-width="80px"
class="form-dialog"
>
<el-form-item label="名称">
<el-input v-model="addForm.shell_name" placeholder="请输入命令名称"></el-input>
</el-form-item>
<el-form-item label="内容">
<el-input type="textarea" v-model="addForm.shell_content" placeholder="请输入命令内容" :rows="4"></el-input>
</el-form-item>
<el-form-item label="服务器">
<el-select v-model="addForm.server" filterable allow-create placeholder="选择服务器">
<el-option v-for="item in serverList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="addConfigFileVisible = false">取消</el-button>
<el-button type="primary" @click="addConfigFile()">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 设备监控对话框 -->
<el-dialog
v-model="dialogVisible"
title="设备状态监控"
width="70%"
@close="handleClose"
top="5vh"
>
<el-button type="primary" @click="monitor_add_update_visible = true" style="margin-bottom: 16px;">
添加设备
</el-button>
<el-table :data="monitor_list" stripe style="width: 100%">
<el-table-column prop="id" label="设备" width="150"></el-table-column>
<el-table-column prop="status" label="状态" width="150">
<template #default="scope">
<el-tag :type="scope.row.status === '1' ? 'success' : 'danger'">
{{ scope.row.status === '1' ? '在线' : '离线' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="expire" label="过期时间" width="150"></el-table-column>
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click.prevent="updateMonitorShow(scope.$index)" :icon="Edit">
编辑
</el-button>
<el-button type="danger" size="small" @click.prevent="DelMonitor(scope.$index)" :icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="dialogVisible = false">关闭</el-button>
</template>
</el-dialog>
<!-- 添加/编辑设备监控对话框 -->
<el-dialog
v-model="monitor_add_update_visible"
title="添加/编辑设备监控"
width="50%"
@close="handleClose"
top="5vh"
>
<el-form
ref="monitorUpdateFormRef"
:model="monitor_update_add"
:rules="UserUpdateFormRules"
label-width="80px"
class="form-dialog"
>
<el-form-item label="设备">
<el-input v-model="monitor_update_add.id" placeholder="设备ID"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="monitor_update_add.status">
<el-option label="在线" value="1"></el-option>
<el-option label="离线" value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label="过期时间">
<el-input v-model="monitor_update_add.expire" placeholder="过期时间" oninput="this.value = this.value.replace(/[^0-9]/g, '')"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="monitor_add_update_visible = false">取消</el-button>
<el-button type="primary" @click="addUpdateMonitor()">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style>
.blueRowbg {
background: "#488aff";
<style scoped>
.page-container {
width: 100%;
height: 100%;
}
</style>
.page-card {
height: 100%;
display: flex;
flex-direction: column;
border: none;
border-radius: 8px;
}
.page-card :deep(.el-card__body) {
flex: 1;
overflow: hidden;
padding: 20px;
display: flex;
flex-direction: column;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
display: flex;
align-items: center;
}
.page-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
.table-wrapper {
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
}
.table-wrapper :deep(.el-table) {
border-radius: 4px;
}
.pagination-wrapper {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.form-dialog {
padding: 10px 0;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
</style>