mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-14 09:00:50 +00:00
Merge pull request #12 from pemeraldy/feature/table-column-toggle
Feature/table column toggle
This commit is contained in:
commit
e7938e1245
150
package-lock.json
generated
150
package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"name": "skywalking-booster-ui",
|
||||
"version": "9.1.0",
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.0.4",
|
||||
"@types/jquery": "^3.5.14",
|
||||
"@types/sockjs-client": "^1.5.1",
|
||||
"@types/vertx3-eventbus-client": "^3.5.1",
|
||||
@ -1837,9 +1838,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz",
|
||||
"integrity": "sha512-S8kDbfVaWkQvbUYQE1ui448tzaHfUvyESCep9J6uPRlViyQPXjdIfwLBhV6AmQSOfFS8rL+xehJGhvzPXLrSBg==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.4.tgz",
|
||||
"integrity": "sha512-UeBVBU3fuBsYa9mzM7DgkRztQ1Aftw3sMTI/1gZsqXq2NWiCOi16ZYXXGIc0jFDIu+k6SojzdlxOjv+rN/Y6FQ==",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
@ -3431,8 +3432,16 @@
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.179",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
|
||||
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w=="
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
|
||||
"integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.2",
|
||||
@ -4743,6 +4752,13 @@
|
||||
"@vue/cli-service": "^3.0.0 || ^4.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/cli-plugin-unit-jest/node_modules/vue": {
|
||||
"version": "2.6.14",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
|
||||
"integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@vue/cli-plugin-unit-jest/node_modules/vue-jest": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.7.tgz",
|
||||
@ -8814,7 +8830,7 @@
|
||||
"node_modules/cssfilter": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||
"integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=",
|
||||
"integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cssnano": {
|
||||
@ -9708,6 +9724,13 @@
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
|
||||
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
|
||||
},
|
||||
"node_modules/de-indent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/deasync": {
|
||||
"version": "0.1.24",
|
||||
"resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
|
||||
@ -10513,6 +10536,14 @@
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@element-plus/icons-vue": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz",
|
||||
"integrity": "sha512-S8kDbfVaWkQvbUYQE1ui448tzaHfUvyESCep9J6uPRlViyQPXjdIfwLBhV6AmQSOfFS8rL+xehJGhvzPXLrSBg==",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/element-resize-detector": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz",
|
||||
@ -12055,10 +12086,6 @@
|
||||
"schema-utils": "2.7.0",
|
||||
"semver": "^7.3.2",
|
||||
"tapable": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"yarn": ">=1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fork-ts-checker-webpack-plugin-v5/node_modules/ansi-styles": {
|
||||
@ -12249,9 +12276,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fork-ts-checker-webpack-plugin-v5/node_modules/semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@ -17903,9 +17930,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
|
||||
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.4.tgz",
|
||||
"integrity": "sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@ -27227,9 +27254,6 @@
|
||||
"chalk": "^4.1.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"loader-utils": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^4.1.0 || ^5.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-loader-v16/node_modules/ansi-styles": {
|
||||
@ -27356,6 +27380,17 @@
|
||||
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vue-template-compiler": {
|
||||
"version": "2.6.14",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
|
||||
"integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-template-es2015-compiler": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||
@ -28906,9 +28941,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/xss": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz",
|
||||
"integrity": "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==",
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz",
|
||||
"integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^2.20.3",
|
||||
@ -30395,9 +30430,9 @@
|
||||
}
|
||||
},
|
||||
"@element-plus/icons-vue": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz",
|
||||
"integrity": "sha512-S8kDbfVaWkQvbUYQE1ui448tzaHfUvyESCep9J6uPRlViyQPXjdIfwLBhV6AmQSOfFS8rL+xehJGhvzPXLrSBg==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.4.tgz",
|
||||
"integrity": "sha512-UeBVBU3fuBsYa9mzM7DgkRztQ1Aftw3sMTI/1gZsqXq2NWiCOi16ZYXXGIc0jFDIu+k6SojzdlxOjv+rN/Y6FQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"@hapi/address": {
|
||||
@ -31754,8 +31789,16 @@
|
||||
"@types/lodash": {
|
||||
"version": "4.14.179",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
|
||||
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w=="
|
||||
},
|
||||
"@types/lodash-es": {
|
||||
"version": "4.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
|
||||
"integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "1.3.2",
|
||||
@ -32800,6 +32843,13 @@
|
||||
"vue-jest": "^3.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": {
|
||||
"version": "2.6.14",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
|
||||
"integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"vue-jest": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.7.tgz",
|
||||
@ -36060,7 +36110,7 @@
|
||||
"cssfilter": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
|
||||
"integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=",
|
||||
"integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
|
||||
"peer": true
|
||||
},
|
||||
"cssnano": {
|
||||
@ -36769,6 +36819,13 @@
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
|
||||
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
|
||||
},
|
||||
"de-indent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"deasync": {
|
||||
"version": "0.1.24",
|
||||
"resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
|
||||
@ -37411,6 +37468,14 @@
|
||||
"lodash-unified": "^1.0.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"normalize-wheel-es": "^1.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz",
|
||||
"integrity": "sha512-S8kDbfVaWkQvbUYQE1ui448tzaHfUvyESCep9J6uPRlViyQPXjdIfwLBhV6AmQSOfFS8rL+xehJGhvzPXLrSBg==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"element-resize-detector": {
|
||||
@ -38887,9 +38952,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@ -43173,9 +43238,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"memfs": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
|
||||
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.4.tgz",
|
||||
"integrity": "sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@ -50586,6 +50651,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-template-compiler": {
|
||||
"version": "2.6.14",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
|
||||
"integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"vue-template-es2015-compiler": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||
@ -51867,9 +51943,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"xss": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz",
|
||||
"integrity": "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==",
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz",
|
||||
"integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.3",
|
||||
|
@ -10,6 +10,7 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.0.4",
|
||||
"@types/jquery": "^3.5.14",
|
||||
"@types/sockjs-client": "^1.5.1",
|
||||
"@types/vertx3-eventbus-client": "^3.5.1",
|
||||
|
@ -29,3 +29,123 @@ export const TextConfig = {
|
||||
fontSize: 14,
|
||||
textAlign: "left",
|
||||
};
|
||||
|
||||
export const ServiceLogConstants = [
|
||||
{
|
||||
label: "serviceName",
|
||||
value: "service",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "serviceInstanceName",
|
||||
value: "instance",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "endpointName",
|
||||
value: "endpoint",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "timestamp",
|
||||
value: "time",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "contentType",
|
||||
value: "contentType",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "tags",
|
||||
value: "tags",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "content",
|
||||
value: "content",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "traceId",
|
||||
value: "traceID",
|
||||
isVisible: true
|
||||
},
|
||||
];
|
||||
export const ServiceLogDetail = [
|
||||
{
|
||||
label: "serviceName",
|
||||
value: "service",
|
||||
},
|
||||
{
|
||||
label: "serviceInstanceName",
|
||||
value: "instance",
|
||||
},
|
||||
{
|
||||
label: "timestamp",
|
||||
value: "time",
|
||||
},
|
||||
{
|
||||
label: "contentType",
|
||||
value: "contentType",
|
||||
},
|
||||
{
|
||||
label: "traceId",
|
||||
value: "traceID",
|
||||
},
|
||||
{
|
||||
label: "tags",
|
||||
value: "tags",
|
||||
},
|
||||
{
|
||||
label: "content",
|
||||
value: "content",
|
||||
},
|
||||
];
|
||||
// The order of columns should be time, service, error, stack, version, url, catalog, and grade.
|
||||
export const BrowserLogConstants = [
|
||||
{
|
||||
label: "service",
|
||||
value: "service",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
label: "serviceVersion",
|
||||
value: "serviceVersion",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
label: "errorUrl",
|
||||
value: "errorPage",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
label: "time",
|
||||
value: "time",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
label: "message",
|
||||
value: "message",
|
||||
isVisible: true,
|
||||
// drag: true,
|
||||
method: 350,
|
||||
},
|
||||
{
|
||||
label: "stack",
|
||||
value: "stack",
|
||||
isVisible: true,
|
||||
// drag: true,
|
||||
method: 350,
|
||||
},
|
||||
{
|
||||
label: "category",
|
||||
value: "category",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
label: "grade",
|
||||
value: "grade",
|
||||
isVisible: true,
|
||||
},
|
||||
];
|
@ -17,6 +17,8 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { Duration } from "@/types/app";
|
||||
import { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import { ServiceLogColumn, BrowserLogColumn } from "@/types/log-column";
|
||||
import { ServiceLogConstants, BrowserLogConstants } from "../data";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { AxiosResponse } from "axios";
|
||||
@ -28,6 +30,8 @@ interface LogState {
|
||||
services: Service[];
|
||||
instances: Instance[];
|
||||
endpoints: Endpoint[];
|
||||
serviceLogColumn: ServiceLogColumn[];
|
||||
browserLogColumn: BrowserLogColumn[];
|
||||
conditions: any;
|
||||
durationTime: Duration;
|
||||
selectorStore: any;
|
||||
@ -47,6 +51,8 @@ export const logStore = defineStore({
|
||||
queryDuration: useAppStoreWithOut().durationTime,
|
||||
paging: { pageNum: 1, pageSize: 15, needTotal: true },
|
||||
},
|
||||
serviceLogColumn: [...ServiceLogConstants],
|
||||
browserLogColumn: [...BrowserLogConstants],
|
||||
supportQueryLogsByKeywords: true,
|
||||
durationTime: useAppStoreWithOut().durationTime,
|
||||
selectorStore: useSelectorStore(),
|
||||
@ -55,6 +61,25 @@ export const logStore = defineStore({
|
||||
loadLogs: false,
|
||||
}),
|
||||
actions: {
|
||||
showColumns(columnsLabel: string) {
|
||||
this.serviceLogColumn.forEach((col: any) => {
|
||||
if (columnsLabel === col.value) {
|
||||
col.isVisible = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
hideColumns(columnsLabel: string) {
|
||||
this.serviceLogColumn.forEach((col: any) => {
|
||||
if (columnsLabel === col.value) {
|
||||
col.isVisible = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
toggleAllColumns(status: boolean) {
|
||||
this.serviceLogColumn.forEach((col: any) => {
|
||||
col.isVisible = status;
|
||||
});
|
||||
},
|
||||
setLogCondition(data: any) {
|
||||
this.conditions = { ...this.conditions, ...data };
|
||||
},
|
||||
|
@ -66,7 +66,7 @@
|
||||
}
|
||||
|
||||
.el-popper.is-light {
|
||||
background: var(--nice-black);
|
||||
background: var(--nice-black) !important;
|
||||
border: 1px solid var(--border-color) !important;
|
||||
}
|
||||
|
||||
@ -553,3 +553,12 @@ a {
|
||||
.tips {
|
||||
color: var(--spp-white) !important;
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
border-top-color: var(--spp-white);
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: var(--border-color);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
5
src/types/components.d.ts
vendored
5
src/types/components.d.ts
vendored
@ -1,15 +1,16 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/vue-next/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
DateCalendar: typeof import('./../components/DateCalendar.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
@ -46,4 +47,4 @@ declare module '@vue/runtime-core' {
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
export { }
|
||||
|
26
src/types/log-column.d.ts
vendored
Normal file
26
src/types/log-column.d.ts
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export interface BrowserLogColumn {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
export interface ServiceLogColumn {
|
||||
label: string;
|
||||
value: string;
|
||||
isVisible?: boolean;
|
||||
methode?: any;
|
||||
}
|
@ -15,22 +15,24 @@ limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<div class="log">
|
||||
<div class="log-header">
|
||||
<div :class="{ 'd-flex': visibleColumns.length < 6 }" class="log-header">
|
||||
<template v-for="(item, index) in columns">
|
||||
<div
|
||||
class="method"
|
||||
:style="`width: ${item.method}px`"
|
||||
v-if="item.drag"
|
||||
:key="index"
|
||||
>
|
||||
<span class="r cp" ref="dragger" :data-index="index">
|
||||
<Icon iconName="settings_ethernet" size="sm" />
|
||||
</span>
|
||||
{{ t(item.value) }}
|
||||
</div>
|
||||
<div v-else :class="item.label" :key="`col${index}`">
|
||||
{{ t(item.value) }}
|
||||
</div>
|
||||
<template v-if="item.isVisible">
|
||||
<div
|
||||
class="method"
|
||||
:style="`width: ${item.method}px`"
|
||||
v-if="item.drag"
|
||||
:key="index"
|
||||
>
|
||||
<span class="r cp" ref="dragger" :data-index="index">
|
||||
<Icon iconName="settings_ethernet" size="sm" />
|
||||
</span>
|
||||
{{ t(item.value) }}
|
||||
</div>
|
||||
<div v-else :class="item.label" :key="`col${index}`">
|
||||
{{ t(item.value) }}
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="type === 'browser'">
|
||||
@ -63,12 +65,12 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ServiceLogConstants, BrowserLogConstants } from "./data";
|
||||
import LogBrowser from "./LogBrowser.vue";
|
||||
import LogService from "./LogService.vue";
|
||||
import LogDetail from "./LogDetail.vue";
|
||||
import { logStore } from "@/store/modules/log";
|
||||
|
||||
/*global defineProps, Nullable */
|
||||
const props = defineProps({
|
||||
@ -76,14 +78,22 @@ const props = defineProps({
|
||||
tableData: { type: Array, default: () => [] },
|
||||
noLink: { type: Boolean, default: true },
|
||||
});
|
||||
const useLogStore = logStore();
|
||||
const { t } = useI18n();
|
||||
const currentLog = ref<any>({});
|
||||
const showDetail = ref<boolean>(false);
|
||||
const dragger = ref<Nullable<HTMLSpanElement>>(null);
|
||||
// const method = ref<number>(380);
|
||||
const columns: any[] =
|
||||
props.type === "browser" ? BrowserLogConstants : ServiceLogConstants;
|
||||
|
||||
const columns = ref<any[]>(
|
||||
props.type === "browser"
|
||||
? useLogStore.browserLogColumn
|
||||
: useLogStore.serviceLogColumn
|
||||
);
|
||||
|
||||
const visibleColumns = computed(() =>
|
||||
columns.value.filter((column) => column.isVisible)
|
||||
);
|
||||
function setCurrentLog(log: any) {
|
||||
showDetail.value = true;
|
||||
currentLog.value = log;
|
||||
@ -134,4 +144,11 @@ function setCurrentLog(log: any) {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.d-flex{
|
||||
display: flex;
|
||||
div{
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -14,8 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
|
||||
<template>
|
||||
<div @click="showSelectSpan" class="log-item">
|
||||
<div v-for="(item, index) in columns" :key="index" :class="item.label">
|
||||
<div
|
||||
@click="showSelectSpan"
|
||||
:class="{ 'd-flex': visibleColumns.length < 6 }"
|
||||
class="log-item"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in visibleColumns"
|
||||
:key="index"
|
||||
:class="item.label"
|
||||
>
|
||||
<span v-if="item.label === 'timestamp'">
|
||||
{{ dateFormat(data.timestamp) }}
|
||||
</span>
|
||||
@ -33,16 +41,20 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { ServiceLogConstants } from "./data";
|
||||
import { logStore } from "@/store/modules/log";
|
||||
/*global defineProps, defineEmits */
|
||||
const props = defineProps({
|
||||
data: { type: Object as any, default: () => ({}) },
|
||||
noLink: { type: Boolean, default: true },
|
||||
});
|
||||
const useLogStore = logStore();
|
||||
const columns = ref<any[]>(useLogStore.serviceLogColumn);
|
||||
const emit = defineEmits(["select"]);
|
||||
const columns = ServiceLogConstants;
|
||||
const visibleColumns = computed(() =>
|
||||
columns.value.filter((column) => column.isVisible)
|
||||
);
|
||||
const tags = computed(() => {
|
||||
if (!props.data.tags) {
|
||||
return "";
|
||||
@ -55,6 +67,7 @@ function showSelectSpan() {
|
||||
emit("select", props.data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.log-item {
|
||||
white-space: nowrap;
|
||||
@ -117,4 +130,11 @@ function showSelectSpan() {
|
||||
height: 100%;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
|
||||
.d-flex{
|
||||
display: flex;
|
||||
div{
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -19,34 +19,42 @@ export const ServiceLogConstants = [
|
||||
{
|
||||
label: "serviceName",
|
||||
value: "service",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "serviceInstanceName",
|
||||
value: "instance",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "endpointName",
|
||||
value: "endpoint",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "timestamp",
|
||||
value: "time",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "contentType",
|
||||
value: "contentType",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "tags",
|
||||
value: "tags",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "content",
|
||||
value: "content",
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
label: "traceId",
|
||||
value: "traceID",
|
||||
isVisible: true
|
||||
},
|
||||
];
|
||||
export const ServiceLogDetail = [
|
||||
@ -84,37 +92,45 @@ export const BrowserLogConstants = [
|
||||
{
|
||||
label: "service",
|
||||
value: "service",
|
||||
isVisble: true,
|
||||
},
|
||||
{
|
||||
label: "serviceVersion",
|
||||
value: "serviceVersion",
|
||||
isVisble: true,
|
||||
},
|
||||
{
|
||||
label: "errorUrl",
|
||||
value: "errorPage",
|
||||
isVisble: true,
|
||||
},
|
||||
{
|
||||
label: "time",
|
||||
value: "time",
|
||||
isVisble: true,
|
||||
},
|
||||
{
|
||||
label: "message",
|
||||
value: "message",
|
||||
isVisble: true,
|
||||
// drag: true,
|
||||
method: 350,
|
||||
},
|
||||
{
|
||||
label: "stack",
|
||||
value: "stack",
|
||||
isVisble: true,
|
||||
// drag: true,
|
||||
method: 350,
|
||||
},
|
||||
{
|
||||
label: "category",
|
||||
value: "category",
|
||||
isVisble: true,
|
||||
},
|
||||
{
|
||||
label: "grade",
|
||||
value: "grade",
|
||||
isVisble: true,
|
||||
},
|
||||
];
|
||||
|
@ -14,6 +14,52 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="flex-h log-wrapper">
|
||||
<div v-if="currentSearchTerm === 'column'" class="flex-h items-center mr-5">
|
||||
<el-dropdown class="dark" :hide-on-click="false">
|
||||
<span class="cursor-pointer">
|
||||
Select visible columns<el-icon class="el-icon--right"
|
||||
><arrow-down
|
||||
/></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu id="toggleColumn" class="dropdownSelector">
|
||||
<el-dropdown-item style="padding: 0">
|
||||
<div
|
||||
style="width: 100%; padding: 5px 16px"
|
||||
class="flex-h items-center"
|
||||
@click="logStore.toggleAllColumns(true)"
|
||||
>
|
||||
<el-icon><View /></el-icon>
|
||||
<span style="margin-right: 10px">Show All</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item style="padding: 0">
|
||||
<div
|
||||
style="width: 100%; padding: 5px 16px"
|
||||
class="flex-h items-center"
|
||||
@click="logStore.toggleAllColumns(false)"
|
||||
>
|
||||
<el-icon><Hide /></el-icon>
|
||||
<span style="margin-right: 10px">Hide All</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-divider />
|
||||
<el-dropdown-item
|
||||
style="padding: 0"
|
||||
v-for="item in logStore.serviceLogColumn"
|
||||
:key="item.value"
|
||||
>
|
||||
<el-checkbox class="custom-checkbox" v-model="item.isVisible">
|
||||
<span>{{ item.value }}</span>
|
||||
</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button class="toggle-btn mx-3danger" @click="setSearchTerm('')">
|
||||
<Icon iconSize="sm" iconName="cancel" />
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="!currentSearchTerm.length" class="flex-h items-center">
|
||||
<div v-for="(item, index) in arrayOfFilters" :key="index">
|
||||
<el-tooltip
|
||||
@ -33,7 +79,23 @@ limitations under the License. -->
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="Toggle columns"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
type="success"
|
||||
:class="[false ? 'active-toggle' : '']"
|
||||
class="toggle-btn mx-3"
|
||||
@click="toggleColumSelector"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="epic" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="flex-h items-center">
|
||||
<div class="flex-h items-center" v-if="currentSearchTerm === 'service'">
|
||||
<div
|
||||
@ -176,7 +238,10 @@ limitations under the License. -->
|
||||
</div>
|
||||
|
||||
<!-- Search&cancel buttons -->
|
||||
<div v-if="currentSearchTerm.length" class="flex-h items-center">
|
||||
<div
|
||||
v-if="currentSearchTerm.length && currentSearchTerm !== 'column'"
|
||||
class="flex-h items-center"
|
||||
>
|
||||
<el-button
|
||||
class="search-btn toggle-btn"
|
||||
size="small"
|
||||
@ -198,7 +263,9 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, computed } from "vue";
|
||||
import { ArrowDown, View, Hide } from "@element-plus/icons-vue";
|
||||
import { ref, reactive, watch, computed, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Option } from "@/types/app";
|
||||
import { useLogStore } from "@/store/modules/log";
|
||||
@ -213,16 +280,18 @@ const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const { portal } = useRoute().query;
|
||||
const logStore = useLogStore();
|
||||
const showColumList = ref<boolean>(false);
|
||||
const traceId = ref<string>("");
|
||||
const keywordsOfContent = ref<string[]>([]);
|
||||
const excludingKeywordsOfContent = ref<string[]>([]);
|
||||
const supportQueryLogsByKeywords = computed<boolean>(() => {
|
||||
return logStore.supportQueryLogsByKeywords
|
||||
})
|
||||
return logStore.supportQueryLogsByKeywords;
|
||||
});
|
||||
const supportExcludeQueryLogsByKeywords = computed<boolean>(() => {
|
||||
return logStore.supportQueryLogsByKeywords
|
||||
})
|
||||
return logStore.supportQueryLogsByKeywords;
|
||||
});
|
||||
|
||||
const currentSearchTerm = ref<string>("");
|
||||
const activeTerms = ref<string[]>([]);
|
||||
@ -287,7 +356,24 @@ const arrayOfFilters = ref<filtersObject[]>([
|
||||
isVisible: dashboardStore.entity !== EntityType[2].value,
|
||||
},
|
||||
]);
|
||||
onMounted(() => {
|
||||
if (portal) {
|
||||
["endpoint", "time", "contentType", "tags", "traceID"].forEach((col) =>
|
||||
logStore.hideColumns(col)
|
||||
);
|
||||
}
|
||||
});
|
||||
init();
|
||||
function toggleColumSelector() {
|
||||
showColumList.value = !showColumList.value;
|
||||
setSearchTerm("column");
|
||||
}
|
||||
|
||||
function hideTags() {
|
||||
let tagsWrap = document.querySelector(".el-select__tags");
|
||||
if (!tagsWrap) return;
|
||||
tagsWrap.style.display = "none";
|
||||
}
|
||||
async function init() {
|
||||
const resp = await logStore.getLogsByKeywords();
|
||||
|
||||
@ -471,6 +557,11 @@ function removeExcludeContent(index: number) {
|
||||
}
|
||||
function setSearchTerm(term: string) {
|
||||
currentSearchTerm.value = term;
|
||||
if (term === "column") {
|
||||
setTimeout(() => {
|
||||
hideTags();
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
function cancelSearchTerm() {
|
||||
switch (currentSearchTerm.value) {
|
||||
@ -501,7 +592,7 @@ function cancelSearchTerm() {
|
||||
}
|
||||
removeFromActiveTerms();
|
||||
currentSearchTerm.value = "";
|
||||
searchLogs()
|
||||
searchLogs();
|
||||
}
|
||||
watch(
|
||||
() => selectorStore.currentService,
|
||||
@ -530,16 +621,42 @@ watch(
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// .log-wrapper {
|
||||
// width: 600px;
|
||||
// padding-left: 40px;
|
||||
// overflow-x: scroll;
|
||||
// align-items: center;
|
||||
// }
|
||||
#toggleColumn.el-dropdown-menu {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.el-checkbox.custom-checkbox {
|
||||
width: 100%;
|
||||
padding: 5px 16px;
|
||||
}
|
||||
.dropdownSelector {
|
||||
background: var(--nice-black);
|
||||
}
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.el-divider--horizontal {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.custom-checkbox .el-checkbox__input.is-checked + .el-checkbox__label,
|
||||
.custom-checkbox .el-checkbox__label {
|
||||
color: var(--spp-white) !important;
|
||||
}
|
||||
.inputs {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.row {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@ -612,4 +729,22 @@ watch(
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.space-between {
|
||||
justify-content: space-between !important;
|
||||
}
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
background: transparent;
|
||||
}
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
|
||||
width: 100%;
|
||||
padding: 0 32px 0 20px;
|
||||
}
|
||||
.el-select-dropdown__item.selected {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0 32px 0 20px;
|
||||
}
|
||||
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user