完成服务器详细信息展示模块
This commit is contained in:
parent
0b8ef34ae8
commit
f5a09cede0
@ -2,6 +2,8 @@ package com.example.controller;
|
|||||||
|
|
||||||
import com.example.entity.RestBean;
|
import com.example.entity.RestBean;
|
||||||
import com.example.entity.vo.request.RenameClientVO;
|
import com.example.entity.vo.request.RenameClientVO;
|
||||||
|
import com.example.entity.vo.request.RenameNodeVO;
|
||||||
|
import com.example.entity.vo.response.ClientDetailsVO;
|
||||||
import com.example.entity.vo.response.ClientPreviewVO;
|
import com.example.entity.vo.response.ClientPreviewVO;
|
||||||
import com.example.service.ClientService;
|
import com.example.service.ClientService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@ -27,4 +29,15 @@ public class MonitorController {
|
|||||||
service.renameClient(vo);
|
service.renameClient(vo);
|
||||||
return RestBean.success();
|
return RestBean.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/node")
|
||||||
|
public RestBean<Void> renameNode(@RequestBody @Valid RenameNodeVO vo) {
|
||||||
|
service.renameNode(vo);
|
||||||
|
return RestBean.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/details")
|
||||||
|
public RestBean<ClientDetailsVO> details(int clientId) {
|
||||||
|
return RestBean.success(service.clientDetails(clientId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.example.entity.vo.request;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RenameNodeVO {
|
||||||
|
int id;
|
||||||
|
@Length(min = 1, max = 10)
|
||||||
|
String node;
|
||||||
|
@Pattern(regexp = "(cn|hk|jp|us|sg|kr|de)")
|
||||||
|
String location;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.example.entity.vo.response;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ClientDetailsVO {
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
boolean online;
|
||||||
|
String node;
|
||||||
|
String location;
|
||||||
|
String ip;
|
||||||
|
String cpuName;
|
||||||
|
String osName;
|
||||||
|
String osVersion;
|
||||||
|
double memory;
|
||||||
|
int cpuCore;
|
||||||
|
double disk;
|
||||||
|
}
|
@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||||||
import com.example.entity.dto.Client;
|
import com.example.entity.dto.Client;
|
||||||
import com.example.entity.vo.request.ClientDetailVO;
|
import com.example.entity.vo.request.ClientDetailVO;
|
||||||
import com.example.entity.vo.request.RenameClientVO;
|
import com.example.entity.vo.request.RenameClientVO;
|
||||||
|
import com.example.entity.vo.request.RenameNodeVO;
|
||||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||||
|
import com.example.entity.vo.response.ClientDetailsVO;
|
||||||
import com.example.entity.vo.response.ClientPreviewVO;
|
import com.example.entity.vo.response.ClientPreviewVO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -18,4 +20,6 @@ public interface ClientService extends IService<Client> {
|
|||||||
void updateRuntimeDetail(RuntimeDetailVO vo, Client client);
|
void updateRuntimeDetail(RuntimeDetailVO vo, Client client);
|
||||||
List<ClientPreviewVO> listClients();
|
List<ClientPreviewVO> listClients();
|
||||||
void renameClient(RenameClientVO vo);
|
void renameClient(RenameClientVO vo);
|
||||||
|
void renameNode(RenameNodeVO vo);
|
||||||
|
ClientDetailsVO clientDetails(int clientId);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import com.example.entity.dto.Client;
|
|||||||
import com.example.entity.dto.ClientDetail;
|
import com.example.entity.dto.ClientDetail;
|
||||||
import com.example.entity.vo.request.ClientDetailVO;
|
import com.example.entity.vo.request.ClientDetailVO;
|
||||||
import com.example.entity.vo.request.RenameClientVO;
|
import com.example.entity.vo.request.RenameClientVO;
|
||||||
|
import com.example.entity.vo.request.RenameNodeVO;
|
||||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||||
|
import com.example.entity.vo.response.ClientDetailsVO;
|
||||||
import com.example.entity.vo.response.ClientPreviewVO;
|
import com.example.entity.vo.response.ClientPreviewVO;
|
||||||
import com.example.mapper.ClientDetailMapper;
|
import com.example.mapper.ClientDetailMapper;
|
||||||
import com.example.mapper.ClientMapper;
|
import com.example.mapper.ClientMapper;
|
||||||
@ -95,7 +97,7 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
|
|||||||
ClientPreviewVO vo = client.asViewObject(ClientPreviewVO.class);
|
ClientPreviewVO vo = client.asViewObject(ClientPreviewVO.class);
|
||||||
BeanUtils.copyProperties(detailMapper.selectById(vo.getId()), vo);
|
BeanUtils.copyProperties(detailMapper.selectById(vo.getId()), vo);
|
||||||
RuntimeDetailVO runtime = currentRuntime.get(client.getId());
|
RuntimeDetailVO runtime = currentRuntime.get(client.getId());
|
||||||
if(runtime != null && System.currentTimeMillis() - runtime.getTimestamp() < 60 * 1000) {
|
if(this.isOnline(runtime)) {
|
||||||
BeanUtils.copyProperties(runtime, vo);
|
BeanUtils.copyProperties(runtime, vo);
|
||||||
vo.setOnline(true);
|
vo.setOnline(true);
|
||||||
}
|
}
|
||||||
@ -109,6 +111,25 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
|
|||||||
this.initClientCache();
|
this.initClientCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renameNode(RenameNodeVO vo) {
|
||||||
|
this.update(Wrappers.<Client>update().eq("id", vo.getId())
|
||||||
|
.set("node", vo.getNode()).set("location", vo.getLocation()));
|
||||||
|
this.initClientCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientDetailsVO clientDetails(int clientId) {
|
||||||
|
ClientDetailsVO vo = this.clientIdCache.get(clientId).asViewObject(ClientDetailsVO.class);
|
||||||
|
BeanUtils.copyProperties(detailMapper.selectById(clientId), vo);
|
||||||
|
vo.setOnline(this.isOnline(currentRuntime.get(clientId)));
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOnline(RuntimeDetailVO runtime) {
|
||||||
|
return runtime != null && System.currentTimeMillis() - runtime.getTimestamp() < 60 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
private void addClientCache(Client client) {
|
private void addClientCache(Client client) {
|
||||||
clientIdCache.put(client.getId(), client);
|
clientIdCache.put(client.getId(), client);
|
||||||
clientTokenCache.put(client.getToken(), client);
|
clientTokenCache.put(client.getToken(), client);
|
||||||
|
198
itbaima-monitor-web/package-lock.json
generated
198
itbaima-monitor-web/package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
|
"less": "^4.2.0",
|
||||||
"unplugin-auto-import": "^0.15.2",
|
"unplugin-auto-import": "^0.15.2",
|
||||||
"unplugin-vue-components": "^0.24.1",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
"vite": "^4.4.6"
|
"vite": "^4.4.6"
|
||||||
@ -823,6 +824,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/copy-anything": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-what": "^3.14.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz",
|
||||||
@ -956,6 +966,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/errno": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
|
||||||
|
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"prr": "~1.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"errno": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.18.17",
|
"version": "0.18.17",
|
||||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.17.tgz",
|
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.17.tgz",
|
||||||
@ -1113,6 +1136,13 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/has": {
|
"node_modules/has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
|
||||||
@ -1125,6 +1155,32 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/image-size": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"image-size": "bin/image-size.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-binary-path": {
|
"node_modules/is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
@ -1176,12 +1232,44 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-what": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/jsonc-parser": {
|
"node_modules/jsonc-parser": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
||||||
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
|
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/less": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/less/-/less-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"copy-anything": "^2.0.1",
|
||||||
|
"parse-node-version": "^1.0.1",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"lessc": "bin/lessc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"errno": "^0.1.1",
|
||||||
|
"graceful-fs": "^4.1.2",
|
||||||
|
"image-size": "~0.5.0",
|
||||||
|
"make-dir": "^2.1.0",
|
||||||
|
"mime": "^1.4.1",
|
||||||
|
"needle": "^3.1.0",
|
||||||
|
"source-map": "~0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/local-pkg": {
|
"node_modules/local-pkg": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.3.tgz",
|
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.4.3.tgz",
|
||||||
@ -1222,6 +1310,20 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pify": "^4.0.1",
|
||||||
|
"semver": "^5.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/memoize-one": {
|
"node_modules/memoize-one": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||||
@ -1249,6 +1351,19 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
@ -1309,6 +1424,23 @@
|
|||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/needle": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"needle": "bin/needle"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@ -1323,6 +1455,15 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
|
||||||
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
|
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/parse-node-version": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
@ -1349,6 +1490,16 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pify": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pkg-types": {
|
"node_modules/pkg-types": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.0.3.tgz",
|
||||||
@ -1378,6 +1529,13 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/prr": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
@ -1445,12 +1603,46 @@
|
|||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/scule": {
|
"node_modules/scule": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.0.0.tgz",
|
||||||
"integrity": "sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==",
|
"integrity": "sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
@ -1489,6 +1681,12 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/ufo": {
|
"node_modules/ufo": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.2.0.tgz",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
|
"less": "^4.2.0",
|
||||||
"unplugin-auto-import": "^0.15.2",
|
"unplugin-auto-import": "^0.15.2",
|
||||||
"unplugin-vue-components": "^0.24.1",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
"vite": "^4.4.6"
|
"vite": "^4.4.6"
|
||||||
|
BIN
itbaima-monitor-web/public/cpu-icons/AMD.png
Normal file
BIN
itbaima-monitor-web/public/cpu-icons/AMD.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
itbaima-monitor-web/public/cpu-icons/Apple.png
Normal file
BIN
itbaima-monitor-web/public/cpu-icons/Apple.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
itbaima-monitor-web/public/cpu-icons/Intel.png
Normal file
BIN
itbaima-monitor-web/public/cpu-icons/Intel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
53
itbaima-monitor-web/src/assets/css/element.less
Normal file
53
itbaima-monitor-web/src/assets/css/element.less
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
.is-success {
|
||||||
|
.el-progress-bar__outer {
|
||||||
|
background-color: #18cb1822 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-bar__inner {
|
||||||
|
background-color: #18cb18 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-circle__track {
|
||||||
|
stroke: #18cb1822;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-circle__path {
|
||||||
|
stroke: #18cb18 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-warning {
|
||||||
|
.el-progress-bar__outer {
|
||||||
|
background-color: #ffa04622;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-circle__track {
|
||||||
|
stroke: #ffa04622;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-circle__path {
|
||||||
|
stroke: #ffa046 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-bar__inner {
|
||||||
|
background-color: #ffa046 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-exception {
|
||||||
|
.el-progress-bar__outer {
|
||||||
|
background-color: #ef4e4e22;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-circle__track {
|
||||||
|
stroke: #ef4e4e22;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-circle__path {
|
||||||
|
stroke: #ef4e4e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-progress-bar__inner {
|
||||||
|
background-color: #ef4e4e !important;
|
||||||
|
}
|
||||||
|
}
|
228
itbaima-monitor-web/src/component/ClientDetails.vue
Normal file
228
itbaima-monitor-web/src/component/ClientDetails.vue
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
<script setup>
|
||||||
|
import {reactive, watch} from "vue";
|
||||||
|
import {get, post} from "@/net";
|
||||||
|
import {copyIp, cpuNameToImage, osNameToIcon, rename} from "@/tools";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
|
||||||
|
const locations = [
|
||||||
|
{name: 'cn', desc: '中国大陆'},
|
||||||
|
{name: 'hk', desc: '香港'},
|
||||||
|
{name: 'jp', desc: '日本'},
|
||||||
|
{name: 'us', desc: '美国'},
|
||||||
|
{name: 'sg', desc: '新加坡'},
|
||||||
|
{name: 'kr', desc: '韩国'},
|
||||||
|
{name: 'de', desc: '德国'}
|
||||||
|
]
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: Number,
|
||||||
|
update: Function
|
||||||
|
})
|
||||||
|
|
||||||
|
const details = reactive({
|
||||||
|
base: {},
|
||||||
|
runtime: {},
|
||||||
|
editNode: false
|
||||||
|
})
|
||||||
|
const nodeEdit = reactive({
|
||||||
|
name: '',
|
||||||
|
location: ''
|
||||||
|
})
|
||||||
|
const enableNodeEdit = () => {
|
||||||
|
details.editNode = true
|
||||||
|
nodeEdit.name = details.base.node
|
||||||
|
nodeEdit.location = details.base.location
|
||||||
|
}
|
||||||
|
const submitNodeEdit = () => {
|
||||||
|
post('/api/monitor/node', {
|
||||||
|
id: props.id,
|
||||||
|
node: nodeEdit.name,
|
||||||
|
location: nodeEdit.location
|
||||||
|
}, () => {
|
||||||
|
details.editNode = false
|
||||||
|
updateDetails()
|
||||||
|
ElMessage.success('节点信息已更新')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDetails() {
|
||||||
|
props.update()
|
||||||
|
init(props.id)
|
||||||
|
}
|
||||||
|
const init = id => {
|
||||||
|
if(id !== -1) {
|
||||||
|
details.base = {}
|
||||||
|
get(`/api/monitor/details?clientId=${id}`, data => Object.assign(details.base, data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(() => props.id, init, { immediate: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="client-details" v-loading="Object.keys(details.base).length === 0">
|
||||||
|
<div v-if="Object.keys(details.base).length">
|
||||||
|
<div class="title">
|
||||||
|
<i class="fa-solid fa-server"></i>
|
||||||
|
服务器信息
|
||||||
|
</div>
|
||||||
|
<el-divider style="margin: 10px 0"/>
|
||||||
|
<div class="details-list">
|
||||||
|
<div>
|
||||||
|
<span>服务器ID</span>
|
||||||
|
<span>{{details.base.id}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>服务器名称</span>
|
||||||
|
<span>{{details.base.name}}</span>
|
||||||
|
<i @click.stop="rename(details.base.id, details.base.name, updateDetails)"
|
||||||
|
class="fa-solid fa-pen-to-square interact-item"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>运行状态</span>
|
||||||
|
<span>
|
||||||
|
<i style="color: #18cb18" class="fa-solid fa-circle-play" v-if="details.base.online"></i>
|
||||||
|
<i style="color: #18cb18" class="fa-solid fa-circle-stop" v-else></i>
|
||||||
|
{{details.base.online ? '运行中' : '离线'}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="!details.editNode">
|
||||||
|
<span>服务器节点</span>
|
||||||
|
<span :class="`flag-icon flag-icon-${details.base.location}`"></span>
|
||||||
|
<span>{{details.base.node}}</span>
|
||||||
|
<i @click.stop="enableNodeEdit"
|
||||||
|
class="fa-solid fa-pen-to-square interact-item"/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span>服务器节点</span>
|
||||||
|
<div style="display: inline-block;height: 15px">
|
||||||
|
<div style="display: flex">
|
||||||
|
<el-select v-model="nodeEdit.location" style="width: 80px" size="small">
|
||||||
|
<el-option v-for="item in locations" :value="item.name">
|
||||||
|
<span :class="`flag-icon flag-icon-${item.name}`"></span>
|
||||||
|
{{item.desc}}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-input v-model="nodeEdit.name" style="margin-left: 10px"
|
||||||
|
size="small" placeholder="请输入节点名称..."/>
|
||||||
|
<div style="margin-left: 10px">
|
||||||
|
<i @click.stop="submitNodeEdit" class="fa-solid fa-check interact-item"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>公网IP地址</span>
|
||||||
|
<span>
|
||||||
|
{{details.base.ip}}
|
||||||
|
<i class="fa-solid fa-copy interact-item" style="color: dodgerblue" @click.stop="copyIp(details.base.ip)"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex">
|
||||||
|
<span>处理器</span>
|
||||||
|
<span>{{details.base.cpuName}}</span>
|
||||||
|
<el-image style="height: 20px;margin-left: 10px"
|
||||||
|
:src="`/cpu-icons/${cpuNameToImage(details.base.cpuName)}`"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>硬件配置信息</span>
|
||||||
|
<span>
|
||||||
|
<i class="fa-solid fa-microchip"></i>
|
||||||
|
<span style="margin-right: 10px">{{` ${details.base.cpuCore} CPU 核心数 /`}}</span>
|
||||||
|
<i class="fa-solid fa-memory"></i>
|
||||||
|
<span>{{` ${details.base.memory.toFixed(1)} GB 内存容量`}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>操作系统</span>
|
||||||
|
<i :style="{color: osNameToIcon(details.base.osName).color}"
|
||||||
|
:class="`fa-brands ${osNameToIcon(details.base.osName).icon}`"></i>
|
||||||
|
<span style="margin-left: 10px">{{`${details.base.osName} ${details.base.osVersion}`}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="title" style="margin-top: 20px">
|
||||||
|
<i class="fa-solid fa-gauge-high"></i>
|
||||||
|
实时监控
|
||||||
|
</div>
|
||||||
|
<el-divider style="margin: 10px 0"/>
|
||||||
|
<div v-if="details.base.online">
|
||||||
|
<div style="display: flex">
|
||||||
|
<el-progress type="dashboard" :width="100" :percentage="20" status="success">
|
||||||
|
<div style="font-size: 17px;font-weight: bold;color: initial">CPU</div>
|
||||||
|
<div style="font-size: 13px;color: grey;margin-top: 5px">20%</div>
|
||||||
|
</el-progress>
|
||||||
|
<el-progress style="margin-left: 20px"
|
||||||
|
type="dashboard" :width="100" :percentage="60" status="success">
|
||||||
|
<div style="font-size: 16px;font-weight: bold;color: initial">内存</div>
|
||||||
|
<div style="font-size: 13px;color: grey;margin-top: 5px">28.6 GB</div>
|
||||||
|
</el-progress>
|
||||||
|
<div style="flex: 1;margin-left: 30px;display: flex;flex-direction: column;height: 80px">
|
||||||
|
<div style="flex: 1;font-size: 14px">
|
||||||
|
<div>实时网络速度</div>
|
||||||
|
<div>
|
||||||
|
<i style="color: orange" class="fa-solid fa-arrow-up"></i>
|
||||||
|
<span>{{` 20KB/s`}}</span>
|
||||||
|
<el-divider direction="vertical"/>
|
||||||
|
<i style="color: dodgerblue" class="fa-solid fa-arrow-down"></i>
|
||||||
|
<span>{{` 0KB/s`}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div style="font-size: 13px;display: flex;justify-content: space-between">
|
||||||
|
<div>
|
||||||
|
<i class="fa-solid fa-hard-drive"></i>
|
||||||
|
<span> 磁盘总容量</span>
|
||||||
|
</div>
|
||||||
|
<div>6.6 GB / 40.0 GB</div>
|
||||||
|
</div>
|
||||||
|
<el-progress type="line" status="success" :percentage="24" :show-text="false"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty description="服务器处于离线状态,请检查服务器是否正常运行" v-else/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.interact-item {
|
||||||
|
transition: .3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
scale: 1.1;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: dodgerblue;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list {
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
& div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
& span:first-child {
|
||||||
|
color: gray;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: normal;
|
||||||
|
width: 120px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,33 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {fitByUnit} from '@/tools'
|
import {copyIp, fitByUnit, osNameToIcon, percentageToStatus, rename} from '@/tools'
|
||||||
import {useClipboard} from "@vueuse/core";
|
|
||||||
import {ElMessage, ElMessageBox} from "element-plus";
|
|
||||||
import {post} from "@/net";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: Object,
|
data: Object,
|
||||||
update: Function
|
update: Function
|
||||||
})
|
})
|
||||||
|
|
||||||
const { copy } = useClipboard()
|
|
||||||
const copyIp = () => copy(props.data.ip).then(() => ElMessage.success('成功复制IP地址到剪贴板'))
|
|
||||||
|
|
||||||
function rename() {
|
|
||||||
ElMessageBox.prompt('请输入新的服务器主机名称', '修改名称', {
|
|
||||||
confirmButtonText: '确认',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
inputValue: props.data.name,
|
|
||||||
inputPattern: /^[a-zA-Z0-9_\u4e00-\u9fa5]{1,10}$/,
|
|
||||||
inputErrorMessage: '名称只能包含中英文字符、数字和下划线',
|
|
||||||
}).then(({ value }) => post('/api/monitor/rename', {
|
|
||||||
id: props.data.id,
|
|
||||||
name: value
|
|
||||||
}, () => {
|
|
||||||
ElMessage.success('主机名称已更新')
|
|
||||||
props.update()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -37,10 +14,13 @@ function rename() {
|
|||||||
<div class="name">
|
<div class="name">
|
||||||
<span :class="`flag-icon flag-icon-${data.location}`"></span>
|
<span :class="`flag-icon flag-icon-${data.location}`"></span>
|
||||||
<span style="margin: 0 5px">{{ data.name }}</span>
|
<span style="margin: 0 5px">{{ data.name }}</span>
|
||||||
<i class="fa-solid fa-pen-to-square interact-item" @click.stop="rename"></i>
|
<i class="fa-solid fa-pen-to-square interact-item" @click.stop="rename(data.id, data.name, update)"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="os">
|
<div class="os">
|
||||||
操作系统: {{`${data.osName} ${data.osVersion}`}}
|
操作系统:
|
||||||
|
<i :style="{color: osNameToIcon(data.osName).color}"
|
||||||
|
:class="`fa-brands ${osNameToIcon(data.osName).icon}`"></i>
|
||||||
|
{{`${data.osName} ${data.osVersion}`}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status" v-if="data.online">
|
<div class="status" v-if="data.online">
|
||||||
@ -55,7 +35,7 @@ function rename() {
|
|||||||
<el-divider style="margin: 10px 0"/>
|
<el-divider style="margin: 10px 0"/>
|
||||||
<div class="network">
|
<div class="network">
|
||||||
<span style="margin-right: 10px">公网IP: {{data.ip}}</span>
|
<span style="margin-right: 10px">公网IP: {{data.ip}}</span>
|
||||||
<i class="fa-solid fa-copy interact-item" @click.stop="copyIp" style="color: dodgerblue"></i>
|
<i class="fa-solid fa-copy interact-item" @click.stop="copyIp(data.ip)" style="color: dodgerblue"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="cpu">
|
<div class="cpu">
|
||||||
<span style="margin-right: 10px">处理器: {{data.cpuName}}</span>
|
<span style="margin-right: 10px">处理器: {{data.cpuName}}</span>
|
||||||
@ -68,12 +48,12 @@ function rename() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<span>{{`CPU: ${(data.cpuUsage * 100).toFixed(1)}%`}}</span>
|
<span>{{`CPU: ${(data.cpuUsage * 100).toFixed(1)}%`}}</span>
|
||||||
<el-progress status="success"
|
<el-progress :status="percentageToStatus(data.cpuUsage * 100)"
|
||||||
:percentage="data.cpuUsage * 100" :stroke-width="5" :show-text="false"/>
|
:percentage="data.cpuUsage * 100" :stroke-width="5" :show-text="false"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<span>内存: <b>{{data.memoryUsage.toFixed(1)}}</b> GB</span>
|
<span>内存: <b>{{data.memoryUsage.toFixed(1)}}</b> GB</span>
|
||||||
<el-progress status="success"
|
<el-progress :status="percentageToStatus(data.memoryUsage/data.memory * 100)"
|
||||||
:percentage="data.memoryUsage/data.memory * 100" :stroke-width="5" :show-text="false"/>
|
:percentage="data.memoryUsage/data.memory * 100" :stroke-width="5" :show-text="false"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="network-flow">
|
<div class="network-flow">
|
||||||
@ -90,14 +70,6 @@ function rename() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
:deep(.el-progress-bar__outer) {
|
|
||||||
background-color: #18cb1822;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-progress-bar__inner) {
|
|
||||||
background-color: #18cb18;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .instance-card { color: #d9d9d9 }
|
.dark .instance-card { color: #d9d9d9 }
|
||||||
|
|
||||||
.interact-item {
|
.interact-item {
|
||||||
@ -117,6 +89,12 @@ function rename() {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: #606060;
|
color: #606060;
|
||||||
|
transition: .3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
scale: 1.02;
|
||||||
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
@ -3,6 +3,7 @@ import App from './App.vue'
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
|
import '@/assets/css/element.less'
|
||||||
import 'flag-icon-css/css/flag-icons.min.css'
|
import 'flag-icon-css/css/flag-icons.min.css'
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import {useClipboard} from "@vueuse/core";
|
||||||
|
import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
|
import {post} from "@/net";
|
||||||
|
|
||||||
function fitByUnit(value, unit) {
|
function fitByUnit(value, unit) {
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
||||||
let index = units.indexOf(unit)
|
let index = units.indexOf(unit)
|
||||||
@ -13,4 +17,57 @@ function fitByUnit(value, unit) {
|
|||||||
return `${parseInt(value)} ${units[index]}`
|
return `${parseInt(value)} ${units[index]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export { fitByUnit }
|
function percentageToStatus(percentage) {
|
||||||
|
if(percentage < 50)
|
||||||
|
return 'success'
|
||||||
|
else if(percentage < 80)
|
||||||
|
return 'warning'
|
||||||
|
else
|
||||||
|
return 'exception'
|
||||||
|
}
|
||||||
|
|
||||||
|
function osNameToIcon(name) {
|
||||||
|
if(name.indexOf('Ubuntu') >= 0)
|
||||||
|
return {icon: 'fa-ubuntu', color: '#db4c1a'}
|
||||||
|
else if(name.indexOf('CentOS') >= 0)
|
||||||
|
return {icon: 'fa-centos', color: '#9dcd30'}
|
||||||
|
else if(name.indexOf('macOS') >= 0)
|
||||||
|
return {icon: 'fa-apple', color: 'grey'}
|
||||||
|
else if(name.indexOf('Windows') >= 0)
|
||||||
|
return {icon: 'fa-windows', color: '#3578b9'}
|
||||||
|
else if(name.indexOf('Debian') >= 0)
|
||||||
|
return {icon: 'fa-debian', color: '#a80836'}
|
||||||
|
else
|
||||||
|
return {icon: 'fa-linux', color: 'grey'}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cpuNameToImage(name) {
|
||||||
|
if(name.indexOf('Apple') >= 0)
|
||||||
|
return 'Apple.png'
|
||||||
|
else if(name.indexOf('AMD') >= 0)
|
||||||
|
return 'AMD.png'
|
||||||
|
else
|
||||||
|
return 'Intel.png'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { copy } = useClipboard()
|
||||||
|
const copyIp = ip => copy(ip).then(() => ElMessage.success('成功复制IP地址到剪贴板'))
|
||||||
|
|
||||||
|
function rename(id, name, after) {
|
||||||
|
ElMessageBox.prompt('请输入新的服务器主机名称', '修改名称', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
inputValue: name,
|
||||||
|
inputPattern: /^[a-zA-Z0-9_\u4e00-\u9fa5]{1,10}$/,
|
||||||
|
inputErrorMessage: '名称只能包含中英文字符、数字和下划线',
|
||||||
|
}).then(({ value }) => post('/api/monitor/rename', {
|
||||||
|
id: id,
|
||||||
|
name: value
|
||||||
|
}, () => {
|
||||||
|
ElMessage.success('主机名称已更新')
|
||||||
|
after()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fitByUnit, percentageToStatus, cpuNameToImage, osNameToIcon, rename, copyIp }
|
||||||
|
@ -1,13 +1,23 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import PreviewCard from "@/component/PreviewCard.vue";
|
import PreviewCard from "@/component/PreviewCard.vue";
|
||||||
import {ref} from "vue";
|
import {reactive, ref} from "vue";
|
||||||
import {get} from "@/net";
|
import {get} from "@/net";
|
||||||
|
import ClientDetails from "@/component/ClientDetails.vue";
|
||||||
|
|
||||||
const list = ref([])
|
const list = ref([])
|
||||||
|
|
||||||
const updateList = () => get('/api/monitor/list', data => list.value = data)
|
const updateList = () => get('/api/monitor/list', data => list.value = data)
|
||||||
setInterval(updateList, 10000)
|
setInterval(updateList, 10000)
|
||||||
updateList()
|
updateList()
|
||||||
|
|
||||||
|
const detail = reactive({
|
||||||
|
show: false,
|
||||||
|
id: -1
|
||||||
|
})
|
||||||
|
const displayClientDetails = (id) => {
|
||||||
|
detail.show = true
|
||||||
|
detail.id = id
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -16,12 +26,27 @@ updateList()
|
|||||||
<div class="desc">在这里管理所有已经注册的主机实例,实时监控主机运行状态,快速进行管理和操作。</div>
|
<div class="desc">在这里管理所有已经注册的主机实例,实时监控主机运行状态,快速进行管理和操作。</div>
|
||||||
<el-divider style="margin: 10px 0"/>
|
<el-divider style="margin: 10px 0"/>
|
||||||
<div class="card-list">
|
<div class="card-list">
|
||||||
<preview-card v-for="item in list" :data="item" :update="updateList"/>
|
<preview-card v-for="item in list" :data="item" :update="updateList"
|
||||||
|
@click="displayClientDetails(item.id)"/>
|
||||||
</div>
|
</div>
|
||||||
|
<el-drawer size="520" :show-close="false" v-model="detail.show"
|
||||||
|
:with-header="false" v-if="list.length" @close="detail.id = -1">
|
||||||
|
<client-details :id="detail.id" :update="updateList"/>
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
:deep(.el-drawer) {
|
||||||
|
margin: 10px;
|
||||||
|
height: calc(100% - 20px);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-drawer__body) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.manage-main {
|
.manage-main {
|
||||||
margin: 0 50px;
|
margin: 0 50px;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user