Merge branch 'dev-3.6' of https://git.mesalab.cn/nezha/nezha-fronted into dev-3.5-webpack

This commit is contained in:
zhangyu
2022-12-21 11:14:42 +08:00
161 changed files with 10873 additions and 805 deletions

View File

@@ -425,6 +425,7 @@ if (process.env.NODE_ENV == 'development') {
{
delete: [
path.join(__dirname, '../dist', '/config.json'),
path.join(__dirname, '../dist', '/*.js'),
path.join(__dirname, '../.cache/')
]
},

View File

@@ -1494,7 +1494,7 @@
},
"@mapbox/geojson-rewind": {
"version": "0.5.2",
"resolved": "https://registry.npmmirror.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
"integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
"requires": {
"get-stream": "^6.0.1",
@@ -1503,24 +1503,24 @@
"dependencies": {
"get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.6.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}
}
},
"@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="
},
"@mapbox/mapbox-gl-supported": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz",
"integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ=="
},
"@mapbox/node-pre-gyp": {
@@ -1676,9 +1676,9 @@
}
},
"tar": {
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
"version": "6.1.12",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz",
"integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==",
"requires": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@@ -1697,22 +1697,22 @@
},
"@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
},
"@mapbox/tiny-sdf": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz",
"integrity": "sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw=="
},
"@mapbox/unitbezier": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="
},
"@mapbox/vector-tile": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
"requires": {
"@mapbox/point-geometry": "~0.1.0"
@@ -1720,7 +1720,7 @@
},
"@mapbox/whoots-js": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="
},
"@npmcli/fs": {
@@ -1894,7 +1894,7 @@
},
"@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.10.tgz",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
},
"@types/glob": {
@@ -1950,12 +1950,12 @@
},
"@types/mapbox__point-geometry": {
"version": "0.1.2",
"resolved": "https://registry.npmmirror.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz",
"resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz",
"integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA=="
},
"@types/mapbox__vector-tile": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz",
"resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz",
"integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==",
"requires": {
"@types/geojson": "*",
@@ -1982,7 +1982,7 @@
},
"@types/pbf": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/@types/pbf/-/pbf-3.0.2.tgz",
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz",
"integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ=="
},
"@types/q": {
@@ -4515,12 +4515,12 @@
"integrity": "sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA=="
},
"canvas": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.1.tgz",
"integrity": "sha512-29pIjn9uwTUsIgJUNd7GXxKk8sg4iyJwLm1wIilNIqX1mVzXSc2nUij9exW1LqNpis1d2ebMYfMqTWcokZ4pdA==",
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.2.tgz",
"integrity": "sha512-FSmlsip0nZ0U4Zcfht0qBJqDhlfGuevTZKE8h+dBOYrJjGvY3iqMGSzzbvkaFhvMXiVxfcMaPHS/kge++T5SKg==",
"requires": {
"@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.15.0",
"nan": "^2.17.0",
"simple-get": "^3.0.3"
},
"dependencies": {
@@ -4800,7 +4800,7 @@
},
"clipboard": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
"resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
"requires": {
"good-listener": "^1.2.2",
@@ -5941,7 +5941,7 @@
},
"csscolorparser": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/csscolorparser/-/csscolorparser-1.0.3.tgz",
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="
},
"cssesc": {
@@ -7125,7 +7125,7 @@
},
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
},
"delegates": {
@@ -7399,7 +7399,7 @@
},
"earcut": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/earcut/-/earcut-2.2.4.tgz",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
"integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
},
"easings-css": {
@@ -9227,7 +9227,7 @@
},
"geojson-vt": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/geojson-vt/-/geojson-vt-3.2.1.tgz",
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
"integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg=="
},
"get-caller-file": {
@@ -9281,7 +9281,7 @@
},
"gl-matrix": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/gl-matrix/-/gl-matrix-3.4.3.tgz",
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
},
"glob": {
@@ -9468,7 +9468,7 @@
},
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
"requires": {
"delegate": "^3.1.2"
@@ -12059,7 +12059,7 @@
},
"kdbush": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/kdbush/-/kdbush-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz",
"integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
},
"keyv": {
@@ -12402,7 +12402,7 @@
},
"maplibre-gl": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/maplibre-gl/-/maplibre-gl-2.2.0.tgz",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-2.2.0.tgz",
"integrity": "sha512-5LB7ROIxvBADPa4PmU2j+mp0jG5IIbEidCOyZEXVbEriluMJn0hz28vszVb4Cr2IA4YQ9cnERqjHaf33MHIRBQ==",
"requires": {
"@mapbox/geojson-rewind": "^0.5.2",
@@ -13020,7 +13020,7 @@
},
"murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="
},
"mv": {
@@ -13995,7 +13995,7 @@
},
"pbf": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/pbf/-/pbf-3.2.1.tgz",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
"requires": {
"ieee754": "^1.1.12",
@@ -15359,7 +15359,7 @@
},
"potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/potpack/-/potpack-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
},
"preload": {
@@ -15500,7 +15500,7 @@
},
"protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://registry.npmmirror.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
},
"proxy-addr": {
@@ -15613,7 +15613,7 @@
},
"quickselect": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/quickselect/-/quickselect-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
},
"quill": {
@@ -16116,7 +16116,7 @@
},
"resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"requires": {
"protocol-buffers-schema": "^3.3.1"
@@ -16493,7 +16493,7 @@
},
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
},
"select-hose": {
@@ -17603,7 +17603,7 @@
},
"supercluster": {
"version": "7.1.5",
"resolved": "https://registry.npmmirror.com/supercluster/-/supercluster-7.1.5.tgz",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz",
"integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==",
"requires": {
"kdbush": "^3.0.0"
@@ -18405,7 +18405,7 @@
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tinycolor2": {
@@ -18415,13 +18415,13 @@
},
"tinyqueue": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/tinyqueue/-/tinyqueue-2.0.3.tgz",
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
"integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="
},
"tmp": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz",
"integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=",
"integrity": "sha512-89PTqMWGDva+GqClOqBV9s3SMh7MA3Mq0pJUdAoHuF65YoE7O0LermaZkVfT5/Ngfo18H4eYiyG7zKOtnEbxsw==",
"dev": true,
"requires": {
"os-tmpdir": "~1.0.1"
@@ -19073,7 +19073,7 @@
},
"vt-pbf": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/vt-pbf/-/vt-pbf-3.1.3.tgz",
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
"integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
"requires": {
"@mapbox/point-geometry": "0.1.0",
@@ -19088,7 +19088,7 @@
},
"vue-clipboard2": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/vue-clipboard2/-/vue-clipboard2-0.3.3.tgz",
"resolved": "https://registry.npmmirror.com/vue-clipboard2/-/vue-clipboard2-0.3.3.tgz",
"integrity": "sha512-aNWXIL2DKgJyY/1OOeITwAQz1fHaCIGvUFHf9h8UcoQBG5a74MkdhS/xqoYe7DNZdQmZRL+TAdIbtUs9OyVjbw==",
"requires": {
"clipboard": "^2.0.0"

4066
nezha-fronted/src/assets/css/animate.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -565,7 +565,7 @@ textarea {
.el-dialog__header{
color: $--color-text-primary;
}
#entranceHtml {
#entrance {
.el-icon-arrow-right {
font-family: "nz-icon" !important;
font-size: 16px;
@@ -576,6 +576,12 @@ textarea {
.el-icon-arrow-right:before {
content: "\e6ad";
}
.el-icon-arrow-up{
font-family: nz-icon!important;
}
.el-icon-arrow-up:before{
content: "\e6af";
}
.el-icon-arrow-down{
font-family: nz-icon!important;
}

View File

@@ -87,7 +87,7 @@
color: $--color-primary;
}
.nz-btn.nz-btn-style-normal:hover:not(.nz-btn-disabled) { /* 橙色按钮hover */
background: $--button-primary-hover-background-color;
background: $--button-primary-hover-background-color !important;
color: $--button-primary-color;
border: 1px solid $--button-primary-hover-background-color;
}

View File

@@ -449,7 +449,7 @@
}
}
}
.endpoint-query-metrics.chart-fullscreen.nz-dialog {
.endpoint-query-metrics.chart-fullscreen.nz-dialog,.recordRules-query-metrics.chart-fullscreen.nz-dialog {
.chart-screen-header .chart-header__tools #browser-go {
margin-left: 5px;
}

View File

@@ -0,0 +1,211 @@
.fileDirectory {
width: 45%;
min-width: 700px;
position: absolute;
top: -5px;
height: calc(100% + 30px);
right: -15px;
background: $--background-color-empty;
box-shadow: 5px 0 3px 0 $--explore-border-color-bottom;
z-index: 10;
font-size: 14px;
.file-directory-header{
display: flex;
font-size: 14px;
width: 100%;
justify-content: space-between;
padding: 0 20px;
height: 36px;
line-height: 36px;
background: $--background-color-empty;
border-bottom: 1px solid rgba(51,51,51,0.10);
box-sizing: border-box;
color:$--color-text-regular;
.header-option{
>i {
margin-right: 22px;
}
.nz-icon-close {
margin-right: 15px;
}
}
}
.file-directory-path{
display: flex;
width: 100%;
justify-content: space-between;
padding: 0 20px;
background: $--background-color-empty;
height: 36px;
line-height: 36px;
box-sizing: border-box;
color:$--color-text-regular;
.breadcrumb-box{
.nz-icon-edit{
margin-left: 15px;
display: none;
}
}
.breadcrumb-box:hover {
.nz-icon-edit{
display: inline-block;
}
}
.nz-icon-edit:hover{
color: $--color-primary;
cursor: pointer;
}
.breadcrumb-item{
color:$--color-text-regular
}
.breadcrumb-action{
cursor: pointer;
}
.breadcrumb-action:hover{
color: $--color-primary;
}
.path-option{
display: inline-block;
align-items: center;
.nz-icon-a-newfolder{
margin-left: 20px;
margin-right: 20px;
}
}
.el-input--suffix .el-input__inner{
padding-right: 106px;
}
}
.file-directory-content{
height: calc(100% - 60px);
width: calc(100% - 15px);
overflow: auto;
}
//.file-state-panel-content::-webkit-scrollbar {
// width: 6px;
//}
//.file-directory-content::-webkit-scrollbar-thumb {
// width: 6px;
//}
//.file-directory-content::-webkit-scrollbar-thumb:hover {
// width: 6px;
//}
.directory-content-header{
height: 32px;
line-height: 32px;
background: $--background-color-2;
width: calc(100% - 22px);
color: $--color-text-regular;
> div{text-transform:capitalize};
}
.file-name{
width: 44%;
box-sizing: border-box;
padding-left: 10px;
display: inline-block;
position: relative;
.nz-icon-link{
position: absolute;
font-size: 10px;
left: 7px;
bottom: -5px;
font-weight: 600;
}
}
.file-size{
width: 15%;
display: inline-block;
position: relative;
}
.file-date{
width: 25%;
display: inline-block;
position: relative;
}
.file-opt{
width: 14%;
display: inline-block;
.nz-icon-shuxing{
margin-right: 20px;
}
}
.file-arrow-header{
display: inline-flex;align-items: center;width: 100%
}
.nz-arrow-box{
display: flex;flex-direction: column;align-items: center;margin-left: 7px
}
.nz-arrow-up{
margin-bottom: 5px;
}
.nz-arrow-up.is-select{
border-bottom:5px solid $--color-primary; /* 定义底部颜色 */
}
.nz-arrow-down.is-select{
border-top:5px solid $--color-primary; /* 定义底部颜色 */
}
.nz-icon-caret-up {
position: absolute;
}
.nz-icon-caret-down {
position: absolute;
}
.file-item{
font-family: Roboto-Regular;
font-size: 14px;
color: $--color-text-regular;
font-weight: 400;
margin-top: 8px;
line-height: 24px;
height: 24px;
}
.file-item:hover{
background: rgba(255,134,0,0.50);
font-family: Roboto-Regular;
}
.my-loading-box{
background: #1a1a1a;
}
.nz-icon:hover{
color: $--color-primary;
}
.file-info-item-header{
padding-bottom: 20px; border-bottom: 1px solid $--border-color-light;
}
.file-info-item{
display: flex;
margin-top: 10px;
align-items: center;
.file-info-item-left{
width: 180px;
flex-shrink: 1;
}
.file-info-item-right{
width: 200px;
flex-shrink: 1;
}
}
}
div.nz-arrow-up {
width:0px;
height:0px;
border-left:5px solid transparent; /* 右透明 */
border-right:5px solid transparent; /*右透明 */
border-bottom:5px solid $--color-text-disabled; /* 定义底部颜色 */
font-size:0px;
line-height:0px;
cursor: pointer;
}
/* css3三角形向下 ▼) */
div.nz-arrow-down {
width:0px;
height:0px;
border-left:5px solid transparent;
border-right:5px solid transparent;
border-top:5px solid $--color-text-disabled;
font-size:0px;
line-height:0px;
cursor: pointer;
}

View File

@@ -0,0 +1,129 @@
.file-state-panel{
position: absolute;
z-index: 10;
right:0;
top:-100px;
max-height: 246px;
min-width: 96px;
width: 308px;
background: $--background-color-empty;
box-shadow: -2px 1px 4px 0 rgba(0,0,0,0.06), 1px 1px 4px -1px rgba(0,0,0,0.16);
border-radius: 2px;
.file-state-panel-content::-webkit-scrollbar {
width: 8px;
}
.file-state-panel-content::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.16);
border-radius: 4px;
border:none
}
.file-state-panel-content::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,0.16);
border-radius: 4px;
border:none
}
.file-state-panel-header{
height: 48px;
background: $--background-color-empty;
box-shadow: 0 1px 0 0 $--background-color-disabled;
padding: 0 20px;
line-height: 48px;
display: flex;
justify-content: space-between;
text-transform: capitalize;
> i {
color: $--color-text-primary;
}
}
.file-state-panel-title{
font-size: 14px;
letter-spacing: 0;
font-weight: 500;
text-transform: capitalize;
color: $--color-text-primary;
}
.file-state-panel-content {
min-height: 50px;
max-height: 180px;
line-height: 48px;
padding-bottom: 15px;
border-radius: 0 0 2px 2px;
width: 100%;
overflow-y: auto;
.file-state-panel-item{
height: 48px;
width: 100%;
box-sizing: border-box;
padding: 3px 14px 3px 20px;
display: flex;
align-items: center;
justify-content: space-around;
.item-icon{
width: 28px;
height: 28px;
background: $--background-color-base;
border-radius: 2px;
color: $--color-text-regular;
display: flex;
justify-items: center;
justify-content: center;
align-items: center;
flex-shrink:0;
}
.item-progress{
width: calc(100% - 86px);
flex: 1;
margin: 0 10px;
display: flex;
flex-direction: column;
.item-progress-top{
width: 100%;
color: $--color-text-regular;
height: 15px;
line-height: 15px;
font-size: 12px;
font-weight: 400;
margin-bottom: 4px;
}
.item-progress-middle{
}
.item-progress-bottom{
margin-top: 4px;
display: flex;
justify-content: space-between;
font-size: 10px;
color: $--color-text-secondary;
line-height: 12px;
font-weight: 400;
}
}
.item-state{
flex-shrink:0;
width: 28px;
height: 28px;
color: $--color-text-regular;
line-height: 28px;
text-align: center;
}
}
}
.file-nodata{
text-align: center;
color: #b7b7b7;
padding-top: 20px;
padding-bottom: 15px;
.table-no-data__title{
text-align: center;
color: #b7b7b7;
}
}
.translationOriginUp{
transform-origin: 70% 100%
}
.translationOriginDown{
transform-origin: 70% 0%
}
.el-progress-bar__outer{
background-color: $--el-progress-bar__outer;
}
}

View File

@@ -126,7 +126,21 @@
background: #000 !important;
left: -2px;
}
.right-tip {
position: absolute;
left: 9px;
top: -8px;
padding: 0 6px;
line-height: 15px;
height: 15px;
background-color: #ba3939;
opacity: .9;
border-radius: 7px;
color: white;
font-size: 6px;
}
}
.shell-service-max {
height: 100%;
position: fixed !important;
@@ -239,6 +253,38 @@
.popper__arrow{
display: none;
}
.webshell-box-top{
border-bottom: 1px solid rgba(25,25,28,0.10);
}
.webshell-box-bottom{
border-top: 1px solid rgba(25,25,28,0.10);
}
.webshell-box-item{
height: 32px;
font-size: 14px;
color: $--color-text-primary;
letter-spacing: 0;
line-height: 32px;
.nz-icon {
margin-right: 8px;
}
}
.webshell-box-item:hover{
background: $--background-color-base;
color: $--color-primary;
}
}
.popover-webshell-new{
padding: 0 !important;
.webshell-box-top, .webshell-box-bottom{
height: 40px;
line-height: 40px;
box-sizing: border-box;
}
.webshell-box-top, .webshell-box-bottom,.webshell-box-item{
padding-left: 15px;
}
}
div.sp-header{
display: none;
@@ -275,4 +321,13 @@ div.sp-header{
.custom-footer {
box-shadow: none;
}
}
.ternimal-header{
display: flex;
width: 100%;
justify-content: space-between;
background: #101010;
box-shadow: 0 1px 0 0 #303030;
}

View File

@@ -0,0 +1,147 @@
.web-terminal-new{
height: calc(100vh - 68px);
background: $--background-color-base;
/*border-top: 1px solid #BEBEBE;*/
box-shadow: 0 1px 0 0 $--border-color-light;
/deep/ .el-tabs{
border: none;
height: 100%;
width:100%;
margin-left:0px;
.el-tabs__nav-prev, .el-tabs__nav-next{
display: inline-block;
background: $--background-color-2 !important;
width: 20px;
height: 36px;
text-align: center;
line-height: 40px;
color: $--color-text-primary;
}
.el-tabs__nav-prev.is-disabled, .el-tabs__nav-next.is-disabled{
cursor: pointer !important;
display: inline-block;
background: $--background-color-2 !important;
width: 20px;
height: 36px;
text-align: center;
line-height: 40px;
color: $--color-text-primary;
}
.el-tabs__header{
height: 36px;
width: 100%;
.el-tabs__item{
height: 36px;
transform: translateY(-2px);
padding: 0 10px 0 10px;
margin-top: 0;
border-top: 2px solid transparent;
width: 126px;
background: $--background-color-empty;
line-height: 34px;
display: inline-block;
box-sizing: border-box;
.el-tabs__item-label{
display: inline-flex;
width: calc(100% - 20px);
align-items: center;
.active-icon{
flex-shrink: 1;
}
.el-tabs__item-label-name{
flex: 1;
width: calc(100% - 30px);
}
}
.el-icon-close{
display: none;
}
}
.el-tabs__item.is-active {
width: 200px;
border-top: 2px solid $--color-primary;
font-size: 14px;
color: $--color-text-primary;
}
.icon-reference{
display: none;
}
.el-tabs__item:hover{
width: 200px;
.icon-reference{
display: inline-block;
}
.el-icon-close{
display: inline-block;
}
}
.el-tabs__item:last-of-type {
height: 36px;
padding: 0 5px;
border: none;
border-top: 2px solid transparent;
background: transparent;
width: 40px;
line-height: 34px;
}
}
.el-tabs__content{
height: calc(100% - 36px);
background: #000;
box-sizing: border-box;
overflow: unset;
padding: 5px 5px;
.el-tab-pane{
height: 100%;
}
}
}
}
.webTerminal{
height: 100%;
width: 100%;
overflow: hidden;
.web-terminal-header{
height: 36px;
width: 100%;
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding: 0 15px;
background: $--background-color-base;
box-shadow: inset 0 -1px 0 0 $--dropdown-menu-box-shadow-color;
font-size: 14px;
line-height: 36px;
color: $--color-text-primary;
img {
vertical-align: sub;
height: 20px;
width: 20px;
margin-right: 10px;
}
}
.right-tip {
position: absolute;
left: 8px;
top: 4px;
padding: 0 6px;
line-height: 15px;
height: 15px;
background-color: #ba3939;
opacity: .9;
border-radius: 7px;
color: white;
font-size: 6px;
}
.shell-input{
input {
background: #1E1E1E !important;
border: none;
}
input::input-placeholder{
color: #7C7C7C;
}
}
}

View File

@@ -97,21 +97,23 @@
}
}
.nz-btn-group {
#endpoint-query-changetime:hover {
#endpoint-query-changetime,
#recordRules-query-changetime:hover {
border: 1px solid $--asset-label-btn-border-color;
}
.nz-btn.nz-btn-size-normal.nz-btn-style-light.control-icon-unchecked:hover {
border: 1px solid $--asset-label-btn-border-color;
z-index: 2;
}
.nz-btn.nz-btn-size-normal.nz-btn-style-light.control-icon-unchecked {
.nz-btn.nz-btn-size-normal.nz-btn-style-light.control-icon-unchecked,
.nz-btn.nz-btn-size-normal.nz-btn-style-light.control-icon-checked {
margin-right: -1px;
}
.project-calendar.nz-input-group-middle {
border-left: 0;
border-right: 0;
border-top: 1px solid $--border-color-base;
border-bottom: 1px solid $--border-color-base;
border-top: 1px solid $--border-color-light;
border-bottom: 1px solid $--border-color-light;
display: flex;
align-items: center;
justify-content: center;
@@ -130,7 +132,7 @@
.nz-table-list .el-table--border {
.gutter {
position: fixed;
right: 31px;
right: 21px;
height: 41px;
border-bottom: 1px solid $--border-color-light;
background-color: $--background-color-empty;
@@ -240,6 +242,16 @@
border: 0 !important;
box-shadow: 0 0 0px 0px rgba(0, 0, 0, 0);
}
.recordRules-query .sub-container .nz-table-list {
height: 100%;
}
.recordRules-query .bottom-data-list .nz-btn-group .nz-input-group-middle input {
border-right: 0 !important;
}
.recordRules-query .bottom-data-list .nz-btn-group.nz-btn-group-size-normal.nz-btn-group-light {
border: 0 !important;
box-shadow: 0 0 0px 0px rgba(0, 0, 0, 0);
}
/* end--二级顶部工具栏*/
.log-no-data {
@@ -257,16 +269,72 @@
// process network vsys comment 二级页面
#processBottomTab,
#networkBottomTab,
#vsysBottomTab {
#vsysBottomTab,
#sftpBottomTab,
#terminalLogCMDTab,
#recordRuleEvalLog {
.sub-container .nz-table-list {
height: 100%;
}
#assetProcessTable,
#assetNetworkTable,
#assetVsysTable {
#assetVsysTable,
#terminalLogSftpTable,
#recordRuleEvalLogTable {
height: calc(100% - 15px) !important;
}
}
#assetProcessTable {
.el-popover__reference {
display: flex;
align-items: center;
}
.bar {
width: 3px;
height: 14px;
border-radius: 0;
margin-right: 0px;
line-height: 100%;
}
.process-name {
height: 100%;
margin-left: 6px;
line-height: 24px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#container {
width: 120px;
min-width: 120px;
height: 18px;
margin-right: 20px;
}
.percentage {
flex-grow: 1;
word-wrap: break-word;
}
.el-progress-bar__outer {
border-radius: 2px;
background-color: #e6eaed;
}
.el-progress-bar__inner {
border-radius: 0;
}
.active-icon {
margin-left: 5px;
}
}
#assetNetworkTable {
.process-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
// comments
#commentBottomTab {
.sub-container .nz-table-list {
@@ -365,3 +433,10 @@
box-sizing: content-box;
}
}
//record command 二级页面搜索
#terminalLogRecordTab {
.pagination-bottom{
bottom: 5px;
}
}

View File

@@ -146,7 +146,7 @@
outline: none;
border: 1px solid #ccc;
}
button:hover {
button:hover:not(.footer__btn) {
background: $--background-color-empty;
}
.nz-btn-style-normal {

View File

@@ -122,25 +122,39 @@
}
}
.dashboard-select-tail{
width: 162px;
background: #FF9219;
margin: 0 auto;
border: none;
display: flex;
justify-content: center;
border-radius: 2px;
cursor: pointer;
.dashboard-select-add{
width: 88%;
flex: 1;
height: 30px;
background: #FF9219;
border-radius: 2px;
font-size: 14px;
color: #fff;
display: flex;
font-size: 14px;
justify-content: center;
align-items: center;
cursor: pointer;
i{
color: #fff;
padding-left: 0;
}
}
.dashboard-addBy-template{
width: 30px;
height: 30px;
font-size: 14px;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
border-left: 1px solid #fff;
box-sizing: border-box;
position: relative;
}
}
}
.pop-item-wider{
@@ -166,3 +180,12 @@
background: none;
}
}
.dashboard-addBy-template-menu{
width: 162px;
// padding: 8px 0;
box-sizing: border-box;
.el-dropdown-menu__item{
// line-height: 32px;
}
}

View File

@@ -0,0 +1,79 @@
.nz-dialog.snapshot-dialog{
.el-dialog__header{
padding: 15px;
padding-right: 40px;
border-bottom: 1px solid $--border-color-light;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.el-dialog__title{
font-family: Roboto-Medium;
font-size: 16px;
color: $--color-text-primary;
letter-spacing: 0;
font-weight: 500;
}
}
.el-dialog__body{
padding: 20px;
padding-bottom: 30px;
.snapshot-name,.snapshot-time{
font-family: Roboto-Regular;
font-size: 14px;
color: $--color-text-regular;
letter-spacing: 0;
font-weight: 400;
}
.el-progress-bar{
margin-top: 20px;
overflow: hidden;
position: relative;
.el-progress-bar__outer{
height: 20px;
border-radius: 0;
background-color: $--background-color-2;
border: 1px solid $--border-color-light;
box-sizing: border-box;
overflow: unset;
.el-progress-bar__inner{
transition: width 0.3s linear;
border-radius: 0;
background-color:#3B92F1;
height: 18px;
// height: 20px;
// position: absolute;
// left: -1px;
// top: -1px;
}
}
}
.snapshot-elapsed{
font-family: Roboto-Black;
font-size: 12px;
color: $--color-text-regular;
letter-spacing: 0;
line-height: 17px;
font-weight: 400;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 2px;
}
.snapshot-msg{
font-family: Roboto-Regular;
font-size: 14px;
color: $--color-text-regular;
letter-spacing: 0;
line-height: 16px;
font-weight: 400;
margin-top: 20px;
}
}
.el-dialog__footer{
padding: 12px 20px;
border-top: 1px solid $--border-color-light;
.el-message-box__btns{
padding: 0;
}
}
}

View File

@@ -189,6 +189,9 @@
.sub-box {
padding: 0 10px;
}
.asset-dropdown.right-box-select-top{
width: unset;
}
.props-box {
.metric-selector-title {

View File

@@ -1,4 +1,4 @@
.endpoint-query-tab {
.endpoint-query-tab,.recordRules-query-tab {
.el-dialog__body {
height: calc(100% - 48px) !important;
padding-bottom: 0 !important;
@@ -31,4 +31,4 @@
cursor: not-allowed;
background-color: #f4f4f5;
}
}
}

View File

@@ -2,6 +2,9 @@
@import './charts/chart.scss';
@import './charts/chart-list.scss';
@import './cli/webSSH.scss';
@import './cli/fileDirectory.scss';
@import './cli/fileListState.scss';
@import './cli/webSSHNew.scss';
@import './common/alert/alertLabel.scss';
@import './common/alert/alertStateInfo.scss';
@import './common/alert/alertRuleInfo.scss';
@@ -26,6 +29,7 @@
@import './common/popBox/selectPanel.scss';
@import './common/popBox/selectDashboard.scss';
@import './common/popBox/selectWalk.scss';
@import './common/popBox/snapshotProgress.scss';
@import './common/project/popData/Info.scss';
@import './common/project/L5/popData/common.scss';
@import './common/project/L5/CanvasContextMenu.scss';

View File

@@ -42,3 +42,9 @@
color: $--color-text-primary;
text-align: center;
}
.agent{
.message-zindex{
z-index: 1000;
}
}

View File

@@ -3,7 +3,7 @@
position: relative;
overflow: auto;
margin: 0px;
.el-form{
.el-form {
padding-left: 15px;
}
.el-tag.el-tag--info {
@@ -36,34 +36,52 @@
.el-input.el-input--small {
width: calc(100% - 49px);
}
}
}
#system-basic{
#system-basic {
display: flex;
align-items: center;
.el-form-item__content{
.el-form-item__content {
margin-left: 0px !important;
}
}
}
.system-config-form{
.system-config-form.basicForm {
.vue-tags-input {
max-width: unset !important;
}
.vue-tags-input.interface-name .ti-input{
padding: 0px !important;
}
.vue-tags-input .ti-input {
min-height: 32px;
padding: 0px 0px 0px 15px !important;
}
.vue-tags-input .ti-input .ti-new-tag-input-wrapper {
padding: 0;
margin: 0;
}
.vue-tags-input.interface-name .ti-input .ti-new-tag-input-wrapper {
padding: 5px 0px 5px 7px;
}
}
.system-config-form {
width: 61.8% !important;
position: relative;
}
.linkBox{
.linkBox {
max-height: 800px;
width: 800px;
}
.scrollBox{
.scrollBox {
position: relative;
height: 600px;
margin-bottom: 20px;
}
.linkTitle{
.linkTitle {
display: flex;
align-items:center;
align-items: center;
border-bottom: 2px solid $--border-color-base;
color: #6b778c;
font-size: 12px;
@@ -71,40 +89,40 @@
line-height: 24px;
font-weight: 600;
}
.handle{
.handle {
cursor: move !important;
padding:0 5px;
padding: 0 5px;
line-height: 34px;
}
.linkBtn{
.linkBtn {
height: 28px;
line-height: 0;
font-size: 12px;
}
.linkTitleHandle{
.linkTitleHandle {
padding: 0 13px;
}
.linkAddBox{
.linkAddBox {
display: flex;
padding: 10px 0;
border-bottom: 1px solid $--border-color-base;
font-size: 13px;
line-height: 34px;
}
.linkContent{
.linkContent {
display: flex;
padding: 10px 0;
font-size: 13px;
line-height: 34px;
}
.linkContent:not(:last-child){
.linkContent:not(:last-child) {
border-bottom: 1px solid $--border-color-base;
}
.linkFormContent{
.linkFormContent {
display: flex;
line-height: 34px;
}
.linkTitleName{
.linkTitleName {
width: 100px;
margin-right: 12px;
overflow: hidden;
@@ -112,12 +130,12 @@
white-space: nowrap;
height: 31px;
}
.linkTitleUrl{
.linkTitleUrl {
width: 462px;
margin-right: 12px;
height: 31px;
}
.linkTitleUrlContent{
.linkTitleUrlContent {
display: inline-block;
width: 460px;
overflow: hidden;
@@ -127,39 +145,39 @@
cursor: pointer;
outline: none;
}
.linkFormBtn .nz-icon-delete{
.linkFormBtn .nz-icon-delete {
cursor: pointer;
color: $--color-primary;
margin-left: 10px;
}
.linkFormBtn .nz-icon-edit{
.linkFormBtn .nz-icon-edit {
font-size: 14px;
cursor: pointer;
}
.nz-icon-create-square{
.nz-icon-create-square {
cursor: pointer;
color: $--color-text-primary;
}
.el-form-item--small .el-form-item__error{
.el-form-item--small .el-form-item__error {
padding-top: 0;
}
.linkUrlTip{
.linkUrlTip {
width: 430px;
}
.system-config-form.terminal .el-input-number--small{
.system-config-form.terminal .el-input-number--small {
width: 512px;
}
.system-config-form.basicForm .el-input-number--small{
.system-config-form.basicForm .el-input-number--small {
width: 512px;
}
.system-config-form .el-input input{
.system-config-form .el-input input {
text-align: left;
}
.utc-item {
color: $--color-text-secondary;
font-size: 12px;
}
.system-title{
.system-title {
font-size: 14px;
color: $--color-text-regular;
letter-spacing: 0;
@@ -168,7 +186,7 @@
font-weight: 500;
margin-bottom: 30px;
}
.system-title-border{
.system-title-border {
border-top: 1px dashed $--border-color-light;
padding-top: 30px;
width: 699px;
@@ -178,19 +196,25 @@
background-color: $--background-color-empty;
padding: 10px;
.el-form-item:last-of-type.monitorLastEle {
.el-form-item__content{
.el-form-item__content {
margin-left: 180px !important;
margin-bottom: 20px;
}
}
.el-tabs__nav-scroll{
.el-form-item:last-of-type.monitorLastElement {
.el-form-item__content {
margin-left: 180px !important;
}
}
.el-tabs__nav-scroll {
background-color: $--background-color-empty;
}
.el-tabs__nav-scroll,.el-tabs__header,.el-tabs__nav-wrap{
.el-tabs__nav-scroll,
.el-tabs__header,
.el-tabs__nav-wrap {
border-bottom: 0;
}
.el-form-item:last-of-type {
.el-form-item__content {
margin-left: 0 !important;
text-align: center;
@@ -200,13 +224,14 @@
}
}
.full-table {
.el-tabs__content{
height: calc( 100% - 100px);
.el-tabs__content {
height: calc(100% - 100px);
}
.list-page .main-container{
.list-page .main-container {
padding: unset;
}
.table-column-setting,.pagination-bottom{
.table-column-setting,
.pagination-bottom {
display: none;
}
}
@@ -218,17 +243,17 @@
outline: none;
box-sizing: border-box;
}
.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active {
.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
color: $--color-primary;
background: $--background-color-empty;
border-bottom: none;
margin-left: .1px;
margin-left: 0.1px;
}
.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover {
.el-tabs--border-card > .el-tabs__header .el-tabs__item:not(.is-disabled):hover {
color: $--color-primary;
opacity: .8;
opacity: 0.8;
}
.el-tabs--border-card>.el-tabs__header .el-tabs__item {
.el-tabs--border-card > .el-tabs__header .el-tabs__item {
height: 40px;
background: $--background-color-base;
border: 1px solid $--border-color-light;
@@ -236,7 +261,7 @@
border-top-right-radius: 4px;
margin-top: 0;
}
.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(:first-child) {
.el-tabs--border-card > .el-tabs__header .el-tabs__item:not(:first-child) {
margin-left: 5px;
}
.el-input-group__prepend {
@@ -249,7 +274,10 @@
border: 1px solid $--border-color-light;
border-left: none;
}
.el-input-number__decrease,.el-input-number__increase,.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled {
.el-input-number__decrease,
.el-input-number__increase,
.el-input-number__decrease.is-disabled,
.el-input-number__increase.is-disabled {
background-color: $--popover-background-color;
}
.el-input-number__decrease {
@@ -273,21 +301,21 @@
border-color: $--color-primary;
}
}
.system-config-form .el-form-item__content{
.system-config-form .el-form-item__content {
width: 512px;
}
.system-config-form .el-input{
.system-config-form .el-input {
width: 512px;
text-align: left;
}
.system-tabs {
box-shadow: unset !important;
border:unset;
border: unset;
border-top: unset;
height: 100%;
}
.system-tabs.el-tabs--border-card{
.system-tabs.el-tabs--border-card {
box-shadow: unset !important;
height: 100%;
width: 100%;
@@ -295,7 +323,7 @@
border-left: unset;
background-color: $--background-color-empty;
}
.reset-form .el-checkbox-group{
.reset-form .el-checkbox-group {
margin-left: -28px !important;
}
.el-form-item__tip {
@@ -318,22 +346,32 @@
margin-bottom: 54px;
}
@keyframes warnBackground {
0% {background-color: #FFFFFF;}
25% {background-color: #EF7458;}
50% {background-color: #FFFFFF;}
75% {background-color: #EF7458;}
100% {background-color: #FFFFFF;}
0% {
background-color: #ffffff;
}
25% {
background-color: #ef7458;
}
50% {
background-color: #ffffff;
}
75% {
background-color: #ef7458;
}
100% {
background-color: #ffffff;
}
}
.warn-pop {
background-color: #FEF0F0;
border-color: #FEF0F0;
color:#F66C6C;
background-color: #fef0f0;
border-color: #fef0f0;
color: #f66c6c;
}
.warn-pop .popper__arrow:after{
border-top-color: #FEF0F0 !important;
.warn-pop .popper__arrow:after {
border-top-color: #fef0f0 !important;
}
.system .logo-upload-tip{
color:#F66C6C;
.system .logo-upload-tip {
color: #f66c6c;
font-size: 12px;
line-height: 20px;
}

View File

@@ -157,7 +157,7 @@
.el-pagination{
font-size: 14px;
.btn-prev, .btn-next{
color: $--background-color-empty;
color: $--color-text-regular;
font-size: 14px;
margin: 0 5px;
border: none;
@@ -195,6 +195,9 @@
.el-dialog__headerbtn{
line-height: 32px;
}
}
.el-dialog__body{
}
.el-button{
//background-color: $--background-color-1 !important;

View File

@@ -16,6 +16,10 @@
border: 1px solid $--border-color-light;
}
}
.not-fixed-height.no-resize.no-close{
height: 100%;
box-sizing: border-box;
}
.query-row {
display: flex;

View File

@@ -97,6 +97,7 @@
height: 23px;
}
.date-pick-group {
display: flex;
align-items: center;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,153 @@
"css_prefix_text": "nz-icon-",
"description": "",
"glyphs": [
{
"icon_id": "680988",
"name": "属性",
"font_class": "shuxing",
"unicode": "e785",
"unicode_decimal": 59269
},
{
"icon_id": "33167519",
"name": "套接字文件",
"font_class": "taojieziwenjian",
"unicode": "e7bb",
"unicode_decimal": 59323
},
{
"icon_id": "33166559",
"name": "目录链接",
"font_class": "mululianjie",
"unicode": "e7b9",
"unicode_decimal": 59321
},
{
"icon_id": "33165863",
"name": "套接字文件链接",
"font_class": "taojieziwenjianlianjie",
"unicode": "e7ba",
"unicode_decimal": 59322
},
{
"icon_id": "33158668",
"name": "文件链接",
"font_class": "wenjianlianjie",
"unicode": "e7b5",
"unicode_decimal": 59317
},
{
"icon_id": "33158669",
"name": "管道文件链接",
"font_class": "guandaowenjianlianjie",
"unicode": "e7b6",
"unicode_decimal": 59318
},
{
"icon_id": "33158670",
"name": "块设备文件链接",
"font_class": "kuaishebeiwenjianlianjie",
"unicode": "e7b7",
"unicode_decimal": 59319
},
{
"icon_id": "33158671",
"name": "字符串设备文件链接",
"font_class": "zifuchuanshebeiwenjianlianjie",
"unicode": "e7b8",
"unicode_decimal": 59320
},
{
"icon_id": "33158636",
"name": "管道文件",
"font_class": "guandaowenjian",
"unicode": "e7b1",
"unicode_decimal": 59313
},
{
"icon_id": "33158637",
"name": "链接文件",
"font_class": "lianjiewenjian",
"unicode": "e7b2",
"unicode_decimal": 59314
},
{
"icon_id": "33158639",
"name": "块设备文件",
"font_class": "kuaishebeiwenjian",
"unicode": "e7b3",
"unicode_decimal": 59315
},
{
"icon_id": "33158641",
"name": "字符设备文件",
"font_class": "zifushebeiwenjian",
"unicode": "e7b4",
"unicode_decimal": 59316
},
{
"icon_id": "33112141",
"name": "home",
"font_class": "home",
"unicode": "e7af",
"unicode_decimal": 59311
},
{
"icon_id": "32964483",
"name": "File",
"font_class": "File",
"unicode": "e7ae",
"unicode_decimal": 59310
},
{
"icon_id": "32961611",
"name": "Clear",
"font_class": "Clear",
"unicode": "e7b0",
"unicode_decimal": 59312
},
{
"icon_id": "32660007",
"name": "Folder ",
"font_class": "Folder",
"unicode": "e7ad",
"unicode_decimal": 59309
},
{
"icon_id": "32659266",
"name": "file transfer",
"font_class": "a-filetransfer",
"unicode": "e7a7",
"unicode_decimal": 59303
},
{
"icon_id": "32659269",
"name": "reconnect",
"font_class": "reconnect",
"unicode": "e7a8",
"unicode_decimal": 59304
},
{
"icon_id": "32659270",
"name": "upper level",
"font_class": "a-upperlevel",
"unicode": "e7a9",
"unicode_decimal": 59305
},
{
"icon_id": "32659271",
"name": "new folder",
"font_class": "a-newfolder",
"unicode": "e7aa",
"unicode_decimal": 59306
},
{
"icon_id": "32659272",
"name": "SFTP",
"font_class": "SFTP",
"unicode": "e7ab",
"unicode_decimal": 59307
},
{
"icon_id": "586976",
"name": "map-sankey",

File diff suppressed because one or more lines are too long

View File

@@ -74,7 +74,7 @@
.el-icon {
display: block;
font-size: 12px;
font-size: 12px !important;
font-weight: bold;
}
}

View File

@@ -259,7 +259,8 @@ $--tooltip-background-color: #222329;
$--tooltip-border-color: rgba(112,116,122,0.6);
/* 17.label*/
$--label-background-color: $--background-color-empty;
/* 18 进度条颜色 */
$--el-progress-bar__outer: #19191C;
/*** themes/common.scss是与主题切换无关的变量 ***/
@import './src/common/var.scss';
@import './common.scss';

View File

@@ -254,7 +254,8 @@ $--tooltip-background-color: #ffffff;
$--tooltip-border-color: rgba(119,131,145,0.6);
/* 17.label*/
$--label-background-color: #D8d8d8;
/* 18 进度条颜色 */
$--el-progress-bar__outer: #ebeef5;
/*** themes/common.scss是与主题切换无关的变量 ***/
@import './src/common/var.scss';
@import './common.scss';

View File

@@ -1270,6 +1270,10 @@ li {
&>span{
margin-right: 12px;
}
.exists-select{
top: auto !important;
left: auto !important;
}
}
.import-select-list{
.import-select-item{
@@ -1282,7 +1286,7 @@ li {
}
.import-failContent-dialog{
.el-dialog__header{
padding: 14px;
padding: 15px;
border-bottom: 1px solid $--border-color-light;
}
.el-dialog__body{
@@ -1314,9 +1318,9 @@ li {
}
}
.result-detail{
height: 182px;
height: 184px;
margin: 10px 0;
padding: 16px 0;
padding: 18px 0;
box-sizing: border-box;
position: relative;
&>div{
@@ -1325,7 +1329,7 @@ li {
.nz-icon-override{
cursor: pointer;
position: absolute;
top: 5px;
top: 3px;
right: 15px;
}
.import-result-item {
@@ -1415,7 +1419,7 @@ li {
.dropdown-item-active {
color: $--color-primary !important;
font-weight: bold;
background-color: #FAFAFA;
background-color: $--background-color-base;
}
.el-dropdown-multi .el-dropdown-menu__item:focus {
color: $--color-text-regular;

View File

@@ -7,12 +7,12 @@
:close-delay=10
trigger="hover"
popper-class="chart-error-popper">
<div >{{errorText}}</div>
<span slot="reference" class="panel-info-corner panel-info-corner--error">
<i class="nz-icon nz-icon-warning fa"></i>
<span class="panel-info-corner-inner"></span>
</span>
</el-popover>
<div >{{errorText}}</div>
<span slot="reference" class="panel-info-corner panel-info-corner--error">
<i class="nz-icon nz-icon-warning fa"></i>
<span class="panel-info-corner-inner"></span>
</span>
</el-popover>
</span>
<span v-if="!isError&&!showAllData&&allDataLength>20" class="chart-header-error moreTitle">
<el-popover
@@ -20,16 +20,16 @@
:close-delay=10
trigger="hover"
popper-class="chart-warring-popper">
<div class="moreTitle" @click="loadMore">{{$t('dashboard.panel.moreTitle')}}{{$t('dashboard.panel.showAll')}}{{allDataLength}}</div>
<span slot="reference" class="panel-info-corner panel-info-corner--error" @click="loadMore">
<i class="nz-icon nz-icon-warning fa"></i>
<span class="panel-info-corner-inner"></span>
</span>
<div class="moreTitle" @click="loadMore">{{$t('dashboard.panel.moreTitle')}}{{$t('dashboard.panel.showAll')}}{{allDataLength}}</div>
<span slot="reference" class="panel-info-corner panel-info-corner--error" @click="loadMore">
<i class="nz-icon nz-icon-warning fa"></i>
<span class="panel-info-corner-inner"></span>
</span>
</el-popover>
</span>
<div class="chart-header__title" :title="chartInfo.name">
<div class="chart-header__title" :title="nameFormate">
<slot name="title-icon"></slot>
{{chartInfo.name}}
{{nameFormate}}
</div>
<div class="chart-header__tools">
<span v-if="chartInfo.remark" class="chart-header__tool top-tool-btn-group">
@@ -51,7 +51,7 @@
></pick-time>
</span>
<span class="chart-header__tool" v-if="showSaveBtn(from)">
<button id="endpoint-query-full-chart-save" v-has="'main_add'" class="nz-btn nz-btn-size-large nz-btn-style-normal" @click="saveChart">{{$t('dashboard.metric.saveChart')}}</button>
<button id="endpoint-query-full-chart-save" v-has="'main_add'" class="nz-btn nz-btn-size-large nz-btn-style-normal" @click="saveChart">{{$t('dashboard.metric.saveChart')}}</button>
</span>
<span class="chart-header__tool" @click="closeDialog" :title="$t('overall.close')">
<i class="nz-icon nz-icon-close" style="font-size: 16px;"></i>
@@ -159,6 +159,7 @@ export default {
showUnit (type) {
switch (type) {
case 'endpointQuery' :
case 'recordRulesQuery' :
return true
default: return false
}
@@ -166,6 +167,7 @@ export default {
showSaveBtn (type) {
switch (type) {
case 'endpointQuery' :
case 'recordRulesQuery' :
return true
default: return false
}

View File

@@ -699,7 +699,7 @@ const unitOptions = [
}
]
const units = []
window.onload = function () {
window.addEventListener('load', function () {
if (units.length < 1) {
unitOptions.forEach((item, index) => {
item.children.forEach((n, i) => {
@@ -707,8 +707,7 @@ window.onload = function () {
})
})
}
}
})
export default {
unitOptions: function () {
return unitOptions

View File

@@ -40,10 +40,10 @@
</span>
</el-popover>
</span>
<div class="chart-header__title" v-if="!isGroup" :title="chartInfo.name">{{chartInfo.name}}</div>
<div class="chart-header__title" v-if="!isGroup" :title="nameFormate">{{nameFormate}}</div>
<div class="chart-header__title groupTitle" v-else >
<span @click="groupShow"> <i class="nz-icon" :class="chartInfo.param.collapse ? 'nz-icon-arrow-right': 'nz-icon-arrow-down'"></i></span>
{{chartInfo.name}}
<span @click="groupShow" style="cursor:pointer"> <i class="nz-icon" :class="chartInfo.param.collapse ? 'nz-icon-arrow-right': 'nz-icon-arrow-down'"></i></span>
{{nameFormate}}
<span v-show="chartInfo.param.collapse" class="collapse-content">({{chartData ? chartData.length : 0}} charts)</span>
</div>
<div class="temp"></div>
@@ -56,7 +56,7 @@
<i class="nz-icon nz-icon-info-normal tool__icon"></i>
</el-tooltip>
</span>
<span v-if="chartInfo.type=='group'" class="chart-header__tool" @click="addChartGroup">
<span v-if="chartInfo.type=='group'&&!chartInfo.repeatIndex" class="chart-header__tool" @click="addChartGroup">
<!-- <el-tooltip :content="$t('button.add')" effect="light" placement="top" > -->
<i class="nz-icon nz-icon-plus tool__icon" :title="$t('tip.add')"></i>
<!-- </el-tooltip> -->
@@ -71,7 +71,7 @@
<i class="nz-icon nz-icon-maxview tool__icon"></i>
</div>
</span>
<span class="chart-header__tool">
<span class="chart-header__tool" v-if="!chartInfo.repeatIndex">
<el-dropdown v-clickoutside="clickos" v-has="['main_edit','main_delete','main_add']" trigger="click">
<i class="el-icon-more tool__icon" @click.stop="dropdownMenuShow = !dropdownMenuShow" :title="$t('overall.more')"></i>
<el-dropdown-menu style="display: none" class="temp-dropdown"></el-dropdown-menu>

View File

@@ -86,6 +86,39 @@ export default {
this.$emit('unitChange', val)
}
},
computed: {
variablesArr () {
return this.$store.getters.getVariablesArr
},
// 所有类型 chart name 支持变量替换
nameFormate () {
let str = this.chartInfo.name
// group图表设置repeat的name替换
if (this.chartInfo.repeatVariable) {
const variable = this.chartInfo.repeatVariable
const reg = new RegExp('\\$' + variable, 'g')
str = str.replace(reg, this.chartInfo.repeatValue.replace(/\\"/g, '"').replace(/\\'/g, "'"))
}
let confirmReg = []
this.variablesArr.forEach(item => {
const reg = '$' + item.name // 后续需要考虑 item,name 使用特殊字符的问题
const index = this.chartInfo.name.indexOf(reg)
if (index !== -1) {
confirmReg.push(item)
}
})
confirmReg = this.$loadsh.orderBy(confirmReg, function (item) { // 根据 匹配的name的长度排序 避免匹配的 $a 没匹配 $asset的问题
return item.name.length
}, 'desc')
if (confirmReg.length) {
confirmReg.forEach(item => {
const reg = new RegExp('\\$' + item.name, 'g') // 后续需要考虑 item,name 使用特殊字符的问题
str = str.replace(reg, item.checked.map(label => label.replace(/\\"/g, '"').replace(/\\'/g, "'")).join('+'))
})
}
return str
}
},
watch: {
isError: {
immediate: true,

View File

@@ -243,7 +243,7 @@ export default {
},
movedEvent (i, newX, newY) {
if (!this.isGroup) {
this.moveChart()
this.moveChart(i, newX, newY)
this.onScroll(this.scrollTop)
} else {
bus.$emit('groupChildMove')
@@ -280,9 +280,32 @@ export default {
this.$set(this.copyDataList, index, chart)
this.onScroll(this.scrollTop)
},
moveChart () {
moveChart (id, newX, newY) {
if (!this.groupInfo) {
const moveItem = this.copyDataList.find(item => item.id == id)
const moveIndex = this.copyDataList.findIndex(item => item.id == id)
const repeatArr = []
this.copyDataList.forEach((item, index) => {
const moveId = String(id).split('-repeat-')[0]
const repeatId = String(item.id).split('-repeat-')[0]
if (moveId == repeatId && moveIndex != index) {
repeatArr.push({
id: item.id,
y: item.y
})
}
})
repeatArr.sort((a, b) => a.y - b.y)
this.copyDataList.forEach(item => {
repeatArr.forEach((subItem, subIndex) => {
if (item.id == subItem.id) {
item.y = moveItem.y + ((subIndex + 1) * 0.03)
}
})
})
}
this.onScroll(this.scrollTop)
const arr = this.copyDataList.filter(item => !item.staic)
const arr = this.copyDataList.filter(item => !item.staic && !item.repeatIndex)
const charts = []
let weight = 0
arr.forEach(item => {
@@ -393,7 +416,7 @@ export default {
bus.$emit('refreshPanel')
},
createChartSuccess (params) {
const arr = this.copyDataList.filter(item => !item.staic)
const arr = this.copyDataList.filter(item => !item.staic && !item.repeatIndex)
const charts = []
let weight = 0
arr.forEach(item => {
@@ -453,6 +476,10 @@ export default {
},
// 比较变量 图表是否显示/隐藏
compareVariables () {
// 防止group中的chartList执行
if (this.groupInfo) {
return
}
const isRegExp = (v) => {
let isReg
try {
@@ -539,7 +566,105 @@ export default {
}
})
this.onScroll(this.scrollTop)
},
// group设置repeat 便利变量重复渲染图表
repeatVariableFn () {
// 防止group中的chartList执行
if (this.groupInfo) {
return
}
// 先删除掉复制的数据
for (let index = 0; index < this.copyDataList.length; index++) {
const item = this.copyDataList[index]
if (item.repeatIndex > 0) {
this.copyDataList.splice(index, 1)
index--
} else if (item.repeatIndex == 0) {
// 置为原来的数据
delete item.repeatIndex
delete item.repeatVariable
delete item.repeatValue
item.children && item.children.forEach(children => {
delete children.repeatIndex
delete children.repeatVariable
delete children.repeatValue
})
}
}
this.$nextTick(() => {
// 先让数组根据 y 排序 之后直接判断顺序 即可判断 Group 是否相邻
let arr = this.$loadsh.cloneDeep(this.copyDataList)
arr = this.$loadsh.orderBy(arr, function (item) {
return item.y
})
const variablesRepeat = {}
for (let index = 0; index < arr.length; index++) { // 遍历找出所有需要 repeat的group 按照 name进行多次的分组
const item = arr[index]
const repeatVariable = this.$loadsh.get(item.param.repeat, 'variable')
if (item.type === 'group' && repeatVariable && this.$loadsh.get(this.showHidden[item.id], 'visibility') !== 'hidden') {
const itemPrev = arr[index - 1]
const repeatPrevVariable = this.$loadsh.get(itemPrev.param.repeat, 'variable')
if (itemPrev && itemPrev.type === 'group' && repeatPrevVariable === repeatVariable) {
const arr = variablesRepeat[repeatVariable]
arr[arr.length - 1].repeatArr.push(item)
arr[arr.length - 1].lastGroup = item
} else {
if (variablesRepeat[repeatVariable]) {
variablesRepeat[repeatVariable].push({
repeatArr: [item],
lastGroup: item
})
} else {
variablesRepeat[repeatVariable] = [{
repeatArr: [item],
lastGroup: item
}]
}
}
}
}
Object.keys(variablesRepeat).forEach(key => { // 遍历 variablesRepeat 确定需要插入的数组
const findVariables = this.variablesArr.find(item => item.name == key)
findVariables && findVariables.checked.forEach((subItem, subIndex) => {
variablesRepeat[key].forEach(group => {
group.repeatArr.forEach(item => {
if (subIndex > 0) {
// 复制数据 重新设置id
const repeatItem = this.$loadsh.cloneDeep(item)
repeatItem.id = item.id + '-' + 'repeat-' + subIndex
repeatItem.i = repeatItem.id
repeatItem.repeatIndex = subIndex
repeatItem.repeatVariable = key
repeatItem.repeatValue = subItem
repeatItem.y = group.lastGroup.y
repeatItem.children && repeatItem.children.forEach(children => {
children.id = children.id + '-' + 'repeat-' + subIndex
children.repeatIndex = subIndex
children.repeatVariable = key
children.repeatValue = subItem
})
// 复制数据
this.copyDataList.push(repeatItem)
} else {
const findItem = this.copyDataList.find(chart => chart.id === item.id)
this.$set(findItem, 'repeatIndex', 0)
this.$set(findItem, 'repeatVariable', key)
this.$set(findItem, 'repeatValue', subItem)
findItem.children && findItem.children.forEach(children => {
this.$set(children, 'repeatIndex', 0)
this.$set(children, 'repeatVariable', key)
this.$set(children, 'repeatValue', subItem)
})
this.$refs['chart' + findItem.id] && this.$refs['chart' + findItem.id][0] && this.$refs['chart' + findItem.id][0].getChartData()
}
})
})
})
})
})
}
},
created () {
this.firstInit = true
@@ -572,9 +697,11 @@ export default {
},
// 监听变量数组
variablesArr: {
handler () {
handler (newVal, oldVal) {
// 比较变量 图表是否显示/隐藏
this.compareVariables()
this.repeatVariableFn()
}
},
panelLock: {
@@ -673,6 +800,8 @@ export default {
this.copyDataList = JSON.parse(JSON.stringify(tempList))
// 比较变量 图表是否显示/隐藏
this.compareVariables()
this.repeatVariableFn()
tempList = null
setTimeout(() => {
this.gridLayoutShow = true

View File

@@ -207,9 +207,16 @@ export default {
value = self.$loadsh.get(myParams, label)
}
if (label) {
const reg = new RegExp(label + '=".+?"')
const reg = new RegExp(label + '=".+?"', 'g')
if (reg.test(legend)) {
const find = legend.match(reg)[0]
const ans = legend.match(reg)
let find = ''
ans.forEach(item => {
const index = legend.indexOf(item)
if (legend[index - 1] !== '_') {
find = item
}
})
value = find.substr(find.indexOf('"') + 1, find.lastIndexOf('"') - find.indexOf('"') - 1)
}
}

View File

@@ -187,9 +187,15 @@ export default {
if (!this.chartInfo.oldElements) { // 创建一个备份 用于判断变量替换 能拿到原本变量的位置
this.chartInfo.oldElements = this.chartInfo.elements ? JSON.parse(JSON.stringify(this.chartInfo.elements)) : []
}
this.chartInfo.elements = this.chartInfo.oldElements.filter(item => item.state) // 处理不显示的表达式
this.chartInfo.elements = this.chartInfo.elements.map(item => { // 处理表达式的变量
const variables = this.variablesReplace(item.expression)
// eslint-disable-next-line vue/no-mutating-props
this.chartInfo.elements = this.$loadsh.cloneDeep(this.chartInfo.oldElements.filter(item => item.state)) // 处理不显示的表达式
// eslint-disable-next-line vue/no-mutating-props
this.chartInfo.elements = this.chartInfo.elements.map((item, index) => { // 处理表达式的变量
// group图表设置repeat的表达式替换
if (this.chartInfo.repeatVariable) {
item.expression = this.variablesReplaceRepeat(item.expression)
}
const variables = this.variablesReplace((item.expression))
this.myVariables.push(variables)
return {
...item,
@@ -211,7 +217,7 @@ export default {
switch (this.chartInfo.datasource) {
case 'metrics':
case 'logs': {
if (this.from === fromRoute.chartTemp) {
if (this.from === fromRoute.chartTemp || this.from === fromRoute.dashboardTemp) {
setTimeout(() => {
this.chartData = [chartTempData.data.result]
this.chartData.forEach(item => {
@@ -230,7 +236,7 @@ export default {
urlPre += '/logs/loki'
}
let requests = elements.map((element) => {
if (this.from === fromRoute.chartTemp) {
if (this.from === fromRoute.chartTemp || this.from === fromRoute.dashboardTemp) {
return chartTempData
}
let query = `${urlPre}/api/v1/query_range?start=${startTime}&end=${endTime}&step=${step}`
@@ -260,7 +266,7 @@ export default {
})
if (this.multipleTime) {
const multipleRequests = elements.map((element) => {
if (this.from === fromRoute.chartTemp) {
if (this.from === fromRoute.chartTemp || this.from === fromRoute.dashboardTemp) {
return chartTempData
}
let query = `${urlPre}/api/v1/query_range?start=${startTime - this.minusTime / 1000}&end=${endTime - this.minusTime / 1000}&step=${step}`
@@ -325,7 +331,7 @@ export default {
}
case 'system': {
this.chartInfo.elements = this.chartInfo.param.datasource
if (this.from === fromRoute.chartTemp) {
if (this.from === fromRoute.chartTemp || this.from === fromRoute.dashboardTemp) {
setTimeout(() => {
this.chartData = [chartTempData.data.result]
this.loading = false
@@ -569,11 +575,19 @@ export default {
if (confirmReg.length) {
confirmReg.forEach(item => {
const reg = new RegExp('\\$' + item.name, 'g') // 后续需要考虑 item,name 使用特殊字符的问题
str = str.replace(reg, item.checked.map(label => label.replace(/\"/g, '\\"').replace(/\'/g, "\\'")).join('|'))
str = str.replace(reg, item.checked.map(label => label.replace(/\\"/g, '"').replace(/\\'/g, "'")).join('|'))
})
}
return str
},
// group图表repeat 表达式替换
variablesReplaceRepeat (expression) {
let str = expression
const variable = this.chartInfo.repeatVariable
const reg = new RegExp('\\$' + variable, 'g')
str = str.replace(reg, this.chartInfo.repeatValue.replace(/\\"/g, '"').replace(/\\'/g, "'"))
return str
},
getHexagonFigureData () {
return new Promise(resolve => {
this.$get('stat/alertMessage/topN', { size: 48, dimension: 'module' }).then(response => {
@@ -766,7 +780,7 @@ export default {
},
variablesArr: {
handler (n) {
const elements = this.chartInfo.oldElements || []
const elements = this.$loadsh.cloneDeep(this.chartInfo.oldElements) || []
const variables = elements.map((element) => {
return this.variablesReplace(element.expression)
})

View File

@@ -8,15 +8,31 @@
</style>
<template>
<div :id="'ternimalContainer'+idIndex" class="console">
<div class="ternimal-header">
<span style="color: #fff">
<div class="active-icon" :class="isLogin ? 'green-bg': 'red-bg'"></div>
{{userName}}
</span>
<span style="color: #fff">
<i class="nz-icon nz-icon-reconnect" v-show="isInit" style="margin-right: 22px" @click="reconnect"></i>
<i class="nz-icon nz-icon-SFTP" v-show="isLogin" v-if="userName && terminalType == '1'" style="margin-right: 22px" @click="showFileDir(true)"></i>
</span>
</div>
<div :id="'terminal'+idIndex" ></div>
<fileDirectory v-clickoutside="closeFileDir" :uuid="terminal.uuid" v-show="fileDirectoryShow" @close="showFileDir(false)" :fileDirectoryShow="fileDirectoryShow" ref="fileDirectory"/>
</div>
</template>
<script>
import Terminal from '../common/js/Xterm'
import fileDirectory from './fileDirectory'
export default {
name: 'console',
components: {
fileDirectory
},
props: {
terminal: { },
terminalType: { default: '1' },
idIndex: {
type: Number,
default: 0
@@ -40,6 +56,8 @@ export default {
obj: {
id: 2
},
isLogin: false,
isInit:false,
successBackContent: 'Connecting to',
failBackContent: 'Sorry',
connectFailContent: 'Connection failed',
@@ -47,7 +65,9 @@ export default {
psdCont: 'password: ',
conFinish: false,
conSuccessNum: 0,
inputSecret: false
inputSecret: false,
userName: '',
fileDirectoryShow: false
}
},
watch: {
@@ -65,7 +85,7 @@ export default {
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
consoleBox.style.height = `${consoleHeigt - 55}px`
// 调整终端可视区域高度
document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = `${consoleHeigt - 10}px`
document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = `${consoleHeigt - 10 - 30}px`
this.$nextTick(() => { // 解决进入全屏和退出全屏是底部隐藏
this.setFontSize(this.fontSize)
})
@@ -130,8 +150,7 @@ export default {
focusConsole () {
this.term.focus()
},
create () {
const that = this
beforeCreate () {
let rows = this.termimalRows
if (this.isFullScreen) {
// 容器高度设置100%
@@ -157,6 +176,42 @@ export default {
})
this.term.open(terminalContainer)
this.term.focus()
const params = {
width: this.terminal.width,
height: this.terminal.height,
cols: this.terminal.cols,
rows: this.terminal.rows,
host: this.$loadsh.get(this.terminal, 'custom.host', ''),
port: this.$loadsh.get(this.terminal, 'custom.port', ''),
assetId: this.$loadsh.get(this.terminal, 'assetId', ''),
accountId: this.$loadsh.get(this.terminal, 'accountId', ''),
authProtocol: this.$loadsh.get(this.terminal, 'custom.authProtocol', ''),
authProtocolPort: this.$loadsh.get(this.terminal, 'custom.authProtocolPort', ''),
authType: this.$loadsh.get(this.terminal, 'custom.authType', ''),
authUsername: this.$loadsh.get(this.terminal, 'custom.authUsername', ''),
authPin: this.$loadsh.get(this.terminal, 'custom.authPin', ''),
authPriKey: this.$loadsh.get(this.terminal, 'custom.authPriKey', ''),
authUserTip: this.$loadsh.get(this.terminal, 'custom.authUserTip', ''),
authPinTip: this.$loadsh.get(this.terminal, 'custom.authPinTip', '')
}
this.$post('/terminal/login', params).then(res => {
if (res.code == 200) {
this.terminal.uuid = res.data.uuid
this.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
this.create()
} else {
this.terminal.uuid = res.data.uuid
this.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
params.name = this.terminal.name
this.$emit('loginFail', params)
this.$message.error(res.msg)
}
})
},
create () {
const that = this
const token = localStorage.getItem('nz-token')
let baseUrl = JSON.parse(JSON.stringify(this.$axios.defaults.baseURL))
const protocol = window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws'
@@ -177,8 +232,10 @@ export default {
})
}
this.terminalSocket = new WebSocket(url)
// 连接成功
// 连接成功onclose
this.terminalSocket.onopen = () => {
this.isLogin = true
this.isInit = true
}
// 登录后,你输入的内容从后台服务返回
this.term.on('data', function (data) {
@@ -192,7 +249,6 @@ export default {
// 返回
this.terminalSocket.onmessage = function (evt) {
let backContent = evt.data
const welComIndex = backContent.indexOf(that.welcomeBackContent)
if (welComIndex > -1) { // 无服务器信息只与nezha进行了连接
const connectResult = {
@@ -235,13 +291,16 @@ export default {
}
// 关闭
this.terminalSocket.onclose = () => {
this.isLogin = false
// 报错sorry的还没来得及看信息就关闭
// this.$emit("closeConsole",this.terminal.name);//
this.term && this.term.setOption('disableStdin', true)
}
// 错误
this.terminalSocket.onerror = (e) => {}
this.terminalSocket.onerror = (e) => {
this.isLogin = false
}
// 选中 复制
this.term.on('selection', function () {})
this.term.attachCustomKeyEventHandler(function (ev) { })
@@ -281,7 +340,7 @@ export default {
rows: this.term.rows
}
// 调整终端可视区域高度
document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = height + 'px'
document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = height - 30 + 'px'
this.$nextTick(() => {
this.term.resize(this.term.cols, this.term.rows)
this.$post('terminal/resize', winStyle).then(response => {
@@ -292,10 +351,33 @@ export default {
}
})
})
},
reconnect () {
this.isLogin = false
this.closeSocket()
this.term.off('selection')
this.term.off('data')
this.beforeCreate()
},
closeFileDir () {
this.showFileDir(false)
},
showFileDir (show) {
if (JSON.stringify(show) == JSON.stringify(this.fileDirectoryShow)) {
return
}
if (show) {
this.fileDirectoryShow = show
}
let animationClass = ''
animationClass = show ? 'backInUp' : 'backOutDown'
this.animateCSS(this.$refs.fileDirectory.$el, animationClass).then((message) => {
this.fileDirectoryShow = show
})
}
},
mounted () {
this.create()
this.beforeCreate()
},
beforeDestroy () {
this.closeSocket()
@@ -304,3 +386,6 @@ export default {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,329 @@
<style scoped>
.console{
height: 100%;
padding:5px 5px;
background-color: black;
position: relative;
}
</style>
<template>
<div :id="'ternimalContainer'+idIndex" class="console">
<div :id="'terminal'+idIndex" style="height: 100%"></div>
<fileDirectory :host="host" v-clickoutside="closeFileDir" :uuid="terminal.uuid" v-show="fileDirectoryShow" @close="showFileDir(false)" :fileDirectoryShow="fileDirectoryShow" ref="fileDirectory"/>
</div>
</template>
<script>
import Terminal from '../common/js/Xterm'
import fileDirectory from './fileDirectory'
export default {
name: 'console',
components: {
fileDirectory
},
props: {
terminal: { },
terminalType: { default: '1' },
idIndex: {
type: Number,
default: 0
},
isFullScreen: {
type: Boolean,
default: false
},
fontSize: {}
},
data () {
return {
term: null,
terminalSocket: null,
termimalRows: 15,
termimalHeight: 270,
topMenuHeight: 30,
dragHeigh: 8,
minRow: 1,
fontSpaceHeight: 2, // 一行高度=fontSize+fontSpaceHeight
obj: {
id: 2
},
isInit: false,
successBackContent: 'Connecting to',
failBackContent: 'Sorry',
connectFailContent: 'Connection failed',
welcomeBackContent: 'Welcome',
psdCont: 'password: ',
conFinish: false,
conSuccessNum: 0,
inputSecret: false,
userName: '',
fileDirectoryShow: false,
host: ''
}
},
watch: {
},
methods: {
prompt (term) {
term.write('\r\n ')// term.write('\r\n~$ ');
},
resize (consoleHeigt, consoleWidth) {
this.term.fit()
this.resizeServiceConsole()
},
resizeServiceConsole () {
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
const width = document.body.clientWidth// 可视宽度
const height = parseInt(consoleBox.offsetHeight)
const winStyle = {
width: width,
height: height - 100,
cols: this.term.cols, // cols和rows在resizeConsole方法已经设置
rows: this.term.rows
}
this.$post('terminal/resize', winStyle).then(response => {
if (response.code === 200) {
this.term.fit()
} else {
this.$message.error(response.msg)
}
})
},
focusConsole () {
this.term.focus()
},
beforeCreate () {
let rows = this.termimalRows
// const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
const height = document.body.clientHeight// 高度
// consoleBox.style.height = `${height - 120}px`
rows = (height - this.topMenuHeight) / (this.fontSize + this.fontSpaceHeight)
rows = parseInt(rows)
const terminalContainer = document.getElementById('terminal' + this.idIndex)
this.term = new Terminal({
rows: rows, // 15行大概300px高无法设置heigh只能设置rows
cursorStyle: 'block', // 光标样式 null | 'block' | 'underline' | 'bar'
disableStdin: false, // 是否应禁用输入
fontSize: this.fontSize
})
this.term.open(terminalContainer)
this.term.focus()
const params = {
width: this.terminal.width,
height: this.terminal.height,
cols: this.terminal.cols,
rows: this.terminal.rows,
host: this.$loadsh.get(this.terminal, 'custom.host', ''),
port: this.$loadsh.get(this.terminal, 'custom.port', ''),
assetId: this.$loadsh.get(this.terminal, 'assetId', ''),
accountId: this.$loadsh.get(this.terminal, 'accountId', ''),
authProtocol: this.$loadsh.get(this.terminal, 'custom.authProtocol', ''),
authProtocolPort: this.$loadsh.get(this.terminal, 'custom.authProtocolPort', ''),
authType: this.$loadsh.get(this.terminal, 'custom.authType', ''),
authUsername: this.$loadsh.get(this.terminal, 'custom.authUsername', ''),
authPin: this.$loadsh.get(this.terminal, 'custom.authPin', ''),
authPriKey: this.$loadsh.get(this.terminal, 'custom.authPriKey', ''),
authUserTip: this.$loadsh.get(this.terminal, 'custom.authUserTip', ''),
authPinTip: this.$loadsh.get(this.terminal, 'custom.authPinTip', '')
}
this.$post('/terminal/login', params).then(res => {
if (res.code == 200) {
this.terminal.uuid = res.data.uuid
this.$forceUpdate()
this.terminal.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
this.create()
} else {
this.terminal.uuid = res.data.uuid
this.terminal.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
params.name = this.terminal.name
this.$emit('loginFail', params)
this.$message.error(res.msg)
}
})
},
create () {
const that = this
const token = localStorage.getItem('nz-token')
let baseUrl = JSON.parse(JSON.stringify(this.$axios.defaults.baseURL))
const protocol = window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws'
if (baseUrl.startsWith('/')) {
baseUrl = `${protocol}://` + window.location.host + baseUrl
} else {
baseUrl = baseUrl.replace('http://', 'ws://').replace('https://', 'wss://')
}
let url = ''
this.terminal.height = document.body.clientHeight - 100
if (this.terminal.type === 'asset') {
url = baseUrl + '/terminal.ws?width=' + this.terminal.width + '&height=' + this.terminal.height + '&cols=' + this.terminal.cols + '&rows=' + this.terminal.rows + '&token=' + token + '&assetId=' + this.terminal.assetId + '&accountId=' + this.terminal.accountId + '&uuid=' + this.terminal.uuid
} else if (this.terminal.type === 'custom') {
url = baseUrl + '/terminal.ws?width=' + this.terminal.width + '&height=' + this.terminal.height + '&cols=' + this.terminal.cols + '&rows=' + this.terminal.rows + '&token=' + token + '&accountId=' + this.terminal.accountId + '&uuid=' + this.terminal.uuid
Object.keys(this.terminal.custom).forEach(key => {
if (this.terminal.custom[key]) {
url += '&' + key + '=' + this.terminal.custom[key]
}
})
}
this.terminalSocket = new WebSocket(url)
// 连接成功onclose
this.terminalSocket.onopen = () => {
this.terminal.isLogin = true
this.isInit = true
}
// 登录后,你输入的内容从后台服务返回
this.term.on('data', function (data) {
/*
let code = data.charCodeAt(0);
if(code==13){
}else {
//that.term.write(data);
} */
})
// 返回
this.terminalSocket.onmessage = function (evt) {
let backContent = evt.data
const welComIndex = backContent.indexOf(that.welcomeBackContent)
if (welComIndex > -1) { // 无服务器信息只与nezha进行了连接
const connectResult = {
title: '',
color: 1
}
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
} else {
const successContentIndex = backContent.indexOf(that.successBackContent)
if (successContentIndex > -1) {
// that.conFinish = true;
const startIndex = successContentIndex + that.successBackContent.length + 1
backContent = backContent.substring(startIndex)
const endIndex = backContent.indexOf('\r\n')
const title = backContent.substring(0, endIndex)
const connectResult = {
title: title,
color: 2
}
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
} else { // 失败
const failContentIndex = backContent.indexOf(that.failBackContent)
const connectFailIndex = backContent.indexOf(that.connectFailContent)
if (failContentIndex > -1) {
// that.conFinish = true;
const connectResult = {
title: '',
color: 3
}
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
} else if (connectFailIndex > -1) {
const connectResult = {
title: '',
color: 3
}
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
}
}
}
}
// 关闭
this.terminalSocket.onclose = () => {
this.terminal.isLogin = false
// 报错sorry的还没来得及看信息就关闭
// this.$emit("closeConsole",this.terminal.name);//
this.term && this.term.setOption('disableStdin', true)
}
// 错误
this.terminalSocket.onerror = (e) => {
this.terminal.isLogin = false
}
// 选中 复制
this.term.on('selection', function () {})
this.term.attachCustomKeyEventHandler(function (ev) { })
this.term.attach(this.terminalSocket)
this.term._initialized = true
this.term.fit()// 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸) 只是width
this.$nextTick(() => { // 解决进入全屏和退出全屏是底部隐藏
this.setFontSize(this.fontSize)
})
},
closeSocket () {
if (this.terminalSocket) {
this.terminalSocket.close()
this.terminalSocket = ''
}
if (this.term) {
this.term.destroy()
}
// 初始化console的高度
this.conFinish = false
},
setFontSize (fontSize) {
this.term && this.term.setOption('fontSize', fontSize)
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
const width = document.body.clientWidth// 可视宽度
let height = parseInt(consoleBox.offsetHeight)
if (height == null || !height) { height = this.termimalHeight }
const winStyle = {
width: width,
height: height,
cols: this.term.cols, // cols和rows在resizeConsole方法已经设置
rows: this.term.rows
}
// 调整终端可视区域高度
// document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = height - 30 + 'px'
this.$nextTick(() => {
this.term.resize(this.term.cols, this.term.rows)
this.$post('terminal/resize', winStyle).then(response => {
if (response.code === 200) {
this.term.fit()
} else {
this.$message.error(response.msg)
}
})
})
},
reconnect () {
this.terminal.isLogin = false
this.closeSocket()
this.term.off('selection')
this.term.off('data')
this.beforeCreate()
},
closeFileDir () {
this.showFileDir(false)
},
showFileDir (show) {
if (JSON.stringify(show) == JSON.stringify(this.fileDirectoryShow)) {
return
}
if (show) {
this.fileDirectoryShow = show
}
let animationClass = ''
animationClass = show ? 'backInRight' : 'backOutRight'
this.animateCSS(this.$refs.fileDirectory.$el, animationClass).then((message) => {
this.fileDirectoryShow = show
})
},
enterStr (message) {
if (this.terminalSocket && this.terminal.isLogin) {
this.terminalSocket.send(message)
setTimeout(()=>{
this.terminalSocket.send('\n')
}, 100)
}
}
},
mounted () {
this.beforeCreate()
},
beforeDestroy () {
this.closeSocket()
this.term.off('selection')
this.term.off('data')
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,616 @@
<template>
<div class="fileDirectory">
<div class="file-directory-header">
{{$t('terminal.sftp')}}
<span class="header-option">
<i class="nz-icon nz-icon-upload" @click="uploadFile"></i>
<i class="nz-icon nz-icon-close" @click="$emit('close')"></i>
</span>
</div>
<div class="file-directory-path" v-clickoutside="hideEditPath">
<span v-show="!editPathShow" class="breadcrumb-box">
<span @click="getSftpPath('/')" class="breadcrumb-item breadcrumb-action"><i class="nz-icon nz-icon-home"/></span>
<span v-for="(item,index) in breadcrumb" :key="index" class="breadcrumb-item">
/<span class="breadcrumb-action" @click="gotoPath(item,index)">{{item}}</span>
</span>
<i class="nz-icon nz-icon-edit" @click="showEditPath"/>
</span>
<span v-show="editPathShow" style="display: inline-block;flex: 1;padding-right: 40px">
<el-input v-model="editPath" size="mini" @keyup.enter.native="goEditPath">
<span slot="suffix">
{{$t('terminal.confirm')}}
</span>
</el-input>
</span>
<div class="path-option">
<span style="margin-right: 5px">Show hide File: </span>
<el-switch v-model="showHideFile"/>
<i class="nz-icon nz-icon-a-newfolder" @click="newFolderBoxShow = true"></i>
<i class="nz-icon nz-icon-upload" @click="uploadFile"></i>
</div>
</div>
<div style="padding: 0 20px;height: calc(100% - 50px)" v-my-loading="fileDirectoryLoading">
<div class="directory-content-header">
<div class="text-ellipsis file-name">
<div class="file-arrow-header" style="cursor: pointer" @click.stop="orderBy('nameOrderType','' , 'name')">
<div>
{{$t('overall.name')}}
</div>
<div class="nz-arrow-box">
<div class="nz-arrow-up" :class="{
'is-select': nameOrderType === 'asc'
}" @click.stop="orderBy('nameOrderType','asc' , 'name')"></div>
<div class="nz-arrow-down" :class="{
'is-select': nameOrderType === 'desc'
}" @click.stop="orderBy('nameOrderType','desc', 'name')"></div>
</div>
</div>
</div>
<div class="text-ellipsis file-size">
<div class="file-arrow-header" style="cursor: pointer" @click.stop="orderBy('sizeOrderType','' , 'size')">
<div>
{{$t('backup.size')}}
</div>
<div class="nz-arrow-box">
<div class="nz-arrow-up" :class="{
'is-select': sizeOrderType === 'asc'
}" @click.stop="orderBy('sizeOrderType','asc' , 'size')"></div>
<div class="nz-arrow-down" :class="{
'is-select': sizeOrderType === 'desc'
}" @click.stop="orderBy('sizeOrderType','desc', 'size')"></div>
</div>
</div>
</div>
<div class="text-ellipsis file-date">
<div class="file-arrow-header" style="cursor: pointer" @click.stop="orderBy('dateOrderType','' , 'cts')">
<div>
{{$t('issue.createTime')}}
</div>
<div class="nz-arrow-box" style="">
<div class="nz-arrow-up" :class="{
'is-select': dateOrderType === 'asc'
}" @click.stop="orderBy('dateOrderType','asc', 'cts')"></div>
<div class="nz-arrow-down" :class="{
'is-select': dateOrderType === 'desc'
}" @click.stop="orderBy('dateOrderType','desc', 'cts')"></div>
</div>
</div>
</div>
<div class="text-ellipsis file-opt">
{{$t('overall.option')}}
</div>
</div>
<div class="file-directory-content"
onselectstart="return false;"
style="-moz-user-select:none;">
<div v-if="fileDirectory !== '/'" @dblclick.prevent="backFileDirectory" class="file-item">
<div class="text-ellipsis file-name">
<i class="nz-icon nz-icon-Folder colorFA901C" style="margin-right: 3px"/>
..
<!-- {{$t('terminal.back')}}-->
</div>
</div>
<div
v-for="(item,index) in fileList"
v-show="showHideFile || !item.isHide"
:key="index" class="file-item"
onselectstart="return false;"
style="-moz-user-select:none;"
@dblclick.prevent="selectFile(item)"
>
<div class="text-ellipsis file-name">
<i class="nz-icon" :class="selIcon(item)"/>
<i class="nz-icon nz-icon-link" v-if="item.isLink"/>
{{item.name}} <span v-if="item.isLink">-> {{item.linkName}}</span>
</div>
<div class="text-ellipsis file-size">
<span v-if="!item.isDir">{{bytes(item.size, 0, 0)}}</span>
</div>
<div class="text-ellipsis file-date">
<span>{{momentTz(item.cts * 1000)}}</span>
</div>
<div class="text-ellipsis file-opt">
<i class="nz-icon nz-icon-shuxing" @click.stop="showFileInfo(item)"/>
<el-dropdown size="medium" trigger="click" @command="tableOperation">
<div class="table-operation-item table-operation-item--more" @click.stop="" :title="$t('overall.moreOperations')">
<i class="nz-icon nz-icon-more3"></i>
</div>
<el-dropdown-menu slot="dropdown" class="right-box-select-top right-public-box-dropdown-top">
<el-dropdown-item
:command="['rename', item]">
<i class="nz-icon nz-icon-edit"></i>
<span class="operation-dropdown-text">
{{$t('terminal.rename')}}
</span>
</el-dropdown-item>
<el-dropdown-item
:disabled="item.isDir"
:command="['download', item]">
<i class="nz-icon nz-icon-download1"></i>
<span class="operation-dropdown-text">
{{$t('overall.download')}}
</span>
</el-dropdown-item>
<el-dropdown-item
:disabled="item.isDir"
:command="['del', item]">
<i class="nz-icon nz-icon-delete"></i>
<span class="operation-dropdown-text">
{{$t('overall.delete')}}
</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- <i class="nz-icon nz-icon-download1" v-if="!item.isDir" @click="downloadFile(item)"></i>-->
<!-- <i class="nz-icon nz-icon-delete" v-if="!item.isDir" @click="delFile(item)"></i>-->
</div>
</div>
</div>
</div>
<el-dialog
class="nz-dialog snapshot-dialog"
width="472px"
:title='$t("overall.newFolder")'
destroy-on-close
:modal-append-to-body="false"
:visible.sync="newFolderBoxShow"
@close="newFolder(false)"
>
<div style="display: flex; align-items: center">
<div style="width: 100px;flex-shrink: 1;text-transform: capitalize">{{$t('overall.folderName')}}</div>
<el-input v-model="folder" size="small" style="flex: 1"/>
</div>
<div slot="footer">
<div class="el-message-box__btns">
<button class="nz-btn el-button el-button--small el-button--default" @click="newFolder(false)">
<span>{{$t('overall.cancel')}}</span>
</button>
<button class="nz-btn el-button--small nz-btn-style-normal" @click="newFolder(true)">
<span style="text-transform:Capitalize">{{$t('overall.create')}}</span>
</button>
</div>
</div>
</el-dialog>
<el-dialog
class="nz-dialog snapshot-dialog"
width="472px"
:title='$t("terminal.rename")'
destroy-on-close
:modal-append-to-body="false"
:visible.sync="renameBox"
@close="renameBox = false"
>
<div style="display: flex; align-items: center">
<div style="width: 100px;flex-shrink: 1;text-transform: capitalize">{{$t('overall.name')}}</div>
<el-input v-model="renameStr" size="small" style="flex: 1"/>
</div>
<div slot="footer">
<div class="el-message-box__btns">
<button class="nz-btn el-button el-button--small el-button--default" @click="renameBox = false">
<span>{{$t('overall.cancel')}}</span>
</button>
<button class="nz-btn el-button--small nz-btn-style-normal" @click="setRename">
<span style="text-transform:Capitalize">{{$t('overall.save')}}</span>
</button>
</div>
</div>
</el-dialog>
<el-dialog
class="nz-dialog snapshot-dialog"
width="472px"
:title='$t("overall.delete")'
destroy-on-close
:modal-append-to-body="false"
:visible.sync="delDialog"
@close="delDialog = false"
>
<div style="display: flex; align-items: center" v-if="delObj">
{{$t('terminal.delinfo',{fileName: delObj.name})}}
</div>
<div slot="footer">
<div class="el-message-box__btns">
<button class="nz-btn el-button el-button--small el-button--default" @click="delDialog = false" >
<span>{{$t('tip.no')}}</span>
</button>
<button class="nz-btn el-button--small nz-btn-style-normal" @click="delFile">
<span style="text-transform:Capitalize">{{$t('tip.yes')}}</span>
</button>
</div>
</div>
</el-dialog>
<!-- fileInfo-->
<el-dialog
class="nz-dialog snapshot-dialog"
width="472px"
:title='$t("overall.labels")'
destroy-on-close
:modal-append-to-body="false"
:visible.sync="fileInfoShow"
@close="fileInfoShow = false"
>
<div v-if="fileInfo">
<div class="file-info-item-header">
<i class="nz-icon" :class="selIcon(fileInfo)"/>
{{fileInfo.name}}
</div>
<div v-for="item in fileAttr" :key="item.name" class="file-info-item">
<div class="file-info-item-left" :class="{'is-disabled': fileInfo.isDir && item.key =='size'}">
{{$t(item.name)}} :
</div>
<div class="file-info-item-right">
{{selInfo(fileInfo, item.key)}}
</div>
</div>
</div>
<div slot="footer">
<div class="el-message-box__btns">
<button class="nz-btn el-button--small nz-btn-style-normal" @click="fileInfoShow = false">
<span style="text-transform:Capitalize">{{$t('overall.close')}}</span>
</button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import chartDataFormat from '@/components/chart/chartDataFormat'
export default {
name: 'fileDirectory',
props: {
uuid: {},
fileDirectoryShow: {},
host: {}
},
data () {
return {
fileDirectory: '/',
sizeOrderType: 0,
dateOrderType: 0,
nameOrderType: 0,
breadcrumb: [],
fileList: [],
oldFileList: [],
newFolderBoxShow: false,
folder: '',
fileDirectoryLoading: false,
timer: '',
editPathShow: false,
editPath: '',
showHideFile: false,
delObj: '',
delDialog: false,
renameBox: false,
renameStr: '',
fileInfo: '',
fileInfoShow: false,
fileAttr: [
{ name: 'overall.type', key: 'isDir' },
{ name: 'config.operationlog.ip', key: 'ip' },
{ name: 'asset.location', key: 'location' },
{ name: 'backup.size', key: 'size' },
{ name: 'terminal.modifyTime', key: 'uts' },
{ name: 'Owner', key: 'Owner' },
{ name: 'dashboard.panel.chartForm.group', key: 'group' },
{ name: 'config.menus.perms', key: 'permissionsString' }
]
}
},
computed: {
updateUuid () {
return this.$store.getters.getUpdateUuid
},
updateIndex () {
return this.$store.getters.getUpdateIndex
}
},
mounted () {
// this.init()
},
watch: {
fileDirectoryShow (n) {
if (n) {
this.init()
}
},
updateIndex (n) {
if (this.updateUuid == this.uuid) {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(() => {
this.getSftpPath(this.fileDirectory)
this.timer = null
}, 200)
}
}
},
methods: {
bytes: chartDataFormat.getUnit(7).compute,
init () {
this.getSftpPath(this.fileDirectory)
},
showEditPath () {
this.editPath = this.fileDirectory
this.editPathShow = true
},
hideEditPath () {
this.editPath = ''
this.editPathShow = false
},
goEditPath () {
this.getSftpPath(this.editPath)
setTimeout(() => {
this.editPath = ''
this.editPathShow = false
})
},
selectFile (item) {
if (item.isDir) {
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
this.getSftpPath(path + '/' + item.name)
}
return false
},
showFileInfo (item) {
this.fileInfo = item
this.fileInfoShow = true
},
newFolder (flag) {
if (!flag) {
this.newFolderBoxShow = false
this.folder = ''
return
}
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
const params = {
uuid: this.uuid,
path: path + '/' + this.folder
}
this.$post('/terminal/sftp/mkdir', params).then(res => {
this.newFolderBoxShow = false
this.getSftpPath(this.fileDirectory)
})
},
backFileDirectory () {
const arr = this.fileDirectory.split('/')
arr.pop()
const path = arr.join('/')
this.getSftpPath(path || '/')
return false
},
gotoPath (item, index) {
const path = '/' + this.breadcrumb.slice(0, index + 1).join('/')
this.getSftpPath(path || '/')
},
getSftpPath (path) {
const params = {
uuid: this.uuid,
path: path,
showHideFile: this.showHideFile
}
this.fileDirectoryLoading = true
this.$post('/terminal/sftp/ls', params).then(res => {
if (res.code === 200) {
this.fileDirectoryLoading = false
this.fileDirectory = res.data.path
res.data.list.forEach(item => {
if (item.name[0] === '.') {
item.isHide = true
}
})
this.fileList = res.data.list
this.oldFileList = res.data.list
this.editPath = ''
this.breadcrumb = res.data.path.split('/').filter(item => item)
if (this.nameOrderType) {
this.orderBy('nameOrderType',this.nameOrderType , 'name')
}
if (this.sizeOrderType) {
this.orderBy('sizeOrderType',this.sizeOrderType , 'size')
}
if (this.dateOrderType) {
this.orderBy('dateOrderType',this.dateOrderType , 'cts')
}
} else {
this.$message.error(res.msg)
this.editPath = ''
}
})
},
uploadFile () {
const params = {
uuid: this.uuid,
path: this.fileDirectory,
myId: 'upload' + this.uuid + new Date().getTime(),
type: 'upload',
isStart: false,
isFinish: false,
isError: false,
importFileList: [],
total: '',
speed: '',
fileLength: '',
startTime: '',
cancel: '',
axios: '',
timer: '',
done: 0,
delObj: ''
}
this.$store.dispatch('uploadFile', params)
},
downloadFile (item) {
if (item.timer) {
clearTimeout(item.timer)
}
item.timer = setTimeout(() => {
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
const params = {
...item,
uuid: this.uuid,
path: path + '/' + item.name,
myId: 'download' + this.uuid + new Date().getTime(),
type: 'download',
isStart: false,
isFinish: false,
isError: false,
total: '',
speed: '',
fileLength: '',
startTime: '',
cancel: '',
axios: ''
}
this.$store.dispatch('dispatchAddFileList', params)
}, 300)
},
setRename () {
this.renameBox = false
},
delFile () {
const item = this.delObj
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
const params = {
uuid: this.uuid,
path: path + '/' + item.name
}
this.$post('/terminal/sftp/rm', params).then(res => {
if (res.code == 200) {
this.delDialog = false
this.getSftpPath(this.fileDirectory)
} else {
this.delDialog = false
this.getSftpPath(this.fileDirectory)
this.$message.error(res.msg)
}
})
},
selIcon (item) {
// console.log(item)
const hz = item.name.split('.')[1]
if (item.isDir) {
return 'nz-icon-Folder colorFA901C'
}
if (item.isReg) {
return 'nz-icon-File'
}
if (item.isFifo) {
return 'nz-icon-guandaowenjian'
}
if (item.isSock) {
return 'nz-icon-taojieziwenjian'
}
if (item.isBlk) {
return 'nz-icon-kuaishebeiwenjian'
}
return 'nz-icon-File'
},
selInfo (item, key) {
if (key === 'isDir') {
if (item.isDir) {
return this.$t('terminal.catalogueFile')
} else {
return this.$t('backup.File')
}
}
if (key === 'ip') {
return this.host
}
// fileAttr: [
// { name: 'overall.type', key: 'isDir' },
// { name: 'config.operationlog.ip', key: 'ip' },
// { name: 'asset.location', key: 'location' },
// { name: 'backup.size', key: 'size' },
// { name: 'terminal.modifyTime', key: 'uts' },
// { name: 'Owner', key: 'Owner' },
// { name: 'dashboard.panel.chartForm.group', key: 'group' },
// { name: 'config.menus.perms', key: 'permissionsString' }
//
// ]
if (key === 'location') {
return this.fileDirectory
}
if (key === 'size' && !item.isDir) {
return this.bytes(item.size, 0, 0)
} else if (key === 'size' && item.isDir) {
return ''
}
if (key === 'uts') {
return this.momentTz(item.uts * 1000)
}
if (key === 'Owner') {
return item.uid
}
if (key === 'group') {
return item.gid
}
if (key === 'permissionsString') {
return item.permissionsString
}
return '-'
},
orderBy (key, type, order) {
let orderType = type
if (!orderType) {
orderType = this[key] === 'asc' ? 'desc' : 'asc'
}
this.sizeOrderType = 0
this.dateOrderType = 0
this.nameOrderType = 0
if (this[key] == orderType) {
this[key] = 0
this.fileList = this.oldFileList
} else if (orderType == 'nameOrderType') {
this[key] = orderType
let isDirArr = this.oldFileList.filter(item => item.isDir)
let isRegArr = this.oldFileList.filter(item => !item.isDir)
isDirArr = this.$loadsh.orderBy(isDirArr, [user => user.name.toLowerCase()], [orderType])
isRegArr = this.$loadsh.orderBy(isRegArr, [user => user.name.toLowerCase()], [orderType])
this.fileList = []
if (orderType === 'asc') {
this.fileList = this.fileList.concat(isDirArr, isRegArr)
} else {
this.fileList = this.fileList.concat(isRegArr, isDirArr)
}
} else {
this[key] = orderType
let isDirArr = this.oldFileList.filter(item => item.isDir)
let isRegArr = this.oldFileList.filter(item => !item.isDir)
isDirArr = this.$loadsh.orderBy(isDirArr, order, orderType)
isRegArr = this.$loadsh.orderBy(isRegArr, order, orderType)
this.fileList = []
if (orderType === 'asc') {
this.fileList = this.fileList.concat(isDirArr, isRegArr)
} else {
this.fileList = this.fileList.concat(isRegArr, isDirArr)
}
}
},
tableOperation ([command, row, param]) {
switch (command) {
case 'rename':
this.renameStr = row.name
this.renameBox = true
break
case 'download':
this.downloadFile(row)
break
case 'del':
this.delObj = row
this.delDialog = true
// this.delFile(row)
break
}
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,288 @@
<template>
<div>
<!-- 显示进度-->
<div class="file-state-panel" v-show="fileStateBox && fileList.length" :style="{'top': position.top + 'px', right: '10px'}" ref="fileStatePanel" :class="position.top>0? 'translationOriginDown': 'translationOriginUp'">
<div class="file-state-panel-header">
<span class="file-state-panel-title">{{$t('terminal.filetransfer')}}</span>
<i class="nz-icon nz-icon-Clear" @click="clearFileList"></i>
</div>
<div class="file-state-panel-content" v-if="fileList.length">
<div v-for="item in fileList" :key="item.myId" class="file-state-panel-item">
<div class="item-icon"> <i class="nz-icon" :class="{'nz-icon-upload':item.type === 'upload','nz-icon-download1':item.type === 'download'}"/></div>
<div class="item-progress">
<div class="item-progress-top text-ellipsis">{{item.name}}</div>
<div class="item-progress-middle">
<el-progress :show-text="false" :percentage="item.done" :color="customColorMethod.bind(item)"></el-progress>
</div>
<div class="item-progress-bottom">
<span>{{bytes(item.total, 0, 0)}}</span>
<span v-if="!item.isFinish && !item.isError">{{item.speed}}</span>
<span v-if="item.isFinish">Completed</span>
<span v-if="item.isError">Error</span>
</div>
</div>
<div class="item-state">
<i v-if="item.isFinish" class="nz-icon nz-icon-check" @click="removeRecord(item)"/>
<i v-else class="nz-icon nz-icon-close" @click="removeRecord(item)"/>
</div>
</div>
</div>
<div v-else class="file-nodata">
<svg class="icon" aria-hidden="true">
<use xlink:href="#nz-icon-no-data-list"></use>
</svg>
<div class="table-no-data__title">No results found</div>
</div>
</div>
<!-- 上传文件-->
<div style="display: none">
<el-upload
:ref="'upload'"
:auto-upload="false"
:on-change="importChange"
:multiple="false"
:file-list="importFileList"
action=""
class="upload-demo"
drag>
</el-upload>
</div>
</div>
</template>
<script>
import axios from 'axios'
import chartDataFormat from '@/components/chart/chartDataFormat'
export default {
name: 'fileListState',
computed: {
fileList () {
return this.$store.getters.getFileList
},
uploadItem () {
return this.$store.getters.getUploadItem
}
},
data () {
return {
myFileList: [],
importFileList: [],
position: {
top: 50,
right: 0
},
fileStateBox: false
}
},
watch: {
fileList: {
immediate: true,
handler (n) {
if (!n.length) {
this.fileStateBox = false
}
n.forEach(item => {
if (!item.isStart) {
item.total = 1
item.fileLength = 0
item.startTime = new Date().getTime()
item.speed = '0b/s'
if (item.type === 'download') {
item.isStart = true
this.download(item)
}
if (item.type === 'upload') {
item.isStart = true
this.upload(item)
}
}
})
}
},
uploadItem: {
handler (n) {
if (n && n.myId) {
this.$refs.upload.$children[0].$refs.input.click()
}
}
}
},
methods: {
bytes: chartDataFormat.getUnit(7).compute,
customColorMethod (percentage, item) {
if (percentage < 100) {
return '#3B92F1'
} else {
return '#21BF9A'
}
},
download (item) {
const self = this
const CancelToken = axios.CancelToken
item.cancel = CancelToken.source()
item.startTime = new Date().getTime()
const params = {
uuid: item.uuid,
path: item.path
}
axios.post('/terminal/sftp/download', {
...params
}, {
responseType: 'blob',
cancelToken: item.cancel.token,
onDownloadProgress: function (progressEvent) {
// 处理原生进度事件
item.total = progressEvent.total
item.fileLength = progressEvent.loaded
item.speed = item.fileLength / ((new Date().getTime() - item.startTime) / 1000)
item.speed = self.bytes(item.speed, 0, 0) + '/s'
item.done = (item.fileLength / item.total) * 100
// this.$set(this.fileList, 0,item)
}
}).then(res => {
item.isFinish = true
if (window.navigator.msSaveOrOpenBlob) {
// 兼容ie11
const blobObject = new Blob([res.data])
window.navigator.msSaveOrOpenBlob(blobObject, item.name)
} else {
const url = URL.createObjectURL(new Blob([res.data]))
const a = document.createElement('a')
document.body.appendChild(a) // 此处增加了将创建的添加到body当中
a.href = url
a.download = item.name
a.target = '_blank'
a.click()
a.remove() // 将a标签移除
}
}, error => {
const $self = this
const reader = new FileReader()
reader.onload = function (event) {
const responseText = reader.result
const exception = JSON.parse(responseText)
if (exception.message) {
$self.$message.error(exception.message)
} else {
console.error(error)
}
}
reader.readAsText(error.response.data)
})
},
importChange (file, fileList) {
if (fileList.length > 0) {
this.uploadItem.importFileList = [fileList[fileList.length - 1]]
}
if (this.uploadItem.importFileList.length) {
this.$store.dispatch('dispatchAddFileList', this.uploadItem)
}
},
removeRecord (item) {
if (!item.isFinish) {
item.cancel.cancel('operation canceled by the user.')
if (item.done || (item.fileLength === item.total)) { // 取消 上传 50-100
clearInterval(item.timer)
if (item.tid) {
this.$delete('/terminal/sftp/cancel/' + item.tid).then(res => {
if (res.code === 200) {
} else {
this.$message.error(res.msg)
}
})
}
}
}
this.$store.dispatch('dispatchDelFileList', item)
},
upload (item) {
const self = this
const CancelToken = axios.CancelToken
item.cancel = CancelToken.source()
const form = new FormData()
const importFile = item.importFileList[0]
item.name = importFile.name
form.append('file', importFile.raw)
form.append('uuid', item.uuid)
form.append('path', item.path)
axios({
url: '/terminal/sftp/upload',
method: 'PUT',
headers: { 'Content-Type': 'multipart/form-data' },
data: form,
cancelToken: item.cancel.token,
onUploadProgress: function (progressEvent) {
// 处理原生进度事件
item.total = progressEvent.total
item.fileLength = progressEvent.loaded
item.speed = item.fileLength / ((new Date().getTime() - item.startTime) / 1000)
item.speed = self.bytes(item.speed, 0, 0) + '/s'
item.done = (item.fileLength / (item.total * 2)) * 100
}
}).then(response => {
const res = response.data
// item.isFinish = true
if (res.code == 200) {
self.nextUpload(item, res.data.tid)
} else {
this.$message.error(res.msg)
}
})
},
nextUpload (item, tid) {
// this.$message.success(response.msg)
item.timer = setInterval(() => { // 上传 50-100
item.tid = tid
this.$get('/terminal/sftp/process/' + tid).then((res) => {
item.done = 50 + parseInt(res.data.done) / 2
item.speed = (item.fileLength + (item.done * 0.01 * item.total)) / ((new Date().getTime() - item.startTime) / 1000)
item.speed = this.bytes(item.speed, 0, 0) + '/s'
if (item.done === 100) {
item.isFinish = true
clearInterval(item.timer)
// todo 上传成功后 刷新对应terminal
this.$store.dispatch('upDateConsole', item.uuid)
}
})
}, 200)
},
fileStateShow (flag, type) {
if (JSON.stringify(flag) == JSON.stringify(this.fileStateBox)) {
return
}
if (flag) {
this.fileStateBox = flag
if (type === 'down') {
this.position = {
top: 45,
right: 0
}
} else {
this.position = {
top: -195,
right: 0
}
}
}
let animationClass = ''
animationClass = flag ? 'zoomIn' : 'zoomOut'
this.animateCSS(this.$refs.fileStatePanel, animationClass).then((message) => {
this.fileStateBox = flag
})
},
clearFileList () {
this.fileList.forEach(item => {
this.removeRecord(item)
})
setTimeout(() => {
this.$store.dispatch('dispatchFileList', [])
})
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,96 @@
<template>
<div class="webTerminal">
<div class="web-terminal-header">
<div> <img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo1-2.png')"/>Web terminal</div>
<div>
<div class="console-title-icon" style='right: 106px;display: inline;position: absolute' @click="showFileState" v-show="fileList.length">
<i class="nz-icon nz-icon-a-filetransfer" :title="$t('terminal.filetransfer')"></i>
<span v-show="fileList.length>0" class="right-tip">{{fileList.length<=99?fileList.length:'99+'}}</span>
</div>
{{name}}</div>
</div>
<fileListState v-clickoutside="hideFileState" ref="fileListState"/>
<webSSHNew ref="websshNew" />
<el-input :placeholder="'发送文本到所有SSH终端'" size="small" class="shell-input" v-model="message" @keyup.enter.native="sendMessage"/>
</div>
</template>
<script>
import webSSHNew from '@/components/cli/webSSHNew'
import fileListState from './fileListState'
export default {
name: 'terminal',
components: {
webSSHNew,
fileListState
},
data () {
return {
logo: '',
fileListStateType: '',
message: '',
name: ''
}
},
computed: {
language () { return this.$store.getters.getLanguage },
fileList () {
return this.$store.getters.getFileList
}
},
created () {
const self = this
window.addEventListener('setItemEvent', function (e) {
if (e.key == 'nz-sys-logo' && e.value) {
self.logo = e.value
}
})
this.logo = localStorage.getItem('nz-sys-logo')
},
mounted () {
const self = this
this.name = localStorage.getItem('nz-username')
window.onbeforeunload = () => {
const opener = window.opener
opener.postMessage(
JSON.stringify({
close: true
})
)
}
window.addEventListener('message', function (e) {
if (e.data) {
try {
const data = JSON.parse(e.data)
self.$get('asset/asset/' + data.id).then(res => {
const asset = res.data
self.$refs.websshNew.addConsole(asset.id, asset.manageIp, '', '', 'asset')
})
} catch (e) {
console.log(e)
}
}
})
},
methods: {
showFileState () {
let type = 'down'
this.fileListStateType = type = 'down'
this.$refs.fileListState.fileStateShow(!this.$refs.fileListState.fileStateBox, type)
},
hideFileState () {
this.$refs.fileListState.fileStateShow(false, this.fileListStateType)
},
sendMessage () {
this.$refs.websshNew.sendMessage(this.message)
}
},
beforeDestroy () {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -31,15 +31,23 @@
:popper-class="'popover-webshell'"
>
<div>
Session ID : {{item.uuid.slice(0,7).toLocaleUpperCase()}}
Session ID : {{item.terminal.uuid.slice(0,7).toLocaleUpperCase()}}
</div>
<span slot="reference"> <i class="nz-icon nz-icon-about" /></span>
</el-popover>
<div :class="{'active-icon grey':item.circleColor == 1,'active-icon green':item.circleColor == 2,'active-icon red':item.circleColor == 3}"
style="margin-top: 0px;"></div>{{item.title}}
</span>
<my-console :fontSize="fontSize" :idIndex="index" :isFullScreen="isFullScreen" :ref="'console'+index" :terminal="item.terminal" @closeConsole="removeTab" @refreshConsoleTitle="refreshTabTitle"></my-console>
<my-console
:fontSize="fontSize"
:terminalType="item.terminal.terminalType"
:idIndex="index"
:isFullScreen="isFullScreen"
:ref="'console'+index"
:terminal="item.terminal"
@loginFail="loginFail"
@closeConsole="removeTab"
@refreshConsoleTitle="refreshTabTitle"
></my-console>
</el-tab-pane>
<el-tab-pane key="add" name="addConsole" style="width: 20px">
@@ -59,13 +67,17 @@
</el-tab-pane>
</el-tabs>
<div class="console-icon">
<div class="console-title-icon" style='right: 106px;display: inline;' @click="showFileState" v-show="fileList.length">
<i class="nz-icon nz-icon-a-filetransfer" :title="$t('terminal.filetransfer')"></i>
<span v-show="fileList.length>0" class="right-tip">{{fileList.length<=99?fileList.length:'99+'}}</span>
</div>
<i @click="minScreen" class="nz-icon nz-icon-minus console-title-icon" style='right: 76px;' :title="$t('overall.shrink')"></i>
<i @click="fullScreen" class="el-icon-full-screen console-title-icon" style='right: 46px;' v-if="!isFullScreen" :title="$t('dashboard.screen')"></i>
<i @click="exitFullScreen" class="nz-icon nz-icon-exit-full-screen console-title-icon" style='right: 46px;' v-else :title="$t('dashboard.screen.exit')"></i>
<i @click="closeConsole" class="nz-icon nz-icon-close console-title-icon" style='right: 16px;' :title="$t('overall.close')"></i>
</div>
</div>
<fileListState v-clickoutside="hideFileState" ref="fileListState"/>
<div >
<el-dialog :modal-append-to-body='false' :show-close="true" :title="uploadBox.title" :visible.sync="uploadBox.showUpload" @close="closeDialog" class="nz-dialog" width="500px" >
<div >
@@ -255,21 +267,27 @@
<script>
import Console from './console'
import fileListState from './fileListState'
import uuidv1 from 'uuid/v1'
import axios from 'axios'
import { host, port } from '@/components/common/js/validate'
export default {
name: 'webSSH',
components: {
'my-console': Console
'my-console': Console,
fileListState
},
computed: {
language () { return this.$store.getters.getLanguage }
language () { return this.$store.getters.getLanguage },
fileList () {
return this.$store.getters.getFileList
}
},
data () {
const termFontSize = parseInt(localStorage.getItem('termFontSize'))
return {
selectValue: 'SSH',
fileListStateType: 'down',
searchMetrics: [
{
value: 'SSH',
@@ -307,9 +325,9 @@ export default {
isFullScreen: false,
closeConfirmShow: false,
closeRemember: false,
initConsoleHeight: 300, // 只读,初始化高度
consoleHeight: 250, // console高度
resizeConsoleHeight: 250, // resize后的高度用于记录最大化、最小化前的高度
initConsoleHeight: 500, // 只读,初始化高度
consoleHeight: 450, // console高度
resizeConsoleHeight: 450, // resize后的高度用于记录最大化、最小化前的高度
currentTransform: 0,
editableTabsValue: '-1', // 当前显示的console
currentIndex: '-1',
@@ -510,6 +528,7 @@ export default {
host: this.customConnect.host,
port: this.customConnect.port,
authType: this.customConnect.authType,
terminalType: this.customConnect.authProtocol,
authUsername: encodeURIComponent(this.customConnect.authUsername),
authPin: this.encode(this.customConnect.authPin),
authPriKey: encodeURIComponent(this.customConnect.authPriKey),
@@ -519,9 +538,16 @@ export default {
authProtocol: encodeURIComponent(this.customConnect.authProtocol)
}
}
this.editableTabsValue = newTabName
this.editableTabs.push(console)
if (id) {
this.$get('/asset/asset/' + id).then(res => {
console.terminal.terminalType = res.data.type.authProtocol
this.editableTabsValue = newTabName
this.editableTabs.push(console)
})
} else {
this.editableTabsValue = newTabName
this.editableTabs.push(console)
}
setTimeout(function () {
const tabScroll = document.getElementsByClassName('el-tabs__nav is-top')
const tabViewWidth = document.getElementsByClassName('el-tabs__nav-scroll')
@@ -620,9 +646,23 @@ export default {
this.$store.commit('removeConsole')
if (this.editableTabs.length <= 0) {
this.closeConsole()
// this.closeConsole()
}
},
loginFail (params) {
this.removeTab(params.name)
setTimeout(() => {
if (params.assetId) {
this.assetShowChange()
} else {
this.customConnect = {
authType: Number(params.authType),
...params
}
this.customShow = true
}
})
},
/* 活动标签切换时触发 */
beforeLeave (currentName, oldName) {
// 重点如果name是add则什么都不触发
@@ -969,6 +1009,25 @@ export default {
authProtocolPort: '',
authProtocol: 1
}
},
showFileState () {
let type = 'down'
const targetDiv = document.getElementById('shell-service')
let height = targetDiv.style.height
if (height) {
height = height.slice(0, -2)
if (height < 265) {
this.fileListStateType = type = 'up'
} else {
this.fileListStateType = type = 'down'
}
} else {
this.fileListStateType = type = 'down'
}
this.$refs.fileListState.fileStateShow(!this.$refs.fileListState.fileStateBox, type)
},
hideFileState () {
this.$refs.fileListState.fileStateShow(false, this.fileListStateType)
}
},
watch: {
@@ -996,6 +1055,10 @@ export default {
})
const targetDiv = document.getElementById('shell-service')
targetDiv.style.height = this.webSSHHeight
if (!this.webSSHHeight) {
this.initConsoleHeight = this.initConsoleHeight > (window.innerHeight * 0.4) ? this.initConsoleHeight : (window.innerHeight * 0.4)
targetDiv.style.height = this.initConsoleHeight + 'px'
}
}
this.$store.state.consoleShow = false
}
@@ -1004,6 +1067,7 @@ export default {
created () {
// window.addEventListener('resize',this.windowChange);
window.addEventListener('resize', this.debounce(this.windowChange, 1000), false)
this.initConsoleHeight = this.initConsoleHeight > (window.innerHeight * 0.4) ? this.initConsoleHeight : (window.innerHeight * 0.4)
},
mounted () {

View File

@@ -0,0 +1,702 @@
<template>
<div class="ani-webSHH-height web-terminal-new" id="web-terminal-new">
<el-tabs :before-leave="beforeLeave"
@tab-click="handleClick"
@tab-remove="removeTab"
type="border-card"
v-model="editableTabsValue" >
<el-tab-pane :key="item.name"
:label="item.title"
:name="item.name"
closable
v-for="(item, index) in editableTabs"
>
<!-- tab显示的内容 1 grey,2 green, 3 red-->
<span slot="label" class="el-tabs__item-label">
<div class="active-icon" :class="item.terminal.isLogin ? 'green-bg': 'red-bg'"></div>
<div class="el-tabs__item-label-name text-ellipsis">
{{item.terminal.userName}}
</div>
<el-popover
slot="label"
placement="bottom"
:ref="'popover' + index"
width="180"
trigger="click"
:popper-class="'popover-webshell popover-webshell-new'"
>
<div class="popover-webshell-box">
<div class="webshell-box-top">
Session ID : {{item.terminal.uuid.slice(0,7).toLocaleUpperCase()}}
</div>
<div class="webshell-box-middle" v-show="item.terminal.userName">
<div @click="duplicate(item, index)" class="webshell-box-item">
<i class="nz-icon nz-icon-override"/>
{{$t('overall.duplicate')}}
</div>
<div @click="reconnect(item, index)" v-show="item.terminal.userName" class="webshell-box-item">
<i class="nz-icon nz-icon-reconnect"></i>
{{$t('terminal.reconnect')}}
</div>
<div @click="showFileDir(item, index)" class="webshell-box-item" v-if="item.terminal.isLogin && item.terminal.terminalType == '1'">
<i class="nz-icon nz-icon-SFTP"></i>
{{$t('terminal.sftp')}}
</div>
</div>
<div class="webshell-box-item webshell-box-bottom" @click="closeShell(item,index)">
<div >
<i class="nz-icon nz-icon-close"/>
{{$t('overall.close')}}
</div>
</div>
</div>
<span slot="reference" class="icon-reference"> <i class="nz-icon nz-icon-more1" /></span>
</el-popover>
</span>
<terminal
:fontSize="fontSize"
:terminalType="item.terminal.terminalType"
:idIndex="index"
:ref="'console'+index"
:terminal="item.terminal"
@loginFail="loginFail"
@closeConsole="removeTab"
@refreshConsoleTitle="refreshTabTitle"
></terminal>
</el-tab-pane>
<el-tab-pane key="add" name="addConsole" class="add-console" style="width: 20px">
<el-popover
slot="label"
placement="bottom-start"
width="150"
trigger="hover"
:popper-class="'popover-webshell'"
>
<div>
<div class="popover-webshell-item" @click="assetShowChange"><i class="nz-icon nz-icon-menu-assets" />{{$t('webshell.selAsset')}}</div>
<div class="popover-webshell-item" @click="customShow=true"><i class="nz-icon nz-icon-edit" />{{$t('webshell.custom')}}</div>
</div>
<span slot="reference" style="padding:8px;font-size:20px;font-weight:bold;">+</span>
</el-popover>
</el-tab-pane>
</el-tabs>
<!--弹窗-->
<el-dialog :modal-append-to-body='false' :show-close="true" :visible.sync="assetShow" @close="closeAssetCustom" class="nz-dialog" width="620px">
<div slot="title">{{$t('webshell.connect')}}</div>
<div >
<el-form label-width="120px" size="small" :model="assetContent" label-position = "top" :rules="rules" ref="assetConnect" v-my-loading="assetLoading" >
<el-form-item :label='$t("overall.asset")' prop="assetId" class="flex">
<el-dropdown trigger="click" class="header-el-dropdown">
<span class="el-dropdown-link">
<span>{{selectValue}}</span>
<span><i class="el-icon-arrow-down el-icon--right"></i></span>
</span>
<el-dropdown-menu style="width: 118px" class="el-dropdown__width right-box-select-top right-public-box-dropdown-top" placement="bottom-end" slot="dropdown">
<el-dropdown-item
@click.native="selectAssetAuth(item.label, item.value)"
v-for="item in searchMetrics"
:key="item.value"><i class="nz-icon" :class="item.icon" style="margin-right: 5px"/>{{item.label}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<v-selectpage
ref="selectPage"
style="flex: 1"
data="asset/asset"
:params="selectPageParams"
:tb-columns="columns"
key-field="id"
show-field="manageIp"
search-field="manageIp"
v-model="assetContent.assetId"
size="small"
:language="language"
:placeholder="$t('dashboard.panel.chartForm.selectAsset')"
id="box-input-asset-id"
:result-format="resultFormat"></v-selectpage>
<button :disabled="prevent_opt.save" class="nz-btn nz-btn-size-normal nz-btn-style-normal" type="button" @click.prevent="connect">Connect</button>
</el-form-item>
</el-form>
</div>
</el-dialog>
<el-dialog :modal-append-to-body='false' :show-close="true" :visible.sync="customShow" @close="closeAssetCustom" class="nz-dialog" width="620px"destroy-on-close >
<div slot="title">{{$t('webshell.connect')}}</div>
<div >
<el-form label-width="120px" size="small" :model="customConnect" label-position = "top" :rules=" customConnect.authProtocol ===2 ? rulesCustom2: rulesCustom" ref="customConnect" v-my-loading="assetLoading" class="custom">
<el-form-item :label='$t("webshell.protocol")' prop="authProtocol">
<el-select @change="protocolChange" value-key="id" popper-class="config-dropdown w260 right-box-select-top right-public-box-dropdown-top" v-model="customConnect.authProtocol" placeholder="" size="small" id="webshell-box-input-protocol">
<el-option v-for="item in authProtocol" :id="'dc-principal-op-'+item.value" :key="item.value" :label="item.name" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item :label='$t("asset.authType")' prop="authType" @change="authTypeChange" >
<el-select value-key="id" popper-class="config-dropdown w260 right-box-select-top right-public-box-dropdown-top" v-model="customConnect.authType" :disabled="customConnect.authProtocol === 2" placeholder="" size="small" id="webshell-box-input-protocol">
<el-option v-for="item in authType" :id="'dc-principal-op-'+item.value" :key="item.value" :label="item.name" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item :label='$t("asset.host")' prop="host">
<el-input v-model="customConnect.host" size="small"/>
</el-form-item>
<el-form-item :label='$t("asset.port")' prop="port">
<el-input v-model="customConnect.port" size="small"/>
</el-form-item>
<el-form-item :label='$t("profile.username")' prop="authUsername">
<el-input v-model="customConnect.authUsername" size="small" autocomplete="new-password"/>
</el-form-item>
<el-form-item
v-if="customConnect.authType === 2"
:label='$t("asset.talon.token")'
prop="authPriKey"
>
<el-input v-model="customConnect.authPriKey" size="small" autocomplete="new-password"/>
</el-form-item>
<el-form-item :label='$t("login.pin")' prop="authPin"
:rules="[
{ required: customConnect.authType ===1, message:$t('validate.required'), trigger: 'change'},
]">
<el-input v-model="customConnect.authPin" size="small" type="password" autocomplete="new-password"/>
</el-form-item>
<el-form-item
v-if="customConnect.authProtocol === 2"
:label='$t("asset.usernamePrompt")'
prop="authUserTip">
<el-input v-model="customConnect.authUserTip" size="small"/>
</el-form-item>
<el-form-item
v-if="customConnect.authProtocol === 2"
:label='$t("asset.pinPrompt")'
prop="authPinTip"
>
<el-input v-model="customConnect.authPinTip" size="small"/>
</el-form-item>
<div class="right-box__footer custom-footer">
<button id="asset-edit-cancel" @click="customShow=false" class="footer__btn footer__btn--light" type="button">
<span>{{$t('overall.cancel')}}</span>
</button>
<button id="asset-edit-save" :disabled="prevent_opt.save" class="footer__btn" @click.prevent="connect" type="button">
<span>{{$t('webshell.connect')}}</span>
</button>
</div>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import terminal from './consoleNew'
import { host, port } from '@/components/common/js/validate'
export default {
name: 'webSSH',
components: {
terminal
},
computed: {
language () { return this.$store.getters.getLanguage },
fileList () {
return this.$store.getters.getFileList
}
},
data () {
return {
tabIndex: 0,
editableTabs: [],
editableTabsValue: '',
fontSize: 16,
assetShow: false,
selectValue: 'SSH',
fileListStateType: 'down',
searchMetrics: [
{
value: 'SSH',
label: 'SSH'
},
{
value: 'Telnet',
label: 'Telnet'
}
],
selectPageParams: {
authProtocol: 1
},
authProtocol: [
{
value: 1,
name: 'SSH'
},
{
value: 2,
name: 'TELNET'
}
],
authType: [
{
value: 1,
name: 'Password'
},
{
value: 2,
name: 'Key'
}
],
consoleShow: false,
closeConfirmShow: false,
customConnect: {
host: '',
port: 22,
authType: 1,
authUsername: '',
authPin: '',
authPriKey: '',
authUserTip: '',
authPinTip: '',
authProtocolPort: '',
authProtocol: 1
},
columns: [
{ title: 'ID', data: 'id' },
{
title: 'Name',
data: function (row) {
if (row.name.length > 15) {
return row.name.substring(0, 12) + '...'
}
return row.name
}
},
{ title: 'Manage Ip', data: 'manageIp' },
{
title: 'Type',
data: (row) => {
return row.type ? row.type.name : ''
}
},
{
title: 'Model',
data: (row) => {
return row.model ? row.model.name : ''
}
},
{
title: 'Datacenter',
data: (row) => {
return row.dc ? row.dc.name : ''
}
}
],
rules: {
assetId: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
rulesCustom: {
authProtocol: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
authType: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
host: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
{ validator: host, trigger: 'change' }
],
port: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
{ validator: port, trigger: 'change' }
],
authUsername: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
authPin: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
// authUserTip: [
// { validator: this.authUserTipValid, trigger: 'change' }
// ],
// authPinTip: [
// { validator: this.authPinTipValid, trigger: 'change' }
// ],
// authPriKey: [
// { validator: this.authPriKeyValid, trigger: 'change' }
// ]
},
rulesCustom2: {
authProtocol: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
authType: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
host: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
{ validator: host, trigger: 'change' }
],
port: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
{ validator: port, trigger: 'change' }
],
authUsername: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
authPin: [
{ required: false, message: this.$t('validate.required'), trigger: 'change' }
],
authUserTip: [
{ required: false, message: this.$t('validate.required'), trigger: 'change' }
],
authPinTip: [
{ required: false, message: this.$t('validate.required'), trigger: 'change' }
],
authPriKey: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
assetData: [],
assetLoading: false,
assetContent: {
assetId: ''
},
customShow: false
}
},
methods: {
selectAssetAuth (val, label) {
this.$refs.selectPage.remove()
if (val) {
this.selectValue = val
if (val === 'SSH') {
this.selectPageParams = { authProtocol: 1, pageNumber: 1 }
} else {
this.selectPageParams = { authProtocol: 2, pageNumber: 1 }
}
} else {
label = 'SSH'
this.selectPageParams = { authProtocol: 1, pageNumber: 1 }
}
setTimeout(() => {
this.$refs.selectPage.pageChange()
}, 100)
},
assetShowChange () {
this.assetShow = true
this.selectValue = 'SSH'
this.selectPageParams = { authProtocol: 1, pageNumber: 1 }
if (this.$refs.selectPage) {
this.$refs.selectPage.remove()
this.$refs.selectPage.pageChange()
}
// this.getAssetData()
},
/* 活动标签切换时触发 */
beforeLeave (currentName, oldName) {
// 重点如果name是add则什么都不触发
if (currentName === 'add') {
this.addTab()
return false
} else {
// 切换tab
this.$nextTick(() => {
if (this.editableTabs && this.editableTabs.length > 0) {
this.editableTabs.forEach((tab, index) => {
if (tab.name === currentName) {
this.$refs['console' + index][0].focusConsole()
this.currentUuid = tab.terminal.uuid
}
})
}
})
this.currentIndex = currentName
}
},
handleClick () {
},
windowChange () { // 窗口大小改变
// alert('winChange');
if (this.editableTabs && this.editableTabs.length > 0) {
this.editableTabs.forEach((tab, index) => {
this.$refs['console' + index][0].resize()
})
}
},
removeTab (targetName) {
const tabs = this.editableTabs
let activeName = this.editableTabsValue
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
this.editableTabsValue = activeName
this.editableTabs = tabs.filter(tab => tab.name !== targetName)
if (this.editableTabs.length <= 0) {
// this.closeConsole()
}
},
loginFail (params) {
this.removeTab(params.name)
setTimeout(() => {
if (params.assetId) {
this.assetShowChange()
} else {
this.customConnect = {
...params,
authType: Number(params.authType),
authPin: atob(decodeURIComponent(params.authPin))
}
this.customShow = true
}
})
},
refreshTabTitle (connectResult) {
if (this.editableTabs && this.editableTabs.length > 0) {
this.editableTabs.forEach((tab, index) => {
if (tab.name === this.editableTabsValue) {
if (connectResult.title && connectResult.title != '') {
tab.title = connectResult.title
}
tab.circleColor = connectResult.color
}
})
}
},
cancleConfirm () {
this.closeConfirmShow = false
},
resultFormat (resp) {
if (resp && resp.data) {
const assetData = {}
assetData.list = resp.data.list
assetData.totalRow = resp.data.total
return assetData
}
},
getAssetData () {
this.assetLoading = true
this.$get('asset/asset', { pageSize: -1, typeIds: '1,2' }).then(res => {
this.assetLoading = false
this.assetData = res.data.list
})
},
connect () {
this.prevent_opt.save = true
if (this.assetShow) {
this.$refs.assetConnect.validate((valid) => {
if (valid) {
this.$get('asset/asset/' + this.assetContent.assetId).then(res => {
const asset = res.data
this.addConsole(asset.id, asset.manageIp, '', '', 'asset')
this.assetShow = false
this.prevent_opt.save = false
})
} else {
this.prevent_opt.save = false
}
})
} else {
this.$refs.customConnect.validate((valid) => {
if (valid) {
this.addConsole('', this.customConnect.host, '', this.customConnect.port, 'custom')
this.customShow = false
this.prevent_opt.save = false
} else {
this.prevent_opt.save = false
}
})
}
},
protocolChange () {
if (this.customConnect.authProtocol === 1) {
this.customConnect.authUserTip = ''
this.customConnect.authPinTip = ''
this.customConnect.port = 22
} else {
this.customConnect.authPriKey = ''
this.customConnect.port = 23
this.customConnect.authType = 1
}
setTimeout(() => {
this.$refs.customConnect.clearValidate()
})
},
authTypeChange () {
if (this.customConnect.authType === 1) {
this.customConnect.authPriKey = ''
}
},
encode (str) {
// 对编码的字符串转化base64
const base64 = encodeURIComponent(btoa(str))
return base64
},
closeAssetCustom () {
this.assetShow = false
this.customShow = false
this.assetContent.assetId = ''
this.customConnect = {
host: '',
port: 22,
authType: 1,
authUsername: '',
authPin: '',
authPriKey: '',
authUserTip: '',
authPinTip: '',
authProtocolPort: '',
authProtocol: 1
}
},
addConsole (id, host, accountId, port, type) {
if (!id) { id = '' }
if (!host) { host = '' }
if (!accountId) { accountId = '' }
if (!port) { port = '' }
const uuid = ''
const newTabName = ++this.tabIndex + ''
let title = host
if (port) {
title = title + ':' + port
}
if (!title) {
title = this.$t('webshell.shellTitle')
}
const width = document.body.clientWidth// 可视宽度
const console = {
title: title,
name: newTabName,
circleColor: 1, // 1 grey,2 green, 3 red
uuid: uuid,
showTooltip: false,
terminal: {
name: newTabName,
cols: 225,
rows: 200,
width: width,
height: this.consoleHeight,
assetId: id,
accountId: accountId,
uuid: uuid,
type: type,
username: '',
isLogin: false
}
}
if (type === 'custom') {
console.terminal.custom = {
host: this.customConnect.host,
port: this.customConnect.port,
authType: this.customConnect.authType,
terminalType: this.customConnect.authProtocol,
authUsername: encodeURIComponent(this.customConnect.authUsername),
authPin: this.encode(this.customConnect.authPin),
authPriKey: encodeURIComponent(this.customConnect.authPriKey),
authUserTip: encodeURIComponent(this.customConnect.authUserTip),
authPinTip: encodeURIComponent(this.customConnect.authPinTip),
authProtocolPort: encodeURIComponent(this.customConnect.authProtocolPort),
authProtocol: encodeURIComponent(this.customConnect.authProtocol)
}
}
if (id) {
this.$get('/asset/asset/' + id).then(res => {
console.terminal.terminalType = res.data.type.authProtocol
console.terminal.username = ''
this.editableTabsValue = newTabName
this.editableTabs.push(console)
})
} else {
console.terminal.username = ''
console.terminal.terminalType = this.customConnect.authProtocol
this.editableTabsValue = newTabName
this.editableTabs.push(console)
}
},
duplicate (item, index) {
if (item.terminal.assetId) {
const newTabName = ++this.tabIndex + ''
this.editableTabsValue = newTabName
const console = JSON.parse(JSON.stringify(item))
console.name = newTabName
console.terminal.name = newTabName
console.terminal.isLogin = false
this.editableTabs.push(console)
} else {
this.customConnect = {
...item.terminal.custom,
authType: Number(item.terminal.custom.authType),
authPin: atob(decodeURIComponent(item.terminal.custom.authPin))
}
this.customShow = true
}
if (this.$refs['popover' + index] && this.$refs['popover' + index][0]) {
this.$refs['popover' + index][0].doClose()
}
},
debounce (operate, delay) {
let time = null
let timer = null
let newTime = null
function task () {
newTime = +new Date()
if (newTime - time < delay) {
timer = setTimeout(task, delay)
} else {
operate()
timer = null
}
time = newTime
}
return function () {
// 更新时间戳
time = +new Date()
if (!timer) {
timer = setTimeout(task, delay)
}
}
},
reconnect (item, index) {
this.$refs['console' + index][0].reconnect()
if (this.$refs['popover' + index] && this.$refs['popover' + index][0]) {
this.$refs['popover' + index][0].doClose()
}
},
showFileDir (item, index) {
this.$refs['console' + index][0].showFileDir(true)
if (this.$refs['popover' + index] && this.$refs['popover' + index][0]) {
this.$refs['popover' + index][0].doClose()
}
},
closeShell (item, index) {
this.removeTab(item.name)
if (this.$refs['popover' + index] && this.$refs['popover' + index][0]) {
this.$refs['popover' + index][0].doClose()
}
},
sendMessage (message) {
this.editableTabs.forEach((item, index) => {
this.$refs['console' + index][0].enterStr(message)
})
}
},
watch: {
},
created () {
},
mounted () {
window.addEventListener('resize', this.debounce(this.windowChange, 1000), false)
},
beforeDestroy () {
window.removeEventListener('resize', this.debounce(this.windowChange, 1000), false)
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -27,8 +27,9 @@
<endpoint-tab v-if="from === fromRoute.asset && targetTab === fromRoute.endpoint" v-show="subResizeShow" :from="from" :obj="obj" @changeTab="changeTab" :targetTab.sync="targetTab"></endpoint-tab>
<!--terminal-log的记录和回放-->
<terminal-log-cmd-tab v-if="from === fromRoute.terminalLog && targetTab === 'cmdTab'" :from="from" :obj="obj" :tabs="tabs.terminalLog.cmd" @changeTab="changeTab" :targetTab.sync="targetTab"></terminal-log-cmd-tab>
<terminal-log-record-tab v-if="from === fromRoute.terminalLog && targetTab === 'recordTab'" ref="reminalLogRecordTab" :from="from" :obj="obj" :tabs="tabs.terminalLog.record" @changeTab="changeTab" :targetTab.sync="targetTab"></terminal-log-record-tab>
<terminal-log-cmd-tab v-if="from === fromRoute.terminalLog && targetTab === 'cmdTab'" :from="from" :obj="obj" :tabs="hasTerminalLogTabs" @changeTab="changeTab" :targetTab.sync="targetTab"></terminal-log-cmd-tab>
<terminal-log-sftp-tab v-if="from === fromRoute.terminalLog && targetTab === 'sftpTab' && obj.protocol == 'SSH'" :from="from" :obj="obj" :tabs="hasTerminalLogTabs" @changeTab="changeTab" :targetTab.sync="targetTab"></terminal-log-sftp-tab>
<terminal-log-record-tab v-if="from === fromRoute.terminalLog && targetTab === 'recordTab'" ref="reminalLogRecordTab" :from="from" :obj="obj" :tabs="hasTerminalLogTabs" @changeTab="changeTab" :targetTab.sync="targetTab"></terminal-log-record-tab>
<terminal-log-monitor-tab v-if="from === fromRoute.terminalLog && targetTab === 'monitorTab'" :from="from" :obj="obj" :tabs="tabs.terminalLog.monitor" @changeTab="changeTab" @exit="closeSubList" :targetTab.sync="targetTab"></terminal-log-monitor-tab>
<asset-tab v-if="from === fromRoute.dc && targetTab === 'asset'" :tabs="tabs.dc.asset" ref="assetTab" :from="from" :obj="obj" @changeTab="changeTab" @exit="closeSubList" :targetTab.sync="targetTab"></asset-tab>
@@ -56,7 +57,9 @@
<log-bottom-tab v-if="from === fromRoute.endpoint && targetTab === 'log' && hasLogConfig" :sign="sign+'log'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="endpointTabs" :targetTab.sync="targetTab" @changeTab="changeTab"></log-bottom-tab>
<alertMessageTabNew v-if="from === fromRoute.endpoint && targetTab === 'endpointAlertMessage'" :sign="sign+'alert'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="endpointTabs" :targetTab.sync="targetTab" @changeTab="changeTab"></alertMessageTabNew>
<!--chartTemp的Tab-->
<panel-tab-new @exit="closeSubList" @getTableData="getTableData" :paramsType="'template'" v-if="from === fromRoute.chartTemp && targetTab === 'panel'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.chartTemp.chartTempTabTitle" :targetTab.sync="targetTab" @changeTab="changeTab"></panel-tab-new>
<panel-tab-new @exit="closeSubList" @getTableData="getTableData" :paramsType="'template'" v-if="from === fromRoute.chartTemp && targetTab === 'panel'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.chartTemp.chartTempTabTitle" :targetTab.sync="targetTab" @changeTab="changeTab"></panel-tab-new>
<!--dashboardTemp的Tab-->
<panel-tab-new @exit="closeSubList" @getTableData="getTableData" :paramsType="'template'" v-if="from === fromRoute.dashboardTemp && targetTab === 'panel'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.dashboardTemp.dashboardTempTabTitle" :targetTab.sync="targetTab" @changeTab="changeTab"></panel-tab-new>
<!--alertRule Tab-->
<alertMessageTabNew v-if="from === fromRoute.alertRule && targetTab === 'alertRuleAlertMessage'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.alertRule.alertRule" @changeTab="changeTab" :targetTab.sync="targetTab"></alertMessageTabNew>
<alertRuleEvalLog v-if="from === fromRoute.alertRule && targetTab === 'evalLog'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.alertRule.alertRule" @changeTab="changeTab" :targetTab.sync="targetTab"></alertRuleEvalLog>
@@ -67,11 +70,12 @@
<!-- ipam -->
<ip-details v-if="from === fromRoute.ipam && targetTab === 'ipam'" :from="from" :obj="obj" :tabs="tabs.ipam" @changeTab="changeTab" :targetTab.sync="targetTab"></ip-details>
<!-- recordRule 下滑-->
<record-rule-eval-log v-if="from === fromRoute.recordRule && targetTab === 'recordRule'" :from="from" :obj="obj" :tabs="tabs.recordRule" @changeTab="changeTab" :targetTab.sync="targetTab"></record-rule-eval-log>
<record-rule-eval-log v-if="from === fromRoute.recordRule && targetTab === 'recordRule'" :from="from" :obj="obj" :tabs="tabs.recordRule.evalLog" @changeTab="changeTab" :targetTab.sync="targetTab"></record-rule-eval-log>
<recordRulesQuery v-if="from === fromRoute.recordRule && targetTab === 'Metrics'" :from="from" :obj="obj" :tabs="tabs.recordRule.Metrics" @changeTab="changeTab" :targetTab.sync="targetTab"></recordRulesQuery>
<!--alertRule Tab-->
<alertMessageTabNew v-if="from === fromRoute.alertSilence && targetTab === 'alertMessageTab'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.alertSilence" @changeTab="changeTab" :targetTab.sync="targetTab"></alertMessageTabNew>
<!--issue Tab-->
<!-- <issueTab v-if="from === fromRoute.issue && targetTab === 'issue'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.alertSilence" @changeTab="changeTab" :targetTab.sync="targetTab"></issueTab>-->
<!-- <issueTab v-if="from === fromRoute.issue && targetTab === 'issue'" v-show="subResizeShow" :from="from" :obj="obj" :tabs="tabs.alertSilence" @changeTab="changeTab" :targetTab.sync="targetTab"></issueTab> -->
</div>
</div>
</div>
@@ -87,10 +91,12 @@ import assetSubTab from './tabs/assetSubTab'
import commentsBottomTab from './tabs/commentsBottomTab'
import vsysBottomTab from './tabs/vsysBottomTab'
import endpointQuery from './tabs/endpointQuery'
import recordRulesQuery from './tabs/recordRulesQuery'
import endpointTab from './tabs/endpointTab'
import endpointTabNew from './tabs/endpointTabNew'
import panelTabNew from './tabs/panelTabNew'
import terminalLogRecordTab from './tabs/terminalLogRecordTab'
import terminalLogSftpTab from './tabs/terminalLogSftpTab'
import terminalLogMonitorTab from './tabs/terminalLogMonitorTab'
import terminalLogCMDTab from './tabs/terminalLogCMDTab'
import operationLogTab from './tabs/operationLogTab'
@@ -117,6 +123,7 @@ export default {
alertMessageTab,
endpointTab,
terminalLogRecordTab,
terminalLogSftpTab,
terminalLogMonitorTab,
operationLogTab,
terminalLogTab,
@@ -130,6 +137,7 @@ export default {
alertRuleEvalLog,
IpDetails,
recordRuleEvalLog,
recordRulesQuery,
issueTab,
commentsBottomTab,
vsysBottomTab
@@ -157,15 +165,15 @@ export default {
terminalLog: {
monitor: [
{ prop: 'monitorTab', name: this.$t('config.terminallog.monitor.monitor'), active: true }
],
cmd: [
{ prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: true },
{ prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: false }
],
record: [
{ prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: false },
{ prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: true }
]
// // cmd: [
// // { prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: true },
// // { prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: false }
// // ],
// // record: [
// // { prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: false },
// // { prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: true }
// // ]
},
user: {
operationLog: [
@@ -206,6 +214,11 @@ export default {
{ prop: 'panel', name: this.$t('overall.tempPrev') }
]
},
dashboardTemp: {
dashboardTempTabTitle: [
{ prop: 'panel', name: this.$t('overall.dashboardTemp') }
]
},
alertRule: {
alertRule: [
{ prop: 'alertRuleAlertMessage', name: this.$t('overall.alert') },
@@ -238,9 +251,16 @@ export default {
ipam: [
{ prop: 'ipam', name: this.$t('ipam.subnet.ipDetails'), active: true }
],
recordRule: [
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: true }
],
recordRule: {
Metrics: [
{ prop: 'Metrics', name: this.$t('overall.metric'), active: true },
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: false }
],
evalLog: [
{ prop: 'Metrics', name: this.$t('overall.metric'), active: false },
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: true }
]
},
alertSilence: [
{ prop: 'alertMessageTab', name: this.$t('overall.alert'), active: true }
],
@@ -251,6 +271,17 @@ export default {
}
},
computed: {
hasTerminalLogTabs () {
const hasSftp = this.obj && this.obj.protocol == 'SSH'
const tabs = [
{ prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd') },
{ prop: 'recordTab', name: this.$t('config.terminallog.record.record') }
]
if (hasSftp) {
tabs.splice(1, 0, { prop: 'sftpTab', name: 'SFTP ' })
}
return tabs
},
hasLogConfig () {
const config = this.obj.configs.find(c => c.type === 'logs')
return config && config.enable === 1
@@ -258,7 +289,7 @@ export default {
assetTabs () {
const hasSub = this.obj && this.obj.childrenNum
const hasProcess = this.obj && this.obj.clientState == '1'
const hasVays = this.obj && this.obj.model.tsgAppliance == '1'
const hasVays = this.obj && this.obj.model && this.obj.model.tsgAppliance == '1'
const tabs = [
{ prop: 'panelTab', name: this.$t('overall.dashboard') },
{ prop: 'alertMessageTab', name: this.$t('overall.alert') },

View File

@@ -23,7 +23,7 @@
</el-input>
</div>
<div class="margin-r-20 nz-btn-group">
<button @click="changeTime(-10)" class="nz-btn nz-btn-size-normal nz-btn-style-light change-time-height nz-input-group-prepend" id="endpoint-query-changetime" :title="$t('overall.decreaseTime')"><i class="el-icon-d-arrow-left"></i></button>
<button @click="changeTime(-10)" class="nz-btn nz-btn-size-normal nz-btn-style-light change-time-height nz-input-group-prepend" style="height:32px" id="endpoint-query-changetime" :title="$t('overall.decreaseTime')"><i class="el-icon-d-arrow-left"></i></button>
<my-date-picker
v-model="formatTime"
type="datetime"
@@ -36,7 +36,7 @@
:format="timeFormatStrToDatePickFormat(timeFormatMain)"
>
</my-date-picker>
<button @click="changeTime(10)" class="nz-btn nz-btn-size-normal nz-btn-style-light change-time-height nz-input-group-append" :title="$t('overall.increaseTime')"><i class="el-icon-d-arrow-right"></i></button>
<button @click="changeTime(10)" class="nz-btn nz-btn-size-normal nz-btn-style-light change-time-height nz-input-group-append" style="height:32px" :title="$t('overall.increaseTime')"><i class="el-icon-d-arrow-right"></i></button>
</div>
<div class="nz-btn-group nz-btn-group-size-normal nz-btn-group-light" style="height: 28px;">
<button class="nz-btn nz-btn-size-normal nz-btn-style-light" :class="{'control-icon-unchecked':selectedEndpoints.length<1,'control-icon-checked':selectedEndpoints.length>0}" @click="viewGraph" :title="$t('overall.showLineChart')">
@@ -81,7 +81,7 @@ import endpointQueryTab from '@/components/common/table/special/endpointQueryTab
import bus from '@/libs/bus'
import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin'
export default {
name: 'endpointTab',
name: 'endpointQuery',
mixins: [subDataListMixin, detailViewRightMixin],
props: {
from: {}

View File

@@ -13,13 +13,13 @@
>
<template v-slot:title><span :title="obj.name">{{obj.name}}</span></template>
<template v-slot:top-tool-right>
<!-- asset -->
<div v-if="from === fromRoute.asset" style="display: flex">
<pick-time ref="pickTime" v-model="searchTime" :refresh-data-func="dateChange" :use-chart-unit="false" :sign="'panel' + obj.id"></pick-time>
<button id="panel-add-chart" v-has="'main_add'" :title="$t('overall.createChart')" class="top-tool-btn margin-r-10"
type="button" @click="addChart">
<button id="panel-add-chart" v-has="'main_add'" :title="$t('overall.createChart')" class="top-tool-btn margin-r-10" type="button" @click="addChart">
<i class="nz-icon-create-square nz-icon"></i>
</button>
<top-tool-more-options
<top-tool-more-options
ref="topTool"
:delete-objs="batchDeleteObjs"
id="asset-list"
@@ -38,24 +38,25 @@
@panelLockChange="panelLockChange"
>
<template v-slot:after>
<el-dropdown-item v-has="'main_add'">
<!-- <el-dropdown-item v-has="'main_add'">
<div id="chart-temp-add" @click="addChartByTemp"><i class="nz-icon nz-icon-add"></i>{{ $t('overall.AddByTemplate') }}</div>
</el-dropdown-item>
<el-dropdown-item v-has="'main_edit'">
<div id="chart-temp-sync" @click="chartBySync"><i class="nz-icon nz-icon-sync"></i>{{ $t('overall.syncChart') }}</div>
</el-dropdown-item>
</el-dropdown-item> -->
<el-dropdown-item v-has="'panel_view'">
<div id="chart-export-html" @click="exportType"><i class="nz-icon nz-icon-kuaizhao"></i>{{ $t('overall.snapshoot') }}</div>
</el-dropdown-item>
</template>
</top-tool-more-options>
</div>
<!-- endpoint -->
<div v-else-if="from === fromRoute.endpoint" style="display: flex">
<pick-time ref="pickTime" v-model="searchTime" :refresh-data-func="dateChange" :use-chart-unit="false" :sign="'panel' + obj.id"></pick-time>
<button id="endpoint-create-chart" v-has="'main_add'" :title="$t('overall.createChart')" class="top-tool-btn margin-r-10" @click.stop="addChart">
<i class="nz-icon nz-icon-create-square"></i>
</button>
<top-tool-more-options
<top-tool-more-options
ref="topTool"
:delete-objs="batchDeleteObjs"
id="asset-list"
@@ -74,24 +75,36 @@
@panelLockChange="panelLockChange"
>
<template v-slot:after>
<el-dropdown-item v-has="'main_add'">
<!-- <el-dropdown-item v-has="'main_add'">
<div id="chart-temp-add" @click="addChartByTemp"><i class="nz-icon nz-icon-add"></i>{{ $t('overall.AddByTemplate') }}</div>
</el-dropdown-item>
<el-dropdown-item v-has="'main_edit'">
<div id="chart-temp-sync" @click="chartBySync"><i class="nz-icon nz-icon-sync"></i>{{ $t('overall.syncChart') }}</div>
</el-dropdown-item>
</el-dropdown-item> -->
<el-dropdown-item v-has="'panel_view'">
<div id="chart-export-html" @click="exportType"><i class="nz-icon nz-icon-kuaizhao"></i>{{ $t('overall.snapshoot') }}</div>
</el-dropdown-item>
</template>
</top-tool-more-options>
</div>
<div v-else-if="from === fromRoute.chartTemp">
<!-- chartTemp -->
<div v-else-if="from === fromRoute.chartTemp">
<button id="panel-lock" :title='panelLock ? $t("overall.unlocked") : $t("overall.locked")' class="top-tool-btn margin-r-10" @click="panelLockChange(!panelLock)" type="button">
<i :class="{'nz-icon nz-icon-lock':!panelLock,'nz-icon nz-icon-unlock':panelLock}"></i>
</button>
<button @click="chartBySync" id="chart-sync" :title="$t('overall.syncChart')" class="top-tool-btn margin-r-10"
type="button">
<i :class="{'nz-icon nz-icon-lock':!panelLock,'nz-icon nz-icon-unlock':panelLock}"></i>
</button>
<button @click="chartBySync" id="chart-sync" :title="$t('overall.syncChart')" class="top-tool-btn margin-r-10" type="button">
<i class="nz-icon nz-icon-sync"></i>
</button>
</div>
<!-- dashboardTemp -->
<div v-else-if="from === fromRoute.dashboardTemp">
<button id="endpoint-create-chart" v-has="'main_add'" :title="$t('overall.createChart')" class="top-tool-btn margin-r-10" @click.stop="addChart">
<i class="nz-icon nz-icon-create-square"></i>
</button>
<button id="panel-lock" :title='panelLock ? $t("overall.unlocked") : $t("overall.locked")' class="top-tool-btn margin-r-10" @click="panelLockChange(!panelLock)" type="button">
<i :class="{'nz-icon nz-icon-lock':!panelLock,'nz-icon nz-icon-unlock':panelLock}"></i>
</button>
<button @click="chartBySync" id="chart-sync" :title="$t('overall.syncChart')" class="top-tool-btn margin-r-10" type="button">
<i class="nz-icon nz-icon-sync"></i>
</button>
</div>
@@ -123,8 +136,9 @@
</div>
</div>
</nz-bottom-data-list>
<transition name="right-box">
<!-- <chart-box v-if="rightBox.chart.show" ref="addChartModal" :chart="chart" :from="from" :panel-data="panelData" :show-panel="showPanel" @close="closeRightBox" @delete-chart="delChart" @on-create-success="createSuccess" @on-delete-success="delChartOk"></chart-box>-->
<!-- <chart-box v-if="rightBox.chart.show" ref="addChartModal" :chart="chart" :from="from" :panel-data="panelData" :show-panel="showPanel" @close="closeRightBox" @delete-chart="delChart" @on-create-success="createSuccess" @on-delete-success="delChartOk"></chart-box> -->
<chart-right-box
v-if="chartRightBoxShow"
v-my-loading="rightBox.loading"
@@ -149,6 +163,8 @@
<script>
// import chartBox from '../../../page/dashboard/chartBox'
import * as echarts from 'echarts'
import { chartCache } from '@/components/common/js/common'
import chartRightBox from '@/components/common/rightBox/chart/chartRightBox'
import chartList from '@/components/chart/chartList'
import bus from '../../../../libs/bus'
@@ -574,12 +590,31 @@ export default {
this.showPanel.id = this.filter.panelId = 0
this.getData(this.filter)
}
} else if (this.from === this.fromRoute.dashboardTemp) {
this.$get('visual/panel', { type: 'template', ids: this.obj.id }).then(response => {
if (response.code === 200) {
this.panelData = response.data.list
if (this.panelData.length > 0) {
this.filter.panelId = this.panelData[0].id
this.showPanel = this.$loadsh.cloneDeep(this.panelData[0])
this.variables = this.$loadsh.get(this.panelData, '[0].param.variables')
this.getData(this.filter)
}
}
}).catch((error) => {
if (error) {
console.error(error)
this.$message.error(error.toString())
}
})
} else {
this.$get('visual/panel', { type: this.from, link: linkId || this.obj.id }).then(response => {
if (response.code === 200) {
this.panelData = response.data.list
if (this.panelData.length > 0) {
this.showPanel.id = this.filter.panelId = this.panelData[0].id
// this.showPanel.id = this.filter.panelId = this.panelData[0].id
this.filter.panelId = this.panelData[0].id
this.showPanel = this.$loadsh.cloneDeep(this.panelData[0])
this.variables = this.$loadsh.get(this.panelData, '[0].param.variables')
this.getData(this.filter)
}
@@ -855,6 +890,31 @@ export default {
this.disposeChart()
}
}
},
// 监听图表联动配置panelId
'showPanel.param.chartShare': {
handler (value) {
// 每次切换联动模式 tooltip设置显示
const option = {
tooltip: {
className: 'chart-time-series'
}
}
for (const key in chartCache) {
if (!chartCache[key] || chartCache[key].group !== 'timeSeriesGroup') {
continue
}
chartCache[key].setOption(option)
}
this.$store.commit('setCurrentMousemove', 0)
if (value && value !== 'none') {
this.$store.commit('setConnect', value)
echarts.connect('timeSeriesGroup')
} else {
this.$store.commit('setConnect', value)
echarts.disconnect('timeSeriesGroup')
}
}
}
},
beforeDestroy () {
@@ -862,6 +922,11 @@ export default {
document.querySelector('#tableList') && document.querySelector('#tableList').removeEventListener('mouseleave', this.tableListLeave)
this.scrollbarWrap && this.scrollbarWrap.removeEventListener('scroll', bus.debounce)
this.$store.dispatch('dispatchPanelLock', { flag: true })
// 页面销毁 清除图表联动
this.$store.commit('setCurrentMousemove', 0)
this.$store.commit('setConnect', 'none')
echarts.disconnect('timeSeriesGroup')
}
}
</script>

View File

@@ -3,16 +3,16 @@
:showTitle='showTitle'
:obj='obj'
:tableId="tableId"
:api="url"
:custom-table-title.sync="tools.customTableTitle"
:layout="['elementSet']"
:search-msg="searchMsg"
:tabs="tabs"
:targetTab="targetTab"
:showPagination="false"
@search="search"
@changeTab="changeTab"
>
id="recordRuleEvalLog"
:api="url"
:custom-table-title.sync="tools.customTableTitle"
:layout="['elementSet']"
:search-msg="searchMsg"
:tabs="tabs"
:targetTab="targetTab"
:showPagination="false"
@search="search"
@changeTab="changeTab">
<template v-slot:title><span :title="obj.name">{{obj.name}}</span></template>
<template v-slot>
<recordRuleEvalLogTable

View File

@@ -0,0 +1,206 @@
<template>
<div class="full-width-height recordRules-query">
<nz-bottom-data-list
:showTitle='showTitle'
:obj='obj'
:layout="[]"
:tabs="tabs"
:targetTab="targetTab"
:showPagination="false"
@changeTab="changeTab"
:customTool="true"
class="full-width-height"
>
<template v-slot:title><span :title="obj.name">{{obj.name}}</span></template>
<template v-slot:top-tool-right>
<div class="top-tool-right">
<div class="top-tool-search margin-r-20">
<el-input ref="elementQuery" @clear="clearInput" id="elementQuery" @focus="focusInput" @blur="blurInput" v-model="queryExpression" class="query-input-inactive" size="mini" clearable :placeholder="$t('project.endpoint.promExpr')" >
<i slot="suffix" class="el-input__icon nz-icon nz-icon-search" style="float:right" @click="focusInput"></i>
</el-input>
</div>
<div class="margin-r-20 nz-btn-group">
<button @click="changeTime(-10)" class="nz-btn nz-btn-size-normal nz-btn-style-light change-time-height nz-input-group-prepend" style="height:32px" id="recordRules-query-changetime" :title="$t('overall.decreaseTime')"><i class="el-icon-d-arrow-left"></i></button>
<my-date-picker
v-model="formatTime"
type="datetime"
size="mini"
class="project-calendar nz-input-group-middle"
clearable
:time-arrow-control="true"
placeholder="Moment"
:value-format="timeFormatStrToDatePickFormat(timeFormatMain)"
:format="timeFormatStrToDatePickFormat(timeFormatMain)"
>
</my-date-picker>
<button @click="changeTime(10)" class="nz-btn nz-btn-size-normal nz-btn-style-light change-time-height nz-input-group-append" style="height:32px" :title="$t('overall.increaseTime')"><i class="el-icon-d-arrow-right"></i></button>
</div>
<div class="nz-btn-group nz-btn-group-size-normal nz-btn-group-light" style="height: 28px;">
<button class="nz-btn nz-btn-size-normal nz-btn-style-light" :class="{'control-icon-unchecked':selectedrecordRules.length<1,'control-icon-checked':selectedrecordRules.length>0}" @click="viewGraph" :title="$t('overall.showLineChart')">
<i class="nz-icon nz-icon-chart" :class="{'control-icon-unchecked':selectedrecordRules.length<1,'control-icon-checked':selectedrecordRules.length>0}"></i>
</button>
<button @click="dropdownHandler(dropdownShow)" class="nz-btn nz-btn-size-normal nz-btn-style-light export-dropdown-btn" id="browser-go">
<i class="nz-icon nz-icon-arrow-down"></i>
<transition name="el-zoom-in-top">
<div v-if="dropdownShow" class="recordRules-query-dropdown el-popover" style="right: 11px;top: 33px;">
<span style="padding-top: 2px">{{$t('project.endpoint.hideSameLabels')}}</span>
<el-switch v-model="hideSameLabels" size="small"></el-switch>
</div>
</transition>
</button>
</div>
</div>
</template>
<template v-slot>
<recordRulesQueryTab
ref="recordRulesQueryTab"
:orderByFa="'id'"
:from="from"
:obj="obj"
:formatTime="formatTime"
:hideSameLabels="hideSameLabels"
:queryExpression="queryExpression"
@changSelection="changSelectionF"
@selectedrecordRulesChange="selectedrecordRulesChange"
/>
</template>
</nz-bottom-data-list>
</div>
</template>
<script>
import subDataListMixin from '@/components/common/mixin/subDataList'
import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList'
import recordRulesQueryTab from '@/components/common/table/special/recordRulesQueryTab'
import bus from '@/libs/bus'
import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin'
export default {
name: 'recordRulesQuery',
mixins: [subDataListMixin, detailViewRightMixin],
props: {
from: {}
},
components: {
nzBottomDataList,
recordRulesQueryTab
},
watch: {
obj: {
immediate: true,
handler (n) {
this.searchLabel = {}
this.formatTime = bus.timeFormate(new Date(bus.computeTimezoneTime(new Date())))
}
}
},
data () {
return {
nowTime: '',
rightBox: {
editShow: false,
show: false
},
fromBottom: true,
selectedrecordRules: [],
queryExpression: '',
dropdownShow: false,
timeout: null,
formatTime: '',
hideSameLabels: true
}
},
methods: {
focusInput () {
let classVal = document.getElementById('elementQuery').parentElement.getAttribute('class')
classVal = classVal.replace('query-input-inactive', 'query-input-active')
document.getElementById('elementQuery').parentElement.setAttribute('class', classVal)
this.$refs.elementQuery.focus()
},
blurInput () {
if (!this.queryExpression || this.queryExpression == '') {
setTimeout(function () {
let classVal = document.getElementById('elementQuery').parentElement.getAttribute('class')
classVal = classVal.replace('query-input-active', 'query-input-inactive')
document.getElementById('elementQuery').parentElement.setAttribute('class', classVal)
}, 100)
}
},
clearInput () {
this.$refs.elementQuery.focus()
},
changeTime (size, unit) {
this.formatTime = this.getTime(size, unit)
},
getTime (size, unit) { // 计算时间
const now = !this.formatTime ? new Date(bus.computeTimezone(new Date().getTime())) : new Date(bus.formateTimeToTime(this.formatTime))
if (unit) {
switch (unit) {
case 'y':
now.setFullYear(now.getFullYear() + size)
break
case 'M':
now.setMonth(now.getMonth() + size)
break
case 'd':
now.setDate(now.getDate() + size)
break
case 'h':
now.setHours(now.getHours() + size)
break
case 'm':
now.setMinutes(now.getMinutes() + size)
break
case 's':
now.setSeconds(now.getSeconds() + size)
break
default:
console.error('unit error')
}
} else {
now.setSeconds(now.getSeconds() + size)
}
const year = now.getFullYear()
let month = now.getMonth() + 1
month = month < 10 ? '0' + month : month
let day = now.getDate()
day = day < 10 ? '0' + day : day
let hour = now.getHours()
hour = hour < 10 ? '0' + hour : hour
let minute = now.getMinutes()
minute = minute < 10 ? '0' + minute : minute
let second = now.getSeconds()
second = second < 10 ? '0' + second : second
const str = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second
return bus.timeFormate(new Date(str))
},
viewGraph () {
this.$refs.recordRulesQueryTab.viewGraph()
},
dropdownHandler (show) {
if (!show) {
this.dropdownShow = true
} else {
this.dropdownShow = false
}
},
changSelectionF (arr) {
this.changSelection = arr
},
selectedrecordRulesChange (n) {
this.selectedrecordRules = n
},
toTop (wrap) {
let currentTop = wrap.scrollTop
const interval = currentTop / 10
const intervalFunc = setInterval(function () { // 花200ms分10次回到顶部模拟动画效果
if (currentTop === 0) {
clearInterval(intervalFunc)
} else {
currentTop = (currentTop - interval) < interval * 0.5 ? 0 : currentTop - interval
wrap.scrollTop = currentTop
}
}, 20)
}
}
}
</script>

View File

@@ -1,5 +1,6 @@
<template>
<nz-bottom-data-list
:showPagination="false"
:showTitle='showTitle'
:obj='obj'
:layout="[]"
@@ -35,14 +36,14 @@
</template>
<script>
import dataListMixin from '@/components/common/mixin/dataList'
// import dataListMixin from '@/components/common/mixin/dataList'
import subDataListMixin from '@/components/common/mixin/subDataList'
import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList'
import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin'
export default {
name: 'terminalLogRecordTab',
mixins: [dataListMixin, subDataListMixin, detailViewRightMixin],
mixins: [subDataListMixin, detailViewRightMixin],
components: {
nzBottomDataList
},
@@ -50,7 +51,7 @@ export default {
calcTime () {
return function (time) {
if (this.obj.startTime) {
const startTime = new Date(this.utcTimeToTimezoneStr(this.obj.startTime)).getTime()
const startTime = this.momentStrToTimestamp(this.utcTimeToTimezoneStr(this.obj.startTime))
if (startTime) {
const thisTime = startTime + time
return this.momentTz(thisTime)
@@ -92,7 +93,8 @@ export default {
records: [ // 加载更多时有多个record否则只有一个
],
tableId: 'terminaLogCMDTab'
tableId: 'terminaLogCMDTab',
detailType: 'list'
}
},
methods: {

View File

@@ -7,6 +7,7 @@
:targetTab="targetTab"
@changeTab="changeTab"
:title="'Session ID'"
id="terminalLogRecordTab"
>
<template v-slot:title><span :title="obj.uuid.substring(0, 8).toUpperCase()">{{obj.uuid.substring(0, 8).toUpperCase()}}</span></template>
<template v-slot>
@@ -36,14 +37,14 @@
<script>
import Terminal from '../../js/Xterm'
import bus from '../../../../libs/bus'
import dataListMixin from '@/components/common/mixin/dataList'
// import dataListMixin from '@/components/common/mixin/dataList'
import subDataListMixin from '@/components/common/mixin/subDataList'
import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList'
import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin'
export default {
name: 'terminalLogReplayTab',
mixins: [dataListMixin, subDataListMixin, detailViewRightMixin],
mixins: [subDataListMixin, detailViewRightMixin],
components: {
nzBottomDataList
},
@@ -77,7 +78,8 @@ export default {
speedOffset: 0, // 快进倍数index
progress: 0, // 进度条进度
needSkip: true, // 是否跳过无操作时间为true时表示需要即不跳过无操作时间
timeUsed: 0
timeUsed: 0,
detailType: 'list'
}
},
methods: {

View File

@@ -0,0 +1,121 @@
<template>
<nz-bottom-data-list
:showTitle='showTitle'
:obj='obj'
:tableId="tableId"
id="sftpBottomTab"
:api="url"
:custom-table-title.sync="tools.customTableTitle"
:layout="['elementSet']"
:tabs="tabs"
:targetTab="targetTab"
:showPagination="false"
@changeTab="changeTab"
:title="'Session ID'"
>
<template v-slot:title><span :title="obj.uuid.substring(0, 8).toUpperCase()">{{obj.uuid.substring(0, 8).toUpperCase()}}</span></template>
<template v-slot>
<terminalLogSftpTable
ref="dataTable"
:orderByFa="'id'"
v-my-loading="tools.loading"
:loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="subTableHeight"
:table-data="tableData"
:terminaLogTab="true"
@del="del"
@edit="edit"
@tableDataSort="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"></terminalLogSftpTable>
</template>
</nz-bottom-data-list>
</template>
<script>
import dataListMixin from '@/components/common/mixin/dataList'
import subDataListMixin from '@/components/common/mixin/subDataList'
import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList'
import terminalLogSftpTable from '@/components/common/table/settings/terminalLogSftpTable'
import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin'
export default {
name: 'sftpBottomTab',
mixins: [dataListMixin, subDataListMixin, detailViewRightMixin],
components: {
nzBottomDataList,
terminalLogSftpTable
},
props: {
obj: Object,
showTitle: {
type: Boolean,
default: true
}
},
data () {
return {
url: '/terminal/sftp/log/',
tableId: 'sftpTable', // 需要分页的table的id用于记录每页数量
detailType: 'list',
searchMsg: { // 给搜索框子组件传递的信息
zheze_none: true,
searchLabelList: []
},
searchLabel: {},
tableData: [],
orderBy: { order: 'ascending', prop: 'id' }
}
},
watch: {
obj: {
deep: true,
handler (n) {
if (n) {
this.getTableData()
}
}
}
},
methods: {
async getTableData () {
this.$set(this.searchLabel, 'uuid', this.obj.uuid)
this.$set(this.searchLabel, 'pageNo', this.pageObj.pageNo)
this.$set(this.searchLabel, 'pageSize', -1)
this.tools.loading = true
this.$get(this.url, this.searchLabel).then(response => {
this.tools.loading = false
if (response.code === 200) {
// this.tableData = response.data.list
this.tableData = this.filterShowData(response.data.list, this.orderBy)
if (!this.scrollbarWrap && this.$refs.dataTable && this.$refs.dataTable.$refs.dataTable) {
this.$nextTick(() => {
this.scrollbarWrap = this.$refs.dataTable.$refs.dataTable.bodyWrapper
this.toTopBtnHandler(this.scrollbarWrap)
})
}
}
})
},
// 数据排序
tableDataSort (item) {
this.orderBy = item
this.filterShowData(this.tableData, item)
},
filterShowData (source, ord) {
let orderBy = null
orderBy = ord
if (orderBy.order === 'ascending') {
source = source.sort(this.$tableSet.asce(orderBy.prop))
}
if (orderBy.order === 'descending') {
source = source.sort(this.$tableSet.desc(orderBy.prop))
}
return source
}
}
}
</script>

View File

@@ -6,7 +6,7 @@
</button>
</div>
<button v-if="this.type === 'btn'" class="table-operation-item delete-button" :id="id" @click="batchDelete" :title="$t('overall.delete')"><i class="nz-icon nz-icon-delete"></i></button>
<div v-if="this.type === 'link'" :id="id" @click="batchDelete" :title="$t('overall.delete')">
<div v-if="this.type === 'link'" :id="id" @click="batchDelete">
<span><i class="nz-icon nz-icon-delete" ></i>{{title}}</span>
</div>
<el-dialog

View File

@@ -20,7 +20,7 @@
<li
v-for="(item,index) in tableData"
:key="index"
class="detail-row"
class="detail-row"
:id="'globalSearch' + item.id"
:class="item.id === detailViewRightObj.id ? 'selected' : ''"
@click="detailViewRightShow(item)"

View File

@@ -10,8 +10,9 @@
<endpoint-tab :showTitle="false" v-if="from === fromRoute.asset && targetTab === fromRoute.endpoint" v-show="subResizeShow" :from="from" :obj="obj" @changeTab="changeTab" :targetTab="targetTab"></endpoint-tab>
<!--terminal-log的记录和回放-->
<terminal-log-cmd-tab :showTitle="false" v-if="from === fromRoute.terminalLog && targetTab === 'cmdTab'" :from="from" :obj="obj" :tabs="tabs.terminalLog.cmd" @changeTab="changeTab" :targetTab="targetTab"></terminal-log-cmd-tab>
<terminal-log-record-tab :showTitle="false" v-if="from === fromRoute.terminalLog && targetTab === 'recordTab'" :from="from" :obj="obj" :tabs="tabs.terminalLog.record" @changeTab="changeTab" :targetTab="targetTab"></terminal-log-record-tab>
<terminal-log-cmd-tab :showTitle="false" v-if="from === fromRoute.terminalLog && targetTab === 'cmdTab'" :from="from" :obj="obj" :tabs="hasTerminalLogTabs" @changeTab="changeTab" :targetTab="targetTab"></terminal-log-cmd-tab>
<terminal-log-sftp-tab :showTitle="false" v-if="from === fromRoute.terminalLog && targetTab === 'sftpTab' && obj.protocol == 'SSH'" :from="from" :obj="obj" :tabs="hasTerminalLogTabs" @changeTab="changeTab" :targetTab.sync="targetTab"></terminal-log-sftp-tab>
<terminal-log-record-tab :showTitle="false" v-if="from === fromRoute.terminalLog && targetTab === 'recordTab'" :from="from" :obj="obj" :tabs="hasTerminalLogTabs" @changeTab="changeTab" :targetTab="targetTab"></terminal-log-record-tab>
<terminal-log-monitor-tab :showTitle="false" v-if="from === fromRoute.terminalLog && targetTab === 'monitorTab'" :from="from" :obj="obj" :tabs="tabs.terminalLog.monitor" @changeTab="changeTab" @exit="closeSubList" :targetTab="targetTab"></terminal-log-monitor-tab>
<asset-tab :showTitle="false" v-if="from === fromRoute.dc && targetTab === 'asset'" :tabs="tabs.dc.asset" ref="assetTab" :from="from" :obj="obj" @changeTab="changeTab" @exit="closeSubList" :targetTab="targetTab"></asset-tab>
@@ -62,6 +63,7 @@ import endpointTab from '@/components/common/bottomBox/tabs/endpointTab'
import endpointTabNew from '@/components/common/bottomBox/tabs/endpointTabNew'
import panelTabNew from '@/components/common/bottomBox/tabs/panelTabNew'
import terminalLogRecordTab from '@/components/common/bottomBox/tabs/terminalLogRecordTab'
import terminalLogSftpTab from '@/components/common/bottomBox/tabs/terminalLogSftpTab'
import terminalLogMonitorTab from '@/components/common/bottomBox/tabs/terminalLogMonitorTab'
import terminalLogCMDTab from '@/components/common/bottomBox/tabs/terminalLogCMDTab'
import operationLogTab from '@/components/common/bottomBox/tabs/operationLogTab'
@@ -83,6 +85,7 @@ export default {
alertMessageTab,
endpointTab,
terminalLogRecordTab,
terminalLogSftpTab,
terminalLogMonitorTab,
operationLogTab,
terminalLogTab,
@@ -133,15 +136,15 @@ export default {
terminalLog: {
monitor: [
{ prop: 'monitorTab', name: this.$t('config.terminallog.monitor.monitor'), active: true }
],
cmd: [
{ prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: true },
{ prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: false }
],
record: [
{ prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: false },
{ prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: true }
]
// cmd: [
// { prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: true },
// { prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: false }
// ],
// record: [
// { prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd'), active: false },
// { prop: 'recordTab', name: this.$t('config.terminallog.record.record'), active: true }
// ]
},
user: {
operationLog: [
@@ -213,6 +216,17 @@ export default {
}
},
computed: {
hasTerminalLogTabs () {
const hasSftp = this.obj && this.obj.protocol == 'SSH'
const tabs = [
{ prop: 'cmdTab', name: this.$t('config.terminallog.cmd.cmd') },
{ prop: 'recordTab', name: this.$t('config.terminallog.record.record') }
]
if (hasSftp) {
tabs.splice(1, 0, { prop: 'sftpTab', name: 'SFTP ' })
}
return tabs
},
hasLogConfig () {
const config = this.obj.configs.find(c => c.type === 'logs')
return config && config.enable === 1

View File

@@ -418,10 +418,10 @@ export const fromRoute = {
assetType: 'assetType',
assetState: 'assetState',
assetLabel: 'assetLabel',
expressionTemplate: 'expressionTemplate',
user: 'user',
agent: 'agent',
recordRule: 'recordRule',
recordRulesQuery: 'recordRulesQuery',
issue: 'issue',
dc: 'dc',
role: 'role',
@@ -437,6 +437,8 @@ export const fromRoute = {
ipam: 'ipam',
apiKey: 'apiKey',
chartTemp: 'chartTemp',
dashboardTemp: 'dashboardTemp',
expressionTemplate: 'expressionTemplate',
backups: 'backups',
ping: 'ping',
trace: 'trace'

View File

@@ -335,6 +335,22 @@ export default {
}
this.silenceBoxShow = true
},
metricTarget (row, type) {
const protocol = row.configs[0].config.protocol
const host = row.configs[0].config.host
const port = row.configs[0].config.port
const metricsPath = row.configs[0].config.metrics_path
const params = row.configs[0].config.params
let str = ''
if (!lodash.isEmpty(params)) {
for (const key in params) {
str += key + '=' + params[key] + '&'
}
}
const linkTarget = `${protocol}://${host}:${port + metricsPath}${str ? '?' + str.slice(0, str.length - 1) : ''}`
// window.open(linkTarget, 'target')
window.open(linkTarget)
},
topology (row, type) {
const chartInfo = lodash.cloneDeep(defaultData)
chartInfo.id = row.id
@@ -1326,10 +1342,12 @@ export default {
}
let localStorageTableTitle = localStorage.getItem('nz-tableTitle-' + localStorage.getItem('nz-username') + '-' + this.tableId)
localStorageTableTitle = localStorageTableTitle ? JSON.parse(localStorageTableTitle) : tableTitle
this.tools.customTableTitle = tableTitle.map((item, index) => { // 修复切换中英文的问题
item.show = localStorageTableTitle[index].show
return item
})
if (tableTitle) {
this.tools.customTableTitle = tableTitle.map((item, index) => { // 修复切换中英文的问题
item.show = localStorageTableTitle[index].show
return item
})
}
if (localStorageTableTitle && (localStorageTableTitle.length > tableTitle.length)) {
const arr = localStorageTableTitle.splice(tableTitle.length, localStorageTableTitle.length)
arr.forEach(item => {

View File

@@ -33,7 +33,10 @@ export default {
changeDetailType (flag) {
setTimeout(() => {
const query = { ...this.$route.query, detailType: flag }
query.bottomBox = this.detailType !== 'view'
if (this.detailType == 'view') {
delete query.bottomBox
delete query.selectObj
}
this.$router.replace({ query: query }).catch(err => {})
}, 100)
if (this.detailType === flag) {

View File

@@ -1,4 +1,3 @@
import i18n from '@/components/common/i18n'
import { getUUID } from '../js/common'
export default {
data () {

View File

@@ -1,5 +1,4 @@
import bus from '@/libs/bus'
import { Loading } from 'element-ui'
export default {
data () {
return {

View File

@@ -95,6 +95,22 @@ export default {
this.$copyText(txt).then(() => {
this.$message.success({ message: this.$t('overall.copySuccess') })
})
},
animateCSS (node, animation, prefix = 'animate__') {
// We create a Promise and return it
return new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`
node.classList.add(`${prefix}animated`, animationName)
// When the animation ends, we clean the classes and resolve the Promise
function handleAnimationEnd (event) {
event.stopPropagation()
node.classList.remove(`${prefix}animated`, animationName)
resolve('Animation ended')
}
node.addEventListener('animationend', handleAnimationEnd, { once: true })
})
}
}
}

View File

@@ -61,66 +61,122 @@ export default {
params.bottomBox = false
params.targetTab = this.$route.query.targetTab
}
if (!params.targetTab) delete params.targetTab
if (!params.bottomBox) delete params.bottomBox
if (this.detailType === 'list' && this.$refs[detailType]) {
params.selectObj = JSON.stringify({
const obj = {
id: this.$refs[detailType].bottomBox.object.id,
name: this.$refs[detailType].bottomBox.object.name,
configs: this.$refs[detailType].bottomBox.object.configs ? this.$refs[detailType].bottomBox.object.configs.map(item => { return { type: item.type, enable: item.enable } }) : '',
childrenNum: this.$refs[detailType].bottomBox.object.childrenNum || '',
clientState: this.$refs[detailType].bottomBox.object.clientState || '',
protocol: this.$refs[detailType].bottomBox.object.protocol || '',
uuid: this.$refs[detailType].bottomBox.object.uuid || '',
startTime: this.$refs[detailType].bottomBox.object.startTime || '',
model: {
tsgAppliance: this.$refs[detailType].bottomBox.object.model ? this.$refs[detailType].bottomBox.object.model.tsgAppliance : ''
}
})
}
if (this.$refs[detailType].bottomBox.object.configs) {
// 判断是JSON字符串还是对象
if (typeof (this.$refs[detailType].bottomBox.object.configs) == 'string') {
obj.configs = JSON.parse(this.$refs[detailType].bottomBox.object.configs).map(item => { return { type: item.type, enable: item.enable } })
} else {
obj.configs = this.$refs[detailType].bottomBox.object.configs.map(item => { return { type: item.type, enable: item.enable } })
}
}
this.removeEmpty(obj)
if (JSON.stringify(obj) != '{}') {
params.selectObj = JSON.stringify(obj)
}
} else if (this.$refs[detailType] && this.$refs[detailType].detailViewRightObj) {
this.$nextTick(() => {
params.selectObj = JSON.stringify({
const obj = {
id: this.$refs[detailType].detailViewRightObj.id,
name: this.$refs[detailType].detailViewRightObj.name,
configs: this.$refs[detailType].detailViewRightObj.configs ? this.$refs[detailType].detailViewRightObj.configs.map(item => { return { type: item.type, enable: item.enable } }) : '',
childrenNum: this.$refs[detailType].detailViewRightObj.childrenNum || '',
clientState: this.$refs[detailType].detailViewRightObj.clientState || '',
protocol: this.$refs[detailType].detailViewRightObj.protocol || '',
uuid: this.$refs[detailType].detailViewRightObj.uuid || '',
startTime: this.$refs[detailType].detailViewRightObj.startTime || '',
model: {
tsgAppliance: this.$refs[detailType].detailViewRightObj.model ? this.$refs[detailType].detailViewRightObj.model.tsgAppliance : ''
}
})
}
if (this.$refs[detailType].detailViewRightObj.configs) {
// 判断是JSON字符串还是对象
if (typeof (this.$refs[detailType].detailViewRightObj.configs) == 'string') {
obj.configs = JSON.parse(this.$refs[detailType].detailViewRightObj.configs).map(item => { return { type: item.type, enable: item.enable } })
} else {
obj.configs = this.$refs[detailType].detailViewRightObj.configs.map(item => { return { type: item.type, enable: item.enable } })
}
}
this.removeEmpty(obj)
if (JSON.stringify(obj) != '{}') {
params.selectObj = JSON.stringify(obj)
}
})
}
this.$router.replace({ path: path, query: params }).catch(err => {})
} else if (from === 'nzDatalist' && this.bottomBox) {
params.bottomBox = this.bottomBox.showSubList
params.targetTab = this.bottomBox.targetTab
params.selectObj = JSON.stringify({
id: this.bottomBox.object.id,
name: this.bottomBox.object.name,
configs: this.bottomBox.object.configs ? this.bottomBox.object.configs.map(item => { return { type: item.type, enable: item.enable } }) : '',
childrenNum: this.bottomBox.object.childrenNum || '',
clientState: this.bottomBox.object.clientState || '',
uuid: this.bottomBox.object.uuid || '',
startTime: this.bottomBox.object.startTime || '',
model: {
tsgAppliance: this.bottomBox.object.model ? this.bottomBox.object.model.tsgAppliance : ''
if (!params.targetTab) delete params.targetTab
if (!params.bottomBox) delete params.bottomBox
if (this.bottomBox.showSubList) { // 关闭bottomBox打开还是关闭
const obj = {
id: this.bottomBox.object.id,
name: this.bottomBox.object.name,
childrenNum: this.bottomBox.object.childrenNum || '',
clientState: this.bottomBox.object.clientState || '',
protocol: this.bottomBox.object.protocol || '',
uuid: this.bottomBox.object.uuid || '',
startTime: this.bottomBox.object.startTime || '',
model: {
tsgAppliance: this.bottomBox.object.model ? this.bottomBox.object.model.tsgAppliance : ''
}
}
})
if (this.bottomBox.object.configs) {
// 判断是JSON字符串还是对象
if (typeof (this.bottomBox.object.configs) == 'string') {
obj.configs = JSON.parse(this.bottomBox.object.configs).map(item => { return { type: item.type, enable: item.enable } })
} else {
obj.configs = this.bottomBox.object.configs.map(item => { return { type: item.type, enable: item.enable } })
}
}
this.removeEmpty(obj)
if (JSON.stringify(obj) != '{}') {
params.selectObj = JSON.stringify(obj)
}
} else {
delete params.selectObj
}
this.$router.replace({ path: path, query: params }).catch(err => {})
} else if (from === 'nzDetailList' && this.detailViewRightObj) {
params.targetTab = this.$route.query.targetTab
params.selectObj = JSON.stringify({
const obj = {
id: this.detailViewRightObj.id,
name: this.detailViewRightObj.name,
configs: this.detailViewRightObj.configs ? this.detailViewRightObj.configs.map(item => { return { type: item.type, enable: item.enable } }) : '',
childrenNum: this.detailViewRightObj.childrenNum || '',
clientState: this.detailViewRightObj.clientState || '',
protocol: this.detailViewRightObj.protocol || '',
uuid: this.detailViewRightObj.uuid || '',
startTime: this.detailViewRightObj.startTime || '',
model: {
tsgAppliance: this.detailViewRightObj.model ? this.detailViewRightObj.model.tsgAppliance : ''
}
})
}
if (this.detailViewRightObj.configs) {
// 判断是JSON字符串还是对象
if (typeof (this.detailViewRightObj.configs) == 'string') {
obj.configs = JSON.parse(this.detailViewRightObj.configs).map(item => { return { type: item.type, enable: item.enable } })
} else {
obj.configs = this.detailViewRightObj.configs.map(item => { return { type: item.type, enable: item.enable } })
}
}
this.removeEmpty(obj)
if (JSON.stringify(obj) != '{}') {
params.selectObj = JSON.stringify(obj)
}
this.$router.replace({ path: path, query: params }).catch(err => {})
} else if (from === 'bottomBox' && this.targetTab) {
params.targetTab = this.targetTab
@@ -153,6 +209,26 @@ export default {
setTimeout(() => {
$temp.$refs[formName].validate()
}, 100)
},
// 清除空数据
removeEmpty (obj) {
if (!obj) {
return
}
Object.keys(obj).forEach(key => {
// 属性为对象
if (Object.prototype.toString.call(obj[key]) === '[object Object]' && (JSON.stringify(obj[key]) != '{}')) {
this.removeEmpty(obj[key])
}
// 属性值为空
if (!obj[key]) {
delete obj[key]
}
// 属性为空对象
if ((Array.isArray(obj[key]) && !obj[key].length) || (JSON.stringify(obj[key]) == '{}')) {
delete obj[key]
}
})
}
}
}

View File

@@ -81,7 +81,8 @@ export default {
metrics_storage_s3_endpoint: '',
metrics_storage_s3_secret_access_key: '',
metrics_storage_type: '',
prometheus_federation_enabled: ''
prometheus_federation_enabled: '',
interface_name: []
},
monitorRules: {
// alert_api: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }],
@@ -118,12 +119,18 @@ export default {
terminal_timeout: 30,
terminal_telnet_user_tip: 'ogin:',
terminal_telnet_pin_tip: 'assword:',
terminal_record_local_retention: 365
terminal_record_local_retention: 365,
terminal_storage_type: '',
terminal_storage_s3_endpoint: '',
terminal_storage_s3_bucket: '',
terminal_storage_s3_access_key: '',
terminal_storage_s3_secret_access_key: ''
},
terminalCopy: null,
terminalRules: {
terminal_timeout: [{ validator: positiveInteger, trigger: 'blur' }],
terminal_record_local_retention: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }, { validator: positiveInteger, trigger: 'blur' }]
terminal_record_local_retention: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }, { validator: positiveInteger, trigger: 'blur' }],
terminal_storage_type: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }]
},
ldap: {
ldap_address: '',
@@ -253,6 +260,13 @@ export default {
this.$refs.emailForm.clearValidate()
}
if (type == 'monitor') {
if(response.data.interface_name){
let arr = response.data.interface_name.split(',')
this.monitor.interface_name = arr.map(item=>{
return {text: item}
})
}
localStorage.setItem('nz-prometheus-federation-enabled', this.monitor.prometheus_federation_enabled)
}
this[type + 'Copy'] = Object.assign({}, this[type])
@@ -270,6 +284,12 @@ export default {
param.map_center_config = JSON.stringify(mapConfig)
param.pin_policy = JSON.stringify(this.basic.pin_policy)
}
if(type == 'monitor') {
let str = this.monitor.interface_name.map(item=>{
return item.text
})
param.interface_name = str.join(',')
}
const postParam = Object.assign({}, param)
for (const key in postParam[type]) {
postParam[type][key] = postParam[type][key] + ''

View File

@@ -78,6 +78,10 @@ export default {
this.$emit('topology', row, param)
break
}
case 'metricTarget': {
this.$emit('metricTarget', row, param)
break
}
default:
this.$emit(command, row, param)
break

View File

@@ -198,9 +198,7 @@ export default {
},
methods: {
dateChange (val) {
console.log(val)
const startTime = this.momentTz(this.momentStrToTimestamp(val))
console.log(this.stepSearchTime[1], this.stepSearchTime[0])
const end = this.momentStrToTimestamp(val) + this.momentStrToTimestamp(this.stepSearchTime[1]) - this.momentStrToTimestamp(this.stepSearchTime[0])
const endTime = this.momentTz(end, this.multipleTime)
this.$set(this.searchTime, 0, startTime)

View File

@@ -33,7 +33,7 @@
<div class="choose-box-content" :title="item.checked.map(value=> labelValue[item.name].find(labelObj=>labelObj.value === value).label).join(' + ')">
{{item.checked.map(value=> labelValue[item.name].find(labelObj=>labelObj.value === value).label).join(' + ')}}
</div>
<i class="nz-icon nz-icon-arrow-down6"/>
<i class="nz-icon nz-icon-arrow-down6" style="cursor:pointer"/>
</div>
</div>
</el-popover>
@@ -389,13 +389,22 @@ export default {
})
},
close () {
let flag = false
this.labelArr.forEach((item) => {
if (item.visible && item.multi) {
this.$store.dispatch('dispatchVariablesArr', [...this.labelArr])
flag = true
}
item.visible = false
})
if (flag) {
const oldVariables = this.$store.state.panel.variablesArr
// 如果新旧值相等 则不执行
if (JSON.stringify(oldVariables) !== JSON.stringify(this.labelArr)) {
this.$store.dispatch('dispatchVariablesArr', [...this.labelArr])
}
}
}
},
beforeDestroy () {
this.$store.dispatch('dispatchVariablesArr', [])

View File

@@ -9,9 +9,7 @@
</li>
</ul>
<div class="dashboard-select-tail">
<span class="dashboard-select-add" v-has="'main_add'" :title='$t("dashboard.panel.createPanelTitleSec")' @click="toAdd">
<i class="nz-icon nz-icon-create-square"></i>&nbsp;&nbsp;{{$t('overall.addDashboard')}}
</span>
<slot name="button"></slot>
</div>
</div>
<div class="pop-item-wider">
@@ -169,10 +167,6 @@ export default {
}
},
methods: {
// 新增仪表盘
toAdd () {
this.$parent.toAdd()
},
// 左侧tab点击
tabChange (value) {
if (this.currentTab !== value) {

View File

@@ -30,48 +30,24 @@
:title="importBox.title"
:visible.sync="importBox.show"
width="580px"
:modal-append-to-body="false"
:append-to-body="true"
@close="closeDialog"
:class="{'import-failContent-dialog':importBox.type==3&&resultNew}"
:class="{'import-failContent-dialog':importBox.type==3}"
class="nz-dialog">
<!-- 导入 -->
<div v-if="importBox.type == 1&&!importNew">
<div class="upload-body" style="margin:10px 0;">
<el-upload :id="id+'-xlsx-input-file'" ref="uploadExcel" :auto-upload="false" :file-list="importFileList" :on-change="importChange" accept=".xlsx,.xls" action="" class="upload-demo" drag>
<div slot="tip" class="el-upload__tip" style="margin-top: 0;">{{$t('overall.importTip')}}</div>
<i class="nz-icon nz-icon-upload"></i>
<div class="el-upload__text">{{$t('overall.dragFileTip')}}{{$t('overall.or')}}&nbsp;<em>{{$t('overall.clickUpload')}}</em></div>
</el-upload>
</div>
<div class="footer">
<div class="el-message-box__btns" style="text-align: right;">
<button :id="id+'-xlsx-import-template'" class="nz-btn el-button el-button--default el-button--small" @click="downloadTemplate()">
<span>{{$t('upload.template')}}</span>
</button>
<button :id="id+'-xlsx-import-add'" :class="{'nz-btn-disabled':prevent_opt.import}" :disabled="prevent_opt.import" class="nz-btn el-button el-button--default el-button--small" @click="importExcel">
<span>{{$t('overall.importExcel')}}</span>
</button>
<button :id="id+'-xlsx-import-esc'" class="nz-btn el-button el-button--default el-button--small" @click="closeDialog">
<span>{{$t('overall.cancel')}}</span>
</button>
</div>
</div>
</div>
<!-- 新版导入 -->
<div v-if="importBox.type == 1&&importNew">
<div v-if="importBox.type == 1">
<div class="upload-body">
<el-upload :id="id+'-xlsx-input-file'" ref="uploadExcel" :auto-upload="false" :file-list="importFileList" :on-change="importChange" accept=".xlsx,.xls,.csv,.json" action="" class="import-upload" drag>
<el-upload :id="id+'-xlsx-input-file'" ref="uploadExcel" :auto-upload="false" :file-list="importFileList" :on-change="importChange" :accept="!isDashboard?'.xlsx,.xls,.csv,.json':'.json'" action="" class="import-upload" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">{{$t('overall.dragFileTip')}}{{$t('overall.or')}}&nbsp;<em>{{$t('overall.clickUpload')}}</em></div>
<div class="el-upload__tip" >{{$t('overall.importSupport')}}</div>
<div class="el-upload__tip" >{{!isDashboard?$t('overall.importSupport'):$t('overall.supportJson')}}</div>
</el-upload>
</div>
<div class="import-select">
<div v-if="showEexisted" class="exists">
<div class="exists">
<span>{{$t('overall.existed')}}</span>
<el-select v-model="importBox.existed" style="flex:1" size="medium" popper-class="exists-select">
<el-select v-model="importBox.existed" :popper-append-to-body="false" style="flex:1" size="medium" popper-class="exists-select">
<el-option
v-for="item in existedArr"
:key="item.value"
@@ -81,7 +57,7 @@
</el-select>
</div>
<ul class="import-select-list">
<li v-if="showIgnoreError" class="import-select-item">
<li class="import-select-item">
<el-checkbox v-model="importBox.ignoreError">{{$t('overall.ignoreError')}}</el-checkbox>
</li>
<li v-if="showSyncDashboard" class="import-select-item">
@@ -95,6 +71,7 @@
<div class="footer">
<div class="el-message-box__btns" style="text-align: right;">
<el-popover
v-if="!isDashboard"
placement="bottom-start"
trigger="click">
<div class="template-pop">
@@ -133,7 +110,7 @@
<div class="export-box">
<span class="export-title">{{$t('export.fileFormat')}}</span>
<el-radio-group v-model="importBox.format" size="small">
<el-radio-button :label="item.value" v-for="(item,index) in formatArr" :key="index">{{item.name}}</el-radio-button>
<el-radio-button :label="item.value" v-for="(item,index) in formatArr" :key="index" :disabled="isDashboard&&item.name!='JSON'">{{item.name}}</el-radio-button>
</el-radio-group>
</div>
</div>
@@ -149,64 +126,8 @@
</div>
</div>
<!-- 导入结果不展示导出失败记录 -->
<div v-if="importBox.type==3&&!resultNew">
<div class="upload-body result-body">
<div v-if="importResult&&importResult.failNum">
<div class="result-title-top">
<div class="">
<i class="nz-icon nz-icon-import-success"/>
<span>{{$t('overall.result.successNum',{successNum:importResult.successNum})}}</span><br/>
</div>
<div class="">
<i class="nz-icon nz-icon-import-failed"/>
<span>{{$t('overall.result.failedNum',{failedNum:importResult.failNum,total:importResult.totalNum})}}</span>
</div>
</div>
<div>
<div v-if="importResult&&importResult.failDetail" class="result-detail">
<div style="height: 100%; overflow: auto">
<template v-for="(item, index) in importResult.failDetail">
<div :key="index" class="import-result-block">
<div class="import-result-item">
<div style="display: flex;">
<span class="import-result-item-left"></span>
<div class="line-num">{{$t('overall.result.line',[item.lineNo])}}</div>
</div>
<div class="line-content" :title="item.errorMsg">{{item.errorMsg}}</div>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
<div v-else class="result-success-box">
<div>
<i class="nz-icon nz-icon-import-success"/>
</div>
<div class="result-success-txt">{{$t('overall.result.successfully')}}</div>
<div class="result-success-txt">
{{$t('overall.result.imported')}}
<span v-if="importResult">{{importResult.successNum}}</span>
{{$t('overall.result.records')}}
</div>
</div>
</div>
<div class="footer">
<div class="el-message-box__btns">
<button :id="id+'-xlsx-import-rollback'" class="nz-btn el-button el-button--small nz-btn-style-error" @click="rollbackImport">
<span>{{$t('overall.rollbackImport')}}</span>
</button>
<button :id="id+'-xlsx-import-close'" class="nz-btn el-button el-button--small el-button--default" @click="closeDialog">
<span>{{$t('overall.close')}}</span>
</button>
</div>
</div>
</div>
<!-- 导入结果展示导出失败记录 -->
<div v-if="importBox.type==3&&resultNew">
<div v-if="importBox.type==3">
<div class="upload-body result-body">
<!-- 失败提示 -->
<div v-if="importResult&&importResult.failNum">
@@ -348,29 +269,13 @@ export default {
deleteObjs: Array,
exportBoxShow: {
type: Boolean, default: false
},
// 已存在的内容处理方式
showEexisted: {
type: Boolean, default: true
},
// 遇到错误是否继续导入
showIgnoreError: {
type: Boolean, default: true
}
},
computed: {
language () { return this.$store.getters.getLanguage },
// 是否展示新版导入
importNew () {
// chartTemplate dashboard
const arr = ['/visual/panel/import']
return !arr.some(item => item === this.importUrl)
},
// 是否展示新版导入结果
resultNew () {
// chartTemplate dashboard导入样式维持原状
const arr = ['/visual/panel/import']
return !arr.some(item => item === this.importUrl)
// 判断是否是dashboard
isDashboard () {
return this.importUrl === '/visual/panel/import'
},
// 是否同步更新关联的dashboard
showSyncDashboard () {
@@ -492,12 +397,8 @@ export default {
this.prevent_opt.import = true
const form = new FormData()
form.append('file', this.importFile.raw)
if (this.showEexisted) {
form.append('existed', this.importBox.existed)
}
if (this.showIgnoreError) {
form.append('ignoreError', Number(this.importBox.ignoreError))
}
form.append('existed', this.importBox.existed)
form.append('ignoreError', Number(this.importBox.ignoreError))
if (this.showSyncDashboard) {
form.append('syncDashboard', Number(this.importBox.syncDashboard))
}
@@ -632,6 +533,9 @@ export default {
delete params.end_time
delete params.id
delete params.searchName
delete params.value
params.ids = params.panelId
delete params.panelId
}
params.language = localStorage.getItem('nz-language') || 'en'
params.format = this.importBox.format
@@ -660,8 +564,7 @@ export default {
delete params.end_time
delete params.id
delete params.searchName
}
if (this.importUrl.indexOf('panel') > -1) {
delete params.value
delete params.panelId
}
// if (this.importUrl.indexOf('endpoint') > -1){
@@ -694,8 +597,7 @@ export default {
delete params.end_time
delete params.id
delete params.searchName
}
if (this.importUrl.indexOf('panel') > -1) {
delete params.value
delete params.panelId
}
// if (this.importUrl.indexOf('endpoint') > -1){
@@ -779,12 +681,10 @@ export default {
uInt8Array[i] = raw.charCodeAt(i)
}
const link = document.createElement('a')
const blob = new Blob([uInt8Array], {
type: 'application/vnd.ms-excel'
})
const blob = new Blob([uInt8Array])
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
link.setAttribute('download', 'failed_records' + '-' + this.getTimeString() + '.xlsx')
link.setAttribute('download', 'failed_records' + '-' + this.getTimeString() + '.json')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
@@ -792,6 +692,10 @@ export default {
showImportBox (type) {
this.importBox.show = true
this.importBox.type = type
// isDashboard 只支持JSON
if (this.isDashboard) {
this.importBox.format = 3
}
if (type == 2 && (!this.showCur)) {
this.exportCur()
return

View File

@@ -133,7 +133,7 @@
:use-chart-unit="false"
class="pickTime margin-r-10">
</pick-time>
<button v-has="'project_edit'" class="top-tool-btn margin-r-10" type="button" @click="editTopology" :title="$t('overall.edit')">
<button v-has="'project_edit'" class="top-tool-btn margin-r-10" type="button" @click="editTopology(false)" :title="$t('overall.edit')">
<i class="nz-icon nz-icon-edit"></i>
</button>
<button class="top-tool-btn" type="button" @click="changeScreen" :title="topoScreen? $t('dashboard.screen.exit') : $t('dashboard.screen')">
@@ -662,6 +662,7 @@ export default {
init () {
// canvasOptions.on = this.onMessage
this.reload()
this.popDataShowUpdate('', false)
},
reload () {
@@ -1982,7 +1983,7 @@ export default {
}
},
editTopology (val) {
editTopology (isPreview) {
this.editTopologyFlag = true
this.showNoData = false
this.topoScreenState = JSON.parse(JSON.stringify(this.topoScreen))
@@ -2027,6 +2028,13 @@ export default {
item.animatePlay = false
item.fontColor = item.data.fontColor || '#222222'
}
if (isPreview) {
const scale = getTopology(this.topologyIndex).store.data.scale
item.x = item.x / scale
item.y = item.y / scale
item.width = item.width / scale
item.height = item.height / scale
}
getTopology(this.topologyIndex)._setValue(item)
})
getTopology(this.topologyIndex).render(true)
@@ -2212,7 +2220,7 @@ export default {
},
previewExit () { // 继续编辑
this.isPreview = false
this.editTopology()
this.editTopology(true)
},
// 联动 project
@@ -2407,7 +2415,6 @@ export default {
window.removeEventListener('click', this.contextmenuNone)
},
destroyed () {
}
}
</script>

View File

@@ -104,7 +104,15 @@ export default {
if (data.type === 0 || data.type === 1) {
data.lineWidth = data.data.lineWidth
}
getTopology(this.topologyIndex).setValue(data)
const obj = this.$loadsh.cloneDeep(data)
delete obj.x
delete obj.y
delete obj.center
delete obj.ex
delete obj.ey
delete obj.width
delete obj.height
getTopology(this.topologyIndex).setValue(obj)
},
pensActive (pens) {
this.props = {

View File

@@ -11,7 +11,7 @@
<el-form ref="assetTypeForm" :model="editAssetType" :rules="rules" label-position="top" label-width="120px">
<!--name-->
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="64" show-word-limit v-model.trim="editAssetType.name" size="small" type="text"></el-input>
<el-input maxlength="64" show-word-limit v-model="editAssetType.name" size="small" type="text"></el-input>
</el-form-item>
<!--vm-->
<el-form-item :label='$t("config.assetType.vm")' prop="vm">

View File

@@ -59,7 +59,7 @@
<el-input
v-if="whoshow === 3 ? true : false"
size="small"
v-model.trim="editBackup.repeat"
v-model="editBackup.repeat"
id="chart-box-title"
@change="inputchange"
>
@@ -68,7 +68,7 @@
<el-input
v-if="whoshow === 2 ? true : false"
size="small"
v-model.trim="editBackup.repeat"
v-model="editBackup.repeat"
id="chart-box-title"
@change="inputchange"
>
@@ -120,7 +120,7 @@
<el-input
maxlength="64"
size="small"
v-model.trim="editBackup.retention"
v-model="editBackup.retention"
id="chart-box-title"
@change="inputchange"
></el-input>

View File

@@ -11,7 +11,7 @@
<el-form ref="modelForm" :model="editModel" :rules="rules" label-position="top" label-width="120px">
<!--name-->
<el-form-item :label="$t('config.model.name')" prop="name">
<el-input maxlength="64" show-word-limit v-model.trim="editModel.name" size="small" type="text"></el-input>
<el-input maxlength="64" show-word-limit v-model="editModel.name" size="small" type="text"></el-input>
</el-form-item>
<!--brand-->
<el-form-item :label='$t("asset.brand")' prop="brandId">
@@ -21,12 +21,12 @@
</el-form-item>
<!-- type -->
<el-form-item :label='$t("overall.type")' prop="typeId">
<el-select value-key="id" allow-create class="right-box__select" popper-class="right-box-select-top prevent-clickoutside" :filterable="true" v-model="editModel.typeId" placeholder="" size="small" id="module-box-input-project">
<el-select :disabled='editModel.id !== ""' value-key="id" allow-create class="right-box__select" popper-class="right-box-select-top prevent-clickoutside" :filterable="true" v-model="editModel.typeId" placeholder="" size="small" id="module-box-input-project">
<el-option v-for="(item, index) in typeDataList" :key="index" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<!-- ChartTemplate -->
<el-form-item :label="$t('overall.chartTemp')" prop="ChartTemplate">
<!-- <el-form-item :label="$t('overall.chartTemp')" prop="ChartTemplate">
<v-selectpage
:data="chartlList"
:tb-columns="ChartSearchShowFields"
@@ -45,10 +45,29 @@
@values="(data) => {editModel.chartIds = data.map(d => d.id).join(',')}"
:result-format="resultFormat"
></v-selectpage>
</el-form-item> -->
<!-- DashboardTemplate -->
<el-form-item :label="$t('model.dashboardtemplate')" prop="panelId" ref="dashboardTemplate">
<v-selectpage
:data="dashboardList"
:tb-columns="DashboardSearchShowFields"
:params="{
varType: 1,type: 'template'}"
:multiple="false"
:language="language"
title="DashboardSearch"
key-field="id"
:width="634"
v-model="editModel.panelId"
show-field="name"
class="form-control"
@values="(data) => {editModel.panelId = data.map(d => d.id).join(',')}"
:result-format="resultFormat"
></v-selectpage>
</el-form-item>
<!-- sys object id -->
<el-form-item :label="$t('config.model.sysObjectId')" prop="sysObjectId">
<el-input maxlength="64" show-word-limit v-model.trim="editModel.sysObjectId" size="small" type="text"></el-input>
<el-input maxlength="64" show-word-limit v-model="editModel.sysObjectId" size="small" type="text"></el-input>
</el-form-item>
<!--remark-->
<el-form-item :label="$t('overall.remark')" prop="remark">
@@ -100,11 +119,17 @@ export default {
brandList: [], // brand 列表数据
editModule: {},
typeDataList: [],
chartlList: [], // chart 列表数据
ChartSearchShowFields: [ // ChartSearch 下拉搜索表头
// chartlList: [], // chart 列表数据
dashboardList: [], // dashboard 列表数据
// ChartSearchShowFields: [ // ChartSearch 下拉搜索表头
// { title: 'ID', data: 'id' },
// { title: this.$t('overall.name'), data: 'name', key: 'name' },
// { title: this.$t('overall.type'), data: 'type', key: 'type' },
// { title: this.$t('overall.remark'), data: 'remark', key: 'remark' }
// ],
DashboardSearchShowFields: [ // DashboardSearch 下拉搜索表头
{ title: 'ID', data: 'id' },
{ title: this.$t('overall.name'), data: 'name', key: 'name' },
{ title: this.$t('overall.type'), data: 'type', key: 'type' },
{ title: this.$t('overall.remark'), data: 'remark', key: 'remark' }
],
url: 'asset/model',
@@ -134,13 +159,15 @@ export default {
handler (n) {
this.isEdit = true
this.editModel = JSON.parse(JSON.stringify(n))
this.editModel.panelId = n.panelId ? n.panelId + '' : ''
}
}
},
created () {
this.getBrandList()
this.modelTypeList()
this.ChartTemplateList()
// this.ChartTemplateList()
this.DashboardTemplateList()
},
methods: {
clickOutside () {
@@ -154,8 +181,12 @@ export default {
saveModel () {
this.$refs.modelForm.validate((valid) => {
if (valid) {
const params = {
...this.editModel,
panelId: this.editModel.panelId == '' ? null : Number(this.editModel.panelId)
}
if (this.editModel.id) {
this.$put(this.url, this.editModel).then(res => {
this.$put(this.url, params).then(res => {
this.prevent_opt.save = false
if (res.code === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
@@ -165,7 +196,7 @@ export default {
}
})
} else {
this.$post(this.url, this.editModel).then(res => {
this.$post(this.url, params).then(res => {
this.prevent_opt.save = false
if (res.code === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
@@ -225,10 +256,16 @@ export default {
return assetData
}
},
/* 获取chart列表数据 */
ChartTemplateList () {
this.$get('visual/panel/chart', { pageSize: -1, varType: 1, panelId: 0, groupId: 0, returnChildren: 0 }).then(res => {
this.chartlList = res.data.list
// /* 获取chart列表数据 */
// ChartTemplateList () {
// this.$get('visual/panel/chart', { pageSize: -1, varType: 1, panelId: 0, groupId: 0, returnChildren: 0 }).then(res => {
// this.chartlList = res.data.list
// })
// },
/* 获取Dashboard列表数据 */
DashboardTemplateList () {
this.$get('visual/panel', { pageSize: -1, varType: 1, type: 'template' }).then(res => {
this.dashboardList = res.data.list
})
},
modelTypeList () {

View File

@@ -12,7 +12,7 @@
<!--name-->
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="64" placeholder="" id="role-box-input-name"
show-word-limit size="small" type="text" v-model.trim="editRole.name"></el-input>
show-word-limit size="small" type="text" v-model="editRole.name"></el-input>
</el-form-item>
<el-form-item :label="$t('overall.remark')">
<el-input maxlength="256" show-word-limit :rows="2" size='small' type="textarea" v-model="editRole.remark" id="role-box-input-remark"/>

View File

@@ -11,22 +11,22 @@
<el-form ref="userForm" :model="editUser" :rules="editUser.id ? rules2 : rules" label-position="top" label-width="120px">
<!--name-->
<el-form-item :label="$t('config.user.name')" prop="name">
<el-input id="account-input-name" v-model.trim="editUser.name" :disabled="editUser.username==='admin' && editUser.id==1"
<el-input id="account-input-name" v-model="editUser.name" :disabled="editUser.username==='admin' && editUser.id==1"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--username-->
<el-form-item :label="$t('login.username')" prop="username">
<el-input id="account-input-username" v-model.trim="editUser.username" :disabled="editUser.username==='admin' && editUser.id==1"
<el-input id="account-input-username" v-model="editUser.username" :disabled="editUser.username==='admin' && editUser.id==1"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--password-->
<el-form-item :label="$t('login.pin')" prop="pin">
<el-input id="account-input-password" v-model.trim="editUser.pin" maxlength="64" placeholder=""
<el-input id="account-input-password" v-model="editUser.pin" maxlength="64" placeholder=""
show-word-limit size="small" type="password" @blur="pinBlur" autocomplete="new-password"></el-input>
</el-form-item>
<!--pinChange-->
<el-form-item :label="$t('profile.confirmPassword')" label-width="200px" prop="pinChange">
<el-input id="account-input-pinChange" v-model.trim="editUser.pinChange" maxlength="64" placeholder=""
<el-input id="account-input-pinChange" v-model="editUser.pinChange" maxlength="64" placeholder=""
show-word-limit size="small" type="password"></el-input>
</el-form-item>
<!--mfaLevel-->
@@ -46,7 +46,7 @@
</el-form-item>
<!--email-->
<el-form-item :label="$t('config.system.email.email')" prop="email">
<el-input id="account-input-email" v-model.trim="editUser.email" maxlength="64" show-word-limit placeholder="" size="small" type="text"></el-input>
<el-input id="account-input-email" v-model="editUser.email" maxlength="64" show-word-limit placeholder="" size="small" type="text"></el-input>
</el-form-item>
<!--mobile-->
<el-form-item :label="$t('profile.mobile')" prop="mobile">
@@ -221,7 +221,7 @@ export default {
/* 密码失去焦点 检验确认密码 */
pinBlur () {
if (this.editUser.pin && this.editUser.pinChange) {
this.$refs.accountForm.validateField('pinChange')
this.$refs.userForm.validateField('pinChange')
}
},
getRoles () {

View File

@@ -14,7 +14,7 @@
<el-form ref="agentForm" :model="editPromServer" :rules="rules" label-position = "top" label-width="120px">
<!-- name-->
<el-form-item :label='$t("overall.name")' prop="name">
<el-input placeholder="" maxlength="64" show-word-limit v-model.trim="editPromServer.name" size="small" id="agent-box-input-name"></el-input>
<el-input placeholder="" maxlength="64" show-word-limit v-model="editPromServer.name" size="small" id="agent-box-input-name"></el-input>
</el-form-item>
<!--DC-->
<el-form-item :label="$t('overall.dc')" prop="dc.name">
@@ -119,6 +119,12 @@ export default {
],
type: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
token: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
protocol: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
editPromServer: {},

View File

@@ -15,7 +15,7 @@
<el-form ref="alertRuleForm" :model="editAlertRule" :rules="rules" label-position = "top" label-width="120px">
<!--name-->
<el-form-item :label='$t("overall.name")' prop="name">
<el-input id="alert-box-input-name" ref="alertName" v-model.trim="editAlertRule.name" maxlength="64" placeholder="" show-word-limit size="small"></el-input>
<el-input id="alert-box-input-name" ref="alertName" v-model="editAlertRule.name" maxlength="64" placeholder="" show-word-limit size="small"></el-input>
</el-form-item>
<!--type-->
<el-form-item :label="$t('overall.type')" prop="type" class="half-form-item">
@@ -295,7 +295,7 @@
<el-form-item :label="$t('dashboard.panel.receivers')" prop="receiver" :rules="[{ required: editAlertRule.notifyExpired || editAlertRule.notifyActive, message: this.$t('validate.required'), trigger: 'blur' }]">
<el-select
id="alert-box-input-receiver"
v-model.trim="editAlertRule.receiverShow"
v-model="editAlertRule.receiverShow"
class="right-box__select"
filterable
multiple

View File

@@ -16,7 +16,7 @@
:rules="rules" label-position="top" label-width="120px">
<el-form-item :label='$t("overall.name")' class="range-name" prop="name">
<el-input
v-model.trim="editAlertSilence.name" :placeholder="''"
v-model="editAlertSilence.name" :placeholder="''"
maxlength="64"
show-word-limit
size="small">
@@ -311,7 +311,7 @@ export default {
rangeTimeCallback () {
if (this.editAlertSilence.time) {
const num = new Date(this.editAlertSilence.endAt).getTime() - new Date(this.editAlertSilence.startAt).getTime()
this.rangeTime = num / (60 * 60 * 1000)// 以小时为单位
this.rangeTime = parseInt(num / (60 * 60 * 1000))// 以小时为单位
} else {
this.rangeTime = null
}

View File

@@ -10,7 +10,7 @@
<div class="container__form">
<el-form ref="apiKeyBoxForm" :model="apiKeyBox" :rules="rules" label-position="top" label-width="120px">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="64" show-word-limit v-model.trim="apiKeyBox.name" size="small" type="text"></el-input>
<el-input maxlength="64" show-word-limit v-model="apiKeyBox.name" size="small" type="text"></el-input>
</el-form-item>
<el-form-item
:label="$t('config.system.apiKey.expireAt')"

View File

@@ -32,19 +32,19 @@
</el-form-item>
<!-- 第三级Auth type = username | key -->
<el-form-item :label="$t('profile.username')" prop="authUsername">
<el-input v-model.trim="editData.authUsername" size="small"/>
<el-input v-model="editData.authUsername" size="small"/>
</el-form-item>
<template v-if="editData.authType === assetConstants.authTypeData.key">
<!-- Key支持私钥 -->
<el-form-item :label="$t('asset.privateKey')" prop="authPriKey">
<el-input v-model.trim="editData.authPriKey" size="small" type="password" autocomplete="new-password"/>
<el-input v-model="editData.authPriKey" size="small" type="password" autocomplete="new-password"/>
</el-form-item>
</template>
<el-form-item :label="$t('login.pin')" prop="authPin">
<el-input v-model.trim="editData.authPin" size="small" type="password" autocomplete="new-password"/>
<el-input v-model="editData.authPin" size="small" type="password" autocomplete="new-password"/>
</el-form-item>
<el-form-item :label="$t('asset.port')" prop="authProtocolPort">
<el-input v-model.trim="editData.authProtocolPort" size="small"/>
<el-input v-model="editData.authProtocolPort" size="small"/>
</el-form-item>
</template>
@@ -52,16 +52,16 @@
<template v-if="editData.authProtocol === assetConstants.authProtocolData.telnet">
<div class="form__sub-title">TELNET</div>
<el-form-item :label="$t('profile.username')" prop="authUsername">
<el-input v-model.trim="editData.authUsername" size="small"/>
<el-input v-model="editData.authUsername" size="small"/>
</el-form-item>
<el-form-item :label="$t('login.pin')" prop="authPin">
<el-input v-model.trim="editData.authPin" size="small"/>
<el-input v-model="editData.authPin" size="small"/>
</el-form-item>
<el-form-item :label="$t('asset.usernamePrompt')" prop="authUserTip">
<el-input v-model.trim="editData.authUserTip" size="small"/>
<el-input v-model="editData.authUserTip" size="small"/>
</el-form-item>
<el-form-item :label="$t('asset.pinPrompt')" prop="authPinTip">
<el-input v-model.trim="editData.authPinTip" size="small"/>
<el-input v-model="editData.authPinTip" size="small"/>
</el-form-item>
</template>
</template>
@@ -83,16 +83,16 @@
</template>
<template v-if="label.action !== 1">
<template v-if="label.type.toUpperCase() === assetConstants.labelTypeData.TEXT">
<el-input v-model.trim="label.value" size="small"/>
<el-input v-model="label.value" size="small"/>
</template>
<template v-else-if="label.type.toUpperCase() === assetConstants.labelTypeData.MULTITEXT">
<div v-for="(value, i) in label.value" :key="i" class="label__multi-text">
<el-input v-model.trim="label.value[i]" size="small" style="margin-right: 15px;"/>
<el-input v-model="label.value[i]" size="small" style="margin-right: 15px;"/>
<span :style="{'opacity': label.value.length > 1 ? 1 : 0.5}" @click="label.value.length > 1 ? label.value.splice(i, 1) : ''" :title="$t('overall.delete')"><i class="nz-icon nz-icon-minus"></i></span>
</div>
</template>
<template v-else-if="label.type.toUpperCase() === assetConstants.labelTypeData.TEXTAREA">
<el-input v-model.trim="label.value" :maxlength="4096" size="small" :rows="2" show-word-limit type="textarea"/>
<el-input v-model="label.value" :maxlength="4096" size="small" :rows="2" show-word-limit type="textarea"/>
</template>
<template v-else-if="label.type.toUpperCase() === assetConstants.labelTypeData.RADIO">
<el-radio v-for="item in JSON.parse(label.param).items" :key="item.name" v-model="label.value" :label="item.name">{{item.name}}</el-radio>
@@ -108,10 +108,10 @@
</el-select>
</template>
<template v-else-if="label.type.toUpperCase() === assetConstants.labelTypeData.INTEGER">
<el-input v-model.trim="label.value" oninput="value=value.replace(/[^\d]/g,'')" size="small"></el-input>
<el-input v-model="label.value" oninput="value=value.replace(/[^\d]/g,'')" size="small"></el-input>
</template>
<template v-else-if="label.type.toUpperCase() === assetConstants.labelTypeData.DOUBLE">
<el-input v-model.trim="label.value" oninput="value=value.replace(/[^0-9.]/g,'')" size="small"></el-input>
<el-input v-model="label.value" oninput="value=value.replace(/[^0-9.]/g,'')" size="small"></el-input>
</template>
<template v-else-if="label.type.toUpperCase() === assetConstants.labelTypeData.DATETIME">
<template v-if="JSON.parse(label.param).subType === assetConstants.labelSubTypeData.time">

View File

@@ -10,7 +10,7 @@
<div class="container__form">
<el-form ref="form" :model="editAsset" :rules="rules" label-position="top" label-width="120px">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="128" show-word-limit v-model.trim="editAsset.name" size="small"/>
<el-input maxlength="128" show-word-limit v-model="editAsset.name" size="small"/>
</el-form-item>
<el-form-item :label="$t('overall.type')" prop="typeId">
<select-asset-type id="asset-type" :asset-type-data="options.typeOptions" :show-type="editAsset.type" size="small"

View File

@@ -14,7 +14,7 @@
<div class="container__form">
<el-form label-width="120px" :model="editAssetMeta" label-position = "top" ref="editAssetMetaForm" :rules="rules">
<el-form-item :label='$t("overall.name")' prop="name">
<el-input placeholder="" maxlength="64" show-word-limit v-model.trim="editAssetMeta.name" size="small" id="editAssetMeta-box-input-name"></el-input>
<el-input placeholder="" maxlength="64" show-word-limit v-model="editAssetMeta.name" size="small" id="editAssetMeta-box-input-name"></el-input>
</el-form-item>
<el-form-item :label='$t("overall.key")' prop="metaKey">
<el-input placeholder="" maxlength="128" show-word-limit v-model="editAssetMeta.metaKey" size="small" id="editAssetMeta-box-input-key"></el-input>
@@ -70,7 +70,7 @@
<el-row v-for="(item,index) in editAssetMeta.param.items" :key="index" class="asset-meta-param-row">
<el-col :span="12">
<el-form-item :prop="'param.items.'+ index + '.name'" :rules="[{ required: true, message: $t('validate.required'), trigger: 'blur'},{ validator: checkItems, trigger: 'blur' }]">
<el-input v-model.trim = "item.name" size="small" @input="(val)=>{inputChange(index, val)}" :ref="'metaNameOption'+index"></el-input>
<el-input v-model = "item.name" size="small" @input="(val)=>{inputChange(index, val)}" :ref="'metaNameOption'+index"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">

View File

@@ -14,7 +14,7 @@
<div class="container__form">
<el-form label-width="120px" :model="editMetaGroup" label-position = "top" ref="editMetaGroupForm">
<el-form-item :label='$t("overall.name")' prop="name" :rules="{required: true, message: $t('validate.required'), trigger: 'blur'}">
<el-input placeholder="" maxlength="64" show-word-limit v-model.trim="editMetaGroup.name" size="small" id="editMetaGroup-box-input-name"></el-input>
<el-input placeholder="" maxlength="64" show-word-limit v-model="editMetaGroup.name" size="small" id="editMetaGroup-box-input-name"></el-input>
</el-form-item>
<el-form-item :label='$t("overall.remark")' prop="remark">
<el-input placeholder="" maxlength="256" type="textarea" show-word-limit v-model="editMetaGroup.remark" size="small" :rows="2" id="editMetaGroup-box-input-remark"></el-input>

View File

@@ -20,7 +20,7 @@
<el-form :model="editAssetState" :rules="rules" class="right-box-form right-box-form-left" label-position="top" label-width="120px" ref="assetStateForm">
<!--name-->
<el-form-item :label="$t('overall.name')" prop="name">
<el-input id="asset-state-input-name" placeholder="" size="small" type="text" v-model.trim="editAssetState.name"></el-input>
<el-input id="asset-state-input-name" placeholder="" size="small" type="text" v-model="editAssetState.name"></el-input>
</el-form-item>
<!--ping-->
<el-form-item label="Ping">

View File

@@ -14,7 +14,7 @@
<div class="right-box-form-box">
<el-form class="right-box-form right-box-form-left" label-width="120px" label-position = "top" :model="editCabinet" ref="cabinetForm" :rules="rules">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input size='mini' maxlength="64" show-word-limit v-model.trim="editCabinet.name" id="cabinet-box-input-name"/>
<el-input size='mini' maxlength="64" show-word-limit v-model="editCabinet.name" id="cabinet-box-input-name"/>
</el-form-item>
<el-form-item :label="$t('overall.dc')" prop="idcId">
<el-input v-if="dcDisabled" size='mini' v-model="currentDc.name" disabled id="cabinet-box-input-dc"/>

View File

@@ -76,7 +76,7 @@
</div>
<transition name="el-zoom-in-top">
<el-row v-show="expressionsShow[index-1].show">
<el-form-item :prop="'elements.' + (index -1) + '.expression'" :rules="{ required: true, message: $t('validate.required'), trigger: 'blur'}">
<el-form-item :prop="'elements.' + (index -1) + '.expression'" :rules="{ required: true, message: $t('validate.required'), trigger: 'change'}">
<promql-input
:from-father-data="true"
:metricOptionsParent="metricOptions"
@@ -480,7 +480,7 @@
<el-row v-if="chartConfig.param.enable.thresholds && isThresholdConfig(chartConfig.type)">
<el-form-item
v-for="(item,index) in chartConfig.param.thresholds"
:key="index"
:key="item.color + '' + index"
class="thresholds-item"
:prop="'param.thresholds.' + index + '.value'"
:rules="{ required: true, message: $t('validate.required'), trigger: 'blur'}"

View File

@@ -17,7 +17,7 @@
<el-form ref="chartForm" :model="editChart" :rules="rules" label-position= "top" label-width="120px">
<el-form-item :label='$t("overall.name")' prop="name">
<div style="display:flex;align-items:center">
<el-input style="flex:1;margin-right:10px" maxlength="64" show-word-limit size="small" v-model.trim="editChart.name" id="chart-box-title"></el-input>
<el-input style="flex:1;margin-right:10px" maxlength="64" show-word-limit size="small" v-model="editChart.name" id="chart-box-title"></el-input>
<!-- 显示头部 -->
<div class="choose-header-btn" v-if="editChart.param&&editChart.param.showHeader!==0" @click="switchHeader(0)" :title="$t('overall.visible')">
<i class="nz-icon nz-icon-mimakejian"></i>
@@ -33,7 +33,7 @@
<!--panel-->
<el-form-item
class="form-item--half-width"
v-if="showPanel.type !== fromRoute.project && showPanel.type !== fromRoute.asset && showPanel.type !== fromRoute.endpoint && showPanel.type !== fromRoute.model && from!=='chartTemp'"
v-if="showPanel.type !== fromRoute.project && showPanel.type !== fromRoute.asset && showPanel.type !== fromRoute.endpoint && showPanel.type !== fromRoute.model && from!=='chartTemp' && from!=='dashboardTemp'"
:label="$t('overall.dashboard')"
prop="panelName"
>
@@ -67,11 +67,11 @@
</div>
<!--remark-->
<el-form-item :label="$t('overall.remark')">
<el-input maxlength="256" show-word-limit v-model.trim="editChart.remark" size="small" :rows="2" type="textarea"/>
<el-input maxlength="256" show-word-limit v-model="editChart.remark" size="small" :rows="2" type="textarea"/>
</el-form-item>
<!--remark-->
<el-form-item :label="$t('overall.link')" v-if="editChart.param">
<el-input maxlength="256" show-word-limit v-model.trim="editChart.param.link" size="small" :rows="2" type="textarea"/>
<el-input maxlength="256" show-word-limit v-model="editChart.param.link" size="small" :rows="2" type="textarea"/>
</el-form-item>
<!--title-->
@@ -164,12 +164,6 @@ export default {
fromEndpoint: {
type: Boolean,
default: false
},
variables: {
type: Array,
default: () => {
return []
}
}
},
mixins: [rz, editRigthBox],
@@ -183,6 +177,9 @@ export default {
computed: {
chartLastPosition () {
return this.$store.getters.getChartLastPosition
},
variables () {
return this.$store.state.panel.variablesArr
}
},
data () {
@@ -580,6 +577,11 @@ export default {
result: ''
}
}
if (obj.type === 'group' && !obj.param.repeat) {
obj.param.repeat = {
variable: ''
}
}
// this.editChart.varType = 1
if (obj.param.enable.legend && !obj.param.legend) {
obj.param.legend = { placement: 'bottom', values: [], show: true }

View File

@@ -247,6 +247,46 @@
</div>
</transition>
</div>
<!-- repeat -->
<div v-if="params.type === 'group'">
<div class="form__sub-title">
<span>{{$t('dashboard.repeat')}}</span>
<el-switch
v-model="chartConfig.param.enable.repeat"
size="small"
@change="change"
></el-switch>
</div>
<transition name="el-zoom-in-top">
<div
v-if="chartConfig.param.enable.repeat"
class="form-items--half-width-group"
>
<!-- variable -->
<el-form-item
:label="$t('dashboard.panel.variableName')"
class="form-item--half-width"
prop="param.repeat.variable"
:rules="{ required: true, message: $t('validate.required'), trigger: 'blur'}"
>
<el-select
v-model="chartConfig.param.repeat.variable"
:placeholder="$t('el.select.placeholder')"
popper-class="right-box-select-top prevent-clickoutside"
size="small"
@change="change"
>
<el-option
v-for="item in variables"
:key="item.name"
:label="$t(item.name)"
:value="item.name"
></el-option>
</el-select>
</el-form-item>
</div>
</transition>
</div>
</el-form>
</div>
@@ -320,7 +360,8 @@ export default {
link: this.chartConfig.param.link,
collapse: true,
enable: {
visibility: false
visibility: false,
repeat: false
},
showHeader: this.chartConfig.param.showHeader,
visibility: {
@@ -328,6 +369,9 @@ export default {
operator: 'equal',
varValue: '',
result: 'show'
},
repeat: {
variable: ''
}
}
break

View File

@@ -396,7 +396,7 @@
<el-row v-if="chartConfig.param.enable.thresholds && isThresholdConfig(chartConfig.type)">
<el-form-item
v-for="(item,index) in chartConfig.param.thresholds"
:key="index"
:key="item.color + '' + index"
class="thresholds-item"
:prop="'param.thresholds.' + index + '.value'"
:rules="{ required: true, message: $t('validate.required'), trigger: 'blur'}"

View File

@@ -12,14 +12,14 @@
<!--type-->
<el-form-item v-if="from !== fromRoute.endpoint" :label="$t('dashboard.panel.chartForm.varType')" prop="varType">
<el-select id="chart-box-group"
v-model="editChartTemp.varType"
class="right-box__select"
clearable
placeholder=""
popper-class="prevent-clickoutside right-box-select-top"
size="small"
value-key="chartType"
@change="varTypeChange">
v-model="editChartTemp.varType"
class="right-box__select"
clearable
placeholder=""
popper-class="prevent-clickoutside right-box-select-top"
size="small"
value-key="chartType"
@change="varTypeChange">
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in varTypeArr">
<span class="panel-dropdown-label-txt" >{{item.name}}</span>
</el-option>
@@ -61,17 +61,18 @@
<!-- 选择资产穿梭框 -->
<template v-if="showTransfer">
<div class="form__sub-title">{{editChartTemp.varType === 1 ? $t('overall.asset') : $t('project.endpoint.endpoint')}}</div>
<nz-transfer ref="transfer"
:from="from"
:page-obj="transfer.pageObj"
:search-msg="transfer.searchMsg"
:table-data="transfer.tableData"
:tableTitle="transferTableTitle"
style="margin-bottom: 20px;"
@pageNo="pageNoChange"
@leftToRight="addAsset"
@rightToLeft="removeAsset"
@search="search">
<nz-transfer
ref="transfer"
:from="from"
:page-obj="transfer.pageObj"
:search-msg="transfer.searchMsg"
:table-data="transfer.tableData"
:tableTitle="transferTableTitle"
style="margin-bottom: 20px;"
@pageNo="pageNoChange"
@leftToRight="addAsset"
@rightToLeft="removeAsset"
@search="search">
<template v-slot:title>{{$t('overall.selected')}}</template>
</nz-transfer>
</template>

View File

@@ -0,0 +1,304 @@
<template>
<div v-clickoutside="{obj: editDashboard, func: esc}" class="right-box right-box-dashboard-temp">
<div class="right-box__header">
<div class="header__title">{{$t("dashboard.panel.createPanelTitle")}}</div>
<div class="header__operation">
<span v-cancel="{obj: editDashboard, func: esc}"><i class="nz-icon nz-icon-close" :title="$t('overall.close')"></i></span>
</div>
</div>
<div class="right-box__container">
<div class="container__form">
<el-form ref="form" :model="editDashboard" :rules="rules" label-position="top" label-width="120px">
<!-- name -->
<el-form-item :label='$t("overall.name")' prop="name">
<el-input v-model="editDashboard.name" maxlength="64" placeholder="" show-word-limit size="small"></el-input>
</el-form-item>
<!-- dashboardTemplate -->
<el-form-item :label="$t('upload.template')" prop="tmplId">
<v-selectpage
:data="templateList"
:tb-columns="ChartSearchShowFields"
:multiple="false"
:language="language"
key-field="id"
show-field="name"
:width="640"
v-model="editDashboard.tmplId"
class="form-control"
:result-format="resultFormat"
@values="tempChange"
></v-selectpage>
</el-form-item>
<!-- link -->
<el-form-item v-if="templateType" :label="templateType==1?$t('asset.asset'):$t('asset.endpoint')" prop="link" key="link">
<v-selectpage
:data="dataList"
:tb-columns="templateType==1?assetColumns:endpointColumns"
:multiple="false"
:language="language"
key-field="id"
show-field="name"
:width="640"
v-model="editDashboard.link"
class="form-control"
:result-format="resultFormat"
@values="linkChange"
></v-selectpage>
</el-form-item>
<!--description-->
<el-form-item :label='$t("overall.remark")' prop="description">
<el-input
v-model="editDashboard.description"
maxlength="256"
:rows="2"
show-word-limit
size="small"
type="textarea">
</el-input>
</el-form-item>
</el-form>
</div>
</div>
<div class="right-box__footer">
<button id="asset-edit-cancel" v-cancel="{obj: editDashboard, func: esc}" class="footer__btn footer__btn--light">
<span>{{$t('overall.cancel')}}</span>
</button>
<button id="asset-edit-save" :class="{'footer__btn--disabled': prevent_opt.save}" :disabled="prevent_opt.save" class="footer__btn" @click="save">
<span>{{$t('overall.save')}}</span>
</button>
</div>
</div>
</template>
<script>
import bus from '@/libs/bus'
import editRigthBox from '../mixin/editRigthBox'
export default {
props: {
},
mixins: [editRigthBox],
data () {
return {
url: '/visual/panel/fromTmpl',
rules: {
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
tmplId: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
link: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
ChartSearchShowFields: [ // ChartSearch 下拉搜索表头
{ title: 'ID', data: 'id' },
{
title: this.$t('overall.name'),
data: function (row) {
if (row.name.length > 15) {
return row.name.substring(0, 12) + '...'
}
return row.name
}
},
{
title: this.$t('dashboard.panel.chartForm.varType'),
data: function (row) {
let str
switch (row.varType) {
case 0 : str = 'None'; break
case 1 : str = 'Asset'; break
case 2 : str = 'Endpoint'; break
}
return str
}
},
{
title: this.$t('overall.remark'),
data: function (row) {
if (row.remark && row.remark.length > 15) {
return row.remark.substring(0, 12) + '...'
}
return row.remark
}
}
],
// asset表头
assetColumns: [
{ title: 'id', data: 'id' },
{
title: this.$t('overall.name'),
data: function (row) {
if (row.name.length > 15) {
return row.name.substring(0, 12) + '...'
}
return row.name
}
},
{ title: 'Manage Ip', data: 'manageIp' },
{
title: this.$t('overall.type'),
data: (row) => {
return row.type ? row.type.name : ''
}
},
{
title: this.$t('asset.model'),
data: (row) => {
return row.model ? row.model.name : ''
}
},
{
title: this.$t('overall.dc'),
data: (row) => {
return row.dc ? row.dc.name : ''
}
}
],
// endpoint表头
endpointColumns: [
{ title: 'ID', data: 'id' },
{
title: this.$t('overall.name'),
data: function (row) {
if (row.name.length > 15) {
return row.name.substring(0, 12) + '...'
}
return row.name
}
},
{
title: this.$t('overall.project'),
data: (row) => {
return row.project ? row.project.name : ''
}
},
{
title: this.$t('asset.asset'),
data: (row) => {
return row.asset ? row.asset.name : ''
}
},
{
title: this.$t('overall.module'),
data: (row) => {
return row.module ? row.module.name : ''
}
}
],
editDashboard: {
name: '',
type: 'dashboard',
tmplId: '',
link: '',
description: ''
},
templateList: [],
dataList: [],
templateType: undefined // 选中模板的varType
}
},
computed: {
language () { return this.$store.getters.getLanguage }
},
created () {
this.isEdit = true
this.getDashboardTempData()
},
methods: {
// 选中的模板变化
tempChange: bus.debounce(function (val) {
const newValue = this.$loadsh.get(val, ['0', 'varType'])
const oldValue = this.templateType
this.templateType = newValue
// 判断varType是否变化
if (oldValue != newValue) {
this.getTableData()
}
this.$refs.form.validateField('tmplId')
}, 50),
// 校验
linkChange: bus.debounce(function () {
this.$refs.form.validateField('link')
}, 50),
// 获取dashboard模板列表
getDashboardTempData () {
this.$get('visual/panel', { type: 'template', pageSize: -1 }).then(response => {
if (response.code === 200) {
this.templateList = response.data.list
}
})
},
// 根据选中模板vartype请求数据
getTableData () {
this.editDashboard.link = ''
this.dataList = []
if (this.templateType === 1) {
this.getAssetData()
} else if (this.templateType === 2) {
this.getEndpointData()
}
},
// 获取asset列表
getAssetData () {
this.$get('asset/asset', { pageSize: -1 }).then(response => {
if (response.code === 200) {
this.dataList = response.data.list
}
})
},
// 获取endpoint列表
getEndpointData () {
this.$get('monitor/endpoint', { pageSize: -1 }).then(response => {
if (response.code === 200) {
this.dataList = response.data.list
}
})
},
save () {
if (this.prevent_opt.save) {
return
}
this.prevent_opt.save = true
const params = this.$loadsh.cloneDeep(this.editDashboard)
this.$refs.form.validate((valid) => {
if (valid) {
// 模板varType ≠ 0时link有效且必填
if (this.templateType == 0) {
delete params.link
}
this.$post(this.url, params).then(response => {
this.prevent_opt.save = false
if (response.code === 200) {
this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(response.msg)
}
})
} else {
this.prevent_opt.save = false
return false
}
})
},
resultFormat (resp) {
if (resp && resp.data) {
const assetData = {}
assetData.list = resp.data.list
assetData.totalRow = resp.data.total
return assetData
}
},
/* 关闭弹框 */
esc (refresh) {
this.prevent_opt.save = false
this.$emit('close', refresh)
},
clickOutside () {
this.esc(false)
}
}
}
</script>

View File

@@ -12,7 +12,7 @@
<div class="container__form">
<el-form label-width="120px" size="small" :model="editDc" label-position = "top" :rules="rules" ref="dcForm">
<el-form-item :label='$t("overall.name")' prop="name">
<el-input placeholder="" maxlength="64" show-word-limit v-model.trim="editDc.name" size="small" id="dc-box-input-name"></el-input>
<el-input placeholder="" maxlength="64" show-word-limit v-model="editDc.name" size="small" id="dc-box-input-name"></el-input>
</el-form-item>
<el-form-item :label='$t("asset.location")' prop="location">
<el-input placeholder="" maxlength="256" show-word-limit v-model="editDc.location" size="small" id="dc-box-input-location"></el-input>

View File

@@ -67,7 +67,7 @@
</el-form-item>
<!--name-->
<el-form-item v-if="optionType!=='batch'" :label='$t("overall.name")' label-width="125px" prop="name">
<el-input id="module-box-input-name" v-model.trim="editEndpoint.name" maxlength="64" placeholder="" show-word-limit size="small"></el-input>
<el-input id="module-box-input-name" v-model="editEndpoint.name" maxlength="64" placeholder="" show-word-limit size="small"></el-input>
</el-form-item>
<!--Enpoint template-->
<div class="right-box-sub-title">{{ $t("overall.config") }}
@@ -627,7 +627,7 @@
<el-form-item
:label="'Format'"
:prop="'configs.1.config.'+ index +'.pipeline.' + index2 + '.format'"
:rules="[{ required: true, message: $t('validate.required'), trigger: 'blur' }]"
:rules="[{ required: true, message: $t('validate.required'), trigger: 'change' }]"
>
<!-- <el-input v-model="item2.format" placeholder="key" size="mini"></el-input> -->
<el-select v-model="item2.format" :id="'timestamp' + index +'-' + index2" class="right-box__select" placeholder="" popper-class="right-box-select-top prevent-clickoutside" size="small">

View File

@@ -14,7 +14,7 @@
<div class="container__form container__form-width">
<el-form ref="ExprTempForm" :model="editExprTemp" :rules="rules" label-position="top" label-width="120px">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="64" show-word-limit v-model.trim="editExprTemp.name" :placeholder="$t('overall.placeHolder')" size="small"></el-input>
<el-input maxlength="64" show-word-limit v-model="editExprTemp.name" :placeholder="$t('overall.placeHolder')" size="small"></el-input>
</el-form-item>
<el-form-item :label="$t('dashboard.panel.chartForm.group')" prop="gname">
<!--<el-input v-model="editExprTemp.gname" size="small" :placeholder="$t('overall.placeHolder')"></el-input>-->

View File

@@ -12,7 +12,7 @@
<div class="container__form">
<el-form :model="editipDetails" label-position="top" label-width="120px" :rules="rules" ref="ipDetailsForm">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="64" rows="4" show-word-limit placeholder="" v-model.trim="editipDetails.name" size="small"></el-input>
<el-input maxlength="64" rows="4" show-word-limit placeholder="" v-model="editipDetails.name" size="small"></el-input>
</el-form-item>
<el-form-item label="IP" prop="addr">
<el-input maxlength="256" rows="4" show-word-limit placeholder="" v-model="editipDetails.addr" size="small"></el-input>

View File

@@ -12,7 +12,7 @@
<div class="container__form">
<el-form :model="editipam" label-position = "top" label-width="120px" :rules="rules" ref="ipamForm">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input maxlength="64" rows="4" show-word-limit placeholder="" v-model.trim="editipam.name" size="small"></el-input>
<el-input maxlength="64" rows="4" show-word-limit placeholder="" v-model="editipam.name" size="small"></el-input>
</el-form-item>
<el-form-item :label="$t('overall.type')" prop="type">
<el-select id="account-input-language"
@@ -132,7 +132,7 @@ export default {
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
'dc.name': [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
dcData: []

View File

@@ -17,7 +17,7 @@
<!-- title -->
<el-form-item :label='$t("project.topology.title")' prop="name">
<el-input
v-model.trim="editIssue.name"
v-model="editIssue.name"
:placeholder="''"
maxlength="64"
show-word-limit
@@ -84,7 +84,7 @@
<el-form-item :label='$t("issue.reporter")' prop="rid">
<el-select
id="issue-box-input-receiver"
v-model.trim="editIssue.rid"
v-model="editIssue.rid"
class="right-box__select"
filterable
placeholder=""
@@ -106,7 +106,7 @@
<el-form-item :label='$t("issue.assignee")' prop="aid">
<el-select
id="issue-box-input-receiver"
v-model.trim="editIssue.aid"
v-model="editIssue.aid"
class="right-box__select"
filterable
placeholder=""

Some files were not shown because too many files have changed in this diff Show More