Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f98619a5b | ||
![]() |
aab44626da | ||
![]() |
0ff5d4d6bb | ||
![]() |
611731d6d0 | ||
![]() |
221751f034 | ||
![]() |
d4dde7e73b | ||
![]() |
5be106fc4f | ||
![]() |
d8f91bbdf3 | ||
![]() |
23e9742946 | ||
![]() |
7a1c83b5fb | ||
![]() |
7d802d490e | ||
![]() |
da1db8def6 | ||
![]() |
2230d05508 | ||
![]() |
e8d909792d | ||
![]() |
dc842609ba | ||
![]() |
670bef1d69 | ||
![]() |
882828b04a | ||
![]() |
ed6fb0448b | ||
![]() |
a0fc879eb1 | ||
![]() |
b37d65eaac | ||
![]() |
fd46211a37 | ||
![]() |
ae0b8c056d | ||
![]() |
4b88d8bbb3 | ||
![]() |
09051e916b | ||
![]() |
e597f91448 | ||
![]() |
4232161d36 | ||
![]() |
eda44db0cd | ||
![]() |
78f0096c00 | ||
![]() |
5e161f17c2 | ||
![]() |
77d189cdfb | ||
![]() |
9f57e35119 | ||
![]() |
2bf90d6a6d | ||
![]() |
0f4319499a | ||
![]() |
5bb58a00cd | ||
![]() |
d50e9fc261 | ||
![]() |
b235929c77 | ||
![]() |
4561e2e374 | ||
![]() |
214b34ddfd | ||
![]() |
26817e9f92 | ||
![]() |
9ed0121fd0 | ||
![]() |
0d63d538c3 | ||
![]() |
5da441ff9a | ||
![]() |
49bc349064 | ||
![]() |
61a4d2f759 | ||
![]() |
0b4e738699 |
4
.github/workflows/nodejs.yml
vendored
@@ -44,9 +44,9 @@ jobs:
|
|||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- name: npm install, lint, build, and test
|
- name: npm ci, lint, build, and test
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm ci
|
||||||
npm run lint
|
npm run lint
|
||||||
npm run build --if-present
|
npm run build --if-present
|
||||||
npm run test:unit
|
npm run test:unit
|
||||||
|
221
package-lock.json
generated
@@ -510,9 +510,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.16.8",
|
"version": "7.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz",
|
||||||
"integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==",
|
"integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
},
|
},
|
||||||
@@ -3433,8 +3433,16 @@
|
|||||||
"node_modules/@types/lodash": {
|
"node_modules/@types/lodash": {
|
||||||
"version": "4.14.179",
|
"version": "4.14.179",
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
|
||||||
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
|
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w=="
|
||||||
"dev": true
|
},
|
||||||
|
"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": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
@@ -4948,6 +4956,46 @@
|
|||||||
"@vue/cli-service": "^3.0.0 || ^4.0.0-0"
|
"@vue/cli-service": "^3.0.0 || ^4.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/cli-plugin-unit-jest/node_modules/@vue/compiler-sfc": {
|
||||||
|
"version": "2.7.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz",
|
||||||
|
"integrity": "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.18.4",
|
||||||
|
"postcss": "^8.4.14",
|
||||||
|
"source-map": "^0.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/cli-plugin-unit-jest/node_modules/@vue/compiler-sfc/node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/cli-plugin-unit-jest/node_modules/csstype": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@vue/cli-plugin-unit-jest/node_modules/vue": {
|
||||||
|
"version": "2.7.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.10.tgz",
|
||||||
|
"integrity": "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-sfc": "2.7.10",
|
||||||
|
"csstype": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vue/cli-plugin-unit-jest/node_modules/vue-jest": {
|
"node_modules/@vue/cli-plugin-unit-jest/node_modules/vue-jest": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.7.tgz",
|
||||||
@@ -9586,9 +9634,9 @@
|
|||||||
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
|
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
|
||||||
},
|
},
|
||||||
"node_modules/d3-color": {
|
"node_modules/d3-color": {
|
||||||
"version": "3.0.1",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||||
"integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==",
|
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -9967,6 +10015,13 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
|
||||||
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
|
"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": {
|
"node_modules/deasync": {
|
||||||
"version": "0.1.24",
|
"version": "0.1.24",
|
||||||
"resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
|
"resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
|
||||||
@@ -18831,9 +18886,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.1.32",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.32.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||||
"integrity": "sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw==",
|
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.cjs"
|
||||||
},
|
},
|
||||||
@@ -20451,20 +20506,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.5",
|
"version": "8.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
|
||||||
"integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==",
|
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
}
|
||||||
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.1.30",
|
"nanoid": "^3.3.4",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"source-map-js": "^1.0.1"
|
"source-map-js": "^1.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-calc": {
|
"node_modules/postcss-calc": {
|
||||||
@@ -24413,9 +24474,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
"integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==",
|
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -27845,6 +27906,17 @@
|
|||||||
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
|
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-template-compiler": {
|
||||||
|
"version": "2.7.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz",
|
||||||
|
"integrity": "sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"he": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-template-es2015-compiler": {
|
"node_modules/vue-template-es2015-compiler": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||||
@@ -29975,9 +30047,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/parser": {
|
"@babel/parser": {
|
||||||
"version": "7.16.8",
|
"version": "7.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz",
|
||||||
"integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw=="
|
"integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw=="
|
||||||
},
|
},
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
|
||||||
"version": "7.16.7",
|
"version": "7.16.7",
|
||||||
@@ -32249,8 +32321,16 @@
|
|||||||
"@types/lodash": {
|
"@types/lodash": {
|
||||||
"version": "4.14.179",
|
"version": "4.14.179",
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
|
||||||
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
|
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w=="
|
||||||
"dev": true
|
},
|
||||||
|
"@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": {
|
"@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
@@ -33439,6 +33519,45 @@
|
|||||||
"vue-jest": "^3.0.5"
|
"vue-jest": "^3.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/compiler-sfc": {
|
||||||
|
"version": "2.7.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz",
|
||||||
|
"integrity": "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/parser": "^7.18.4",
|
||||||
|
"postcss": "^8.4.14",
|
||||||
|
"source-map": "^0.6.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"csstype": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"vue": {
|
||||||
|
"version": "2.7.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.10.tgz",
|
||||||
|
"integrity": "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"@vue/compiler-sfc": "2.7.10",
|
||||||
|
"csstype": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vue-jest": {
|
"vue-jest": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-3.0.7.tgz",
|
||||||
@@ -37173,9 +37292,9 @@
|
|||||||
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
|
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
|
||||||
},
|
},
|
||||||
"d3-color": {
|
"d3-color": {
|
||||||
"version": "3.0.1",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||||
"integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw=="
|
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
|
||||||
},
|
},
|
||||||
"d3-contour": {
|
"d3-contour": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
@@ -37456,6 +37575,13 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
|
||||||
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
|
"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": {
|
"deasync": {
|
||||||
"version": "0.1.24",
|
"version": "0.1.24",
|
||||||
"resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
|
"resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
|
||||||
@@ -44424,9 +44550,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "3.1.32",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.32.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||||
"integrity": "sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw=="
|
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
|
||||||
},
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
"version": "1.2.13",
|
"version": "1.2.13",
|
||||||
@@ -45679,13 +45805,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"version": "8.4.5",
|
"version": "8.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
|
||||||
"integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==",
|
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"nanoid": "^3.1.30",
|
"nanoid": "^3.3.4",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"source-map-js": "^1.0.1"
|
"source-map-js": "^1.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picocolors": {
|
"picocolors": {
|
||||||
@@ -48795,9 +48921,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"source-map-js": {
|
"source-map-js": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
"integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA=="
|
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
||||||
},
|
},
|
||||||
"source-map-resolve": {
|
"source-map-resolve": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
@@ -51480,6 +51606,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"vue-template-compiler": {
|
||||||
|
"version": "2.7.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz",
|
||||||
|
"integrity": "sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==",
|
||||||
|
"dev": true,
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"he": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vue-template-es2015-compiler": {
|
"vue-template-es2015-compiler": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||||
|
@@ -19,7 +19,7 @@ limitations under the License. -->
|
|||||||
#app {
|
#app {
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
min-width: 1024px;
|
min-width: 1024px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
15
src/assets/icons/circle.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<svg t="1667899293763" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4705" width="16" height="16"><path d="M512 512m-368 0a368 368 0 1 0 736 0 368 368 0 1 0-736 0Z" p-id="4706"></path></svg>
|
After Width: | Height: | Size: 1001 B |
16
src/assets/icons/conditions.svg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
|
||||||
|
<svg t="1666624449554" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2649" width="48" height="48"><path d="M381.482667 673.877333a90.389333 90.389333 0 0 1 85.226666 60.245334H853.333333v64H465.28a90.389333 90.389333 0 0 1-167.573333 0H170.666667v-64h125.610666a90.389333 90.389333 0 0 1 85.205334-60.245334z m0 64a26.346667 26.346667 0 1 0 0 52.693334 26.346667 26.346667 0 0 0 0-52.693334z m261.034666-304.938666a90.389333 90.389333 0 0 1 85.205334 60.245333H853.333333v64h-127.04a90.389333 90.389333 0 0 1-167.573333 0H170.666667v-64h386.624a90.389333 90.389333 0 0 1 85.226666-60.245333z m0 64a26.346667 26.346667 0 1 0 0 52.693333 26.346667 26.346667 0 0 0 0-52.693333zM381.482667 192a90.389333 90.389333 0 0 1 85.226666 60.224H853.333333v64H465.28a90.389333 90.389333 0 0 1-167.573333 0H170.666667v-64h125.610666A90.389333 90.389333 0 0 1 381.482667 192z m0 64a26.346667 26.346667 0 1 0 0 52.693333 26.346667 26.346667 0 0 0 0-52.693333z" p-id="2650"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
15
src/assets/icons/copy.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<svg t="1664265269855" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4109" width="48" height="48"><path d="M866.461538 39.384615H354.461538c-43.323077 0-78.769231 35.446154-78.76923 78.769231v39.384616h472.615384c43.323077 0 78.769231 35.446154 78.769231 78.76923v551.384616h39.384615c43.323077 0 78.769231-35.446154 78.769231-78.769231V118.153846c0-43.323077-35.446154-78.769231-78.769231-78.769231z m-118.153846 275.692308c0-43.323077-35.446154-78.769231-78.76923-78.769231H157.538462c-43.323077 0-78.769231 35.446154-78.769231 78.769231v590.769231c0 43.323077 35.446154 78.769231 78.769231 78.769231h512c43.323077 0 78.769231-35.446154 78.76923-78.769231V315.076923z m-354.461538 137.846154c0 11.815385-7.876923 19.692308-19.692308 19.692308h-157.538461c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h157.538461c11.815385 0 19.692308 7.876923 19.692308 19.692308v39.384615z m157.538461 315.076923c0 11.815385-7.876923 19.692308-19.692307 19.692308H216.615385c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h315.076923c11.815385 0 19.692308 7.876923 19.692307 19.692308v39.384615z m78.769231-157.538462c0 11.815385-7.876923 19.692308-19.692308 19.692308H216.615385c-11.815385 0-19.692308-7.876923-19.692308-19.692308v-39.384615c0-11.815385 7.876923-19.692308 19.692308-19.692308h393.846153c11.815385 0 19.692308 7.876923 19.692308 19.692308v39.384615z" p-id="4110"></path></svg>
|
After Width: | Height: | Size: 2.3 KiB |
15
src/assets/icons/gateway.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<svg t="1664244255409" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2266" width="48" height="48"><path d="M523.776 430.592l-153.088 88.576 153.088 88.576 152.576-88.576-152.576-88.576z m-165.888 108.544l0.512 177.152 153.6 87.552-1.024-176.64-153.088-88.064z m330.24 0l-153.6 87.552-1.024 176.64 153.6-87.552 1.024-176.64z m131.072 205.824l-68.096-40.96 39.936-8.704-5.632-26.112-67.072 14.848-13.824 23.04 101.376 60.928 13.312-23.04z m-142.848 7.68l68.096 40.96-39.936 8.704 5.632 26.112 67.072-14.848 13.824-23.04-101.888-60.928-12.8 23.04zM481.28 424.96h26.624V306.176H481.28v79.36l-27.648-29.696-19.456 18.432L481.28 424.96z m53.76-118.784V424.96h26.624V345.088l27.648 29.696 19.456-18.432-47.104-50.176h-26.624z m-190.464 401.92l-13.312-23.04-68.608 40.448 11.264-38.912-25.6-7.168-18.944 66.048 13.312 23.04 101.888-60.416z m-89.088 82.944l13.312 23.04 68.608-39.936-11.264 38.912 25.6 7.168 19.456-66.048-13.312-23.04-102.4 59.904z m622.08-45.056c-45.568 0-82.432 36.864-82.432 82.432 0 45.568 36.864 82.432 82.432 82.432 45.568 0 82.432-36.864 82.432-82.432 0-45.568-36.864-82.432-82.432-82.432z m0 122.88c-22.528 0-40.448-17.92-40.448-40.448s17.92-40.448 40.448-40.448 40.448 17.92 40.448 40.448-17.92 40.448-40.448 40.448zM521.728 292.864c45.568 0 82.432-36.864 82.432-82.432 0-45.568-36.864-82.432-82.432-82.432-45.568 0-82.432 36.864-82.432 82.432 0 45.568 36.864 82.432 82.432 82.432z m0-122.88c22.528 0 40.448 17.92 40.448 40.448s-17.92 40.448-40.448 40.448-40.448-17.92-40.448-40.448 18.432-40.448 40.448-40.448zM167.936 749.056c-45.568 0-82.432 36.864-82.432 82.432 0 45.568 36.864 82.432 82.432 82.432 45.568 0 82.432-36.864 82.432-82.432 0-45.568-36.864-82.432-82.432-82.432z m0 122.88c-22.528 0-40.448-17.92-40.448-40.448s17.92-40.448 40.448-40.448 40.448 17.92 40.448 40.448-18.432 40.448-40.448 40.448z" p-id="2267"></path></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@@ -13,6 +13,5 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
<title>info_outline</title>
|
|
||||||
<path d="M11.016 9v-2.016h1.969v2.016h-1.969zM12 20.016q3.281 0 5.648-2.367t2.367-5.648-2.367-5.648-5.648-2.367-5.648 2.367-2.367 5.648 2.367 5.648 5.648 2.367zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93zM11.016 17.016v-6h1.969v6h-1.969z"></path>
|
<path d="M11.016 9v-2.016h1.969v2.016h-1.969zM12 20.016q3.281 0 5.648-2.367t2.367-5.648-2.367-5.648-5.648-2.367-5.648 2.367-2.367 5.648 2.367 5.648 5.648 2.367zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93zM11.016 17.016v-6h1.969v6h-1.969z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
15
src/assets/icons/operation.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<svg t="1664266918236" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5378" width="48" height="48"><path d="M571.178667 643.328a144 144 0 0 1-189.098667-193.450667l77.781333 77.866667a48 48 0 1 0 67.882667-67.84l-77.824-77.909333a144 144 0 0 1 193.450667 189.141333l226.517333 207.061333a64.896 64.896 0 1 1-91.690667 91.690667l-207.018666-226.56z m51.498666 134.656a288.298667 288.298667 0 0 1-38.656 12.928v95.488c0 5.290667-4.309333 9.6-9.642666 9.6h-124.757334a9.6 9.6 0 0 1-9.6-9.6v-95.488a286.293333 286.293333 0 0 1-74.325333-30.805333l-67.541333 67.541333a9.6 9.6 0 0 1-13.568 0L196.352 739.413333a9.6 9.6 0 0 1 0-13.568l67.541333-67.541333a286.293333 286.293333 0 0 1-30.805333-74.325333H137.6A9.6 9.6 0 0 1 128 574.378667v-124.757334c0-5.290667 4.309333-9.6 9.6-9.6h95.488c6.826667-26.453333 17.28-51.370667 30.805333-74.325333L196.352 298.154667a9.6 9.6 0 0 1 0-13.568L284.586667 196.352a9.6 9.6 0 0 1 13.568 0l67.541333 67.498667a287.146667 287.146667 0 0 1 74.325333-30.848V137.6c0-5.290667 4.266667-9.6 9.6-9.6h124.8c5.248 0 9.6 4.309333 9.6 9.6v95.488c26.368 6.826667 51.328 17.28 74.282667 30.805333l67.541333-67.541333a9.6 9.6 0 0 1 13.568 0l88.234667 88.234667a9.6 9.6 0 0 1 0 13.568l-67.498667 67.541333a287.146667 287.146667 0 0 1 30.848 74.282667h95.402667c5.290667 0 9.6 4.352 9.6 9.642666v124.757334c0 5.333333-4.266667 9.6-9.6 9.6h-95.488c-4.693333 18.133333-11.178667 35.754667-19.328 52.650666a9.6 9.6 0 0 1-15.018667 2.986667l-10.112-9.173333-38.314666-34.261334-12.16-10.88a9.6 9.6 0 0 1-2.688-10.24A192.298667 192.298667 0 0 0 512 320a192 192 0 1 0 63.018667 373.333333 9.6 9.6 0 0 1 10.24 2.645334l10.837333 12.074666 35.285333 39.338667 8.149334 9.130667a9.6 9.6 0 0 1-2.901334 15.061333 283.306667 283.306667 0 0 1-13.952 6.4z" p-id="5379"></path></svg>
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/assets/img/technologies/EVENTMESH.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
src/assets/img/technologies/IMPALA.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
@@ -632,11 +632,6 @@ onMounted(() => {
|
|||||||
right: 24px;
|
right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-next-month-btn .middle,
|
|
||||||
.calendar-prev-month-btn .middle {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar-body {
|
.calendar-body {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 196px;
|
width: 196px;
|
||||||
|
@@ -15,6 +15,28 @@ limitations under the License. -->
|
|||||||
<template>
|
<template>
|
||||||
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
|
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
|
||||||
<div v-if="!available" class="no-data">No Data</div>
|
<div v-if="!available" class="no-data">No Data</div>
|
||||||
|
<div class="menus" v-show="visMenus" ref="menus">
|
||||||
|
<div class="tools" @click="associateMetrics" v-if="associate.length">
|
||||||
|
{{ t("associateMetrics") }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="tools"
|
||||||
|
@click="viewTrace"
|
||||||
|
v-if="relatedTrace && relatedTrace.enableRelate"
|
||||||
|
>
|
||||||
|
{{ t("viewTrace") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-drawer
|
||||||
|
v-model="showTrace"
|
||||||
|
size="100%"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="() => (showTrace = false)"
|
||||||
|
:append-to-body="true"
|
||||||
|
title="The Related Traces"
|
||||||
|
>
|
||||||
|
<Trace :data="traceOptions" />
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -28,15 +50,28 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { EventParams } from "@/types/app";
|
||||||
|
import { Filters, RelatedTrace } from "@/types/dashboard";
|
||||||
import { useECharts } from "@/hooks/useEcharts";
|
import { useECharts } from "@/hooks/useEcharts";
|
||||||
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
||||||
|
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||||
|
import associateProcessor from "@/hooks/useAssociateProcessor";
|
||||||
|
|
||||||
/*global Nullable, defineProps, defineEmits*/
|
/*global Nullable, defineProps, defineEmits*/
|
||||||
const emits = defineEmits(["select"]);
|
const emits = defineEmits(["select"]);
|
||||||
|
const { t } = useI18n();
|
||||||
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
||||||
|
const menus = ref<Nullable<HTMLDivElement>>(null);
|
||||||
|
const visMenus = ref<boolean>(false);
|
||||||
const { setOptions, resize, getInstance } = useECharts(
|
const { setOptions, resize, getInstance } = useECharts(
|
||||||
chartRef as Ref<HTMLDivElement>
|
chartRef as Ref<HTMLDivElement>
|
||||||
);
|
);
|
||||||
|
const currentParams = ref<Nullable<EventParams>>(null);
|
||||||
|
const showTrace = ref<boolean>(false);
|
||||||
|
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||||
|
type: "Trace",
|
||||||
|
});
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
height: { type: String, default: "100%" },
|
height: { type: String, default: "100%" },
|
||||||
width: { type: String, default: "100%" },
|
width: { type: String, default: "100%" },
|
||||||
@@ -45,15 +80,14 @@ const props = defineProps({
|
|||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
type: Object as PropType<{
|
type: Object as PropType<Filters>,
|
||||||
duration: {
|
},
|
||||||
startTime: string;
|
relatedTrace: {
|
||||||
endTime: string;
|
type: Object as PropType<RelatedTrace>,
|
||||||
};
|
},
|
||||||
isRange: boolean;
|
associate: {
|
||||||
dataIndex?: number;
|
type: Array as PropType<{ widgetId: string }[]>,
|
||||||
sourceId: string;
|
default: () => [],
|
||||||
}>,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const available = computed(
|
const available = computed(
|
||||||
@@ -66,14 +100,34 @@ const available = computed(
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await setOptions(props.option);
|
await setOptions(props.option);
|
||||||
chartRef.value && addResizeListener(unref(chartRef), resize);
|
chartRef.value && addResizeListener(unref(chartRef), resize);
|
||||||
|
instanceEvent();
|
||||||
|
});
|
||||||
|
|
||||||
|
function instanceEvent() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const instance = getInstance();
|
const instance = getInstance();
|
||||||
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance.on("click", (params: unknown) => {
|
instance.on("click", (params: EventParams) => {
|
||||||
emits("select", params);
|
currentParams.value = params;
|
||||||
|
if (!menus.value || !chartRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visMenus.value = true;
|
||||||
|
const w = chartRef.value.getBoundingClientRect().width || 0;
|
||||||
|
const h = chartRef.value.getBoundingClientRect().height || 0;
|
||||||
|
if (w - params.event.offsetX > 120) {
|
||||||
|
menus.value.style.left = params.event.offsetX + "px";
|
||||||
|
} else {
|
||||||
|
menus.value.style.left = params.event.offsetX - 120 + "px";
|
||||||
|
}
|
||||||
|
if (h - params.event.offsetY < 50) {
|
||||||
|
menus.value.style.top = params.event.offsetY - 40 + "px";
|
||||||
|
} else {
|
||||||
|
menus.value.style.top = params.event.offsetY + 2 + "px";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
@@ -81,9 +135,7 @@ onMounted(async () => {
|
|||||||
if (instance.isDisposed()) {
|
if (instance.isDisposed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance.dispatchAction({
|
visMenus.value = false;
|
||||||
type: "hideTip",
|
|
||||||
});
|
|
||||||
instance.dispatchAction({
|
instance.dispatchAction({
|
||||||
type: "updateAxisPointer",
|
type: "updateAxisPointer",
|
||||||
currTrigger: "leave",
|
currTrigger: "leave",
|
||||||
@@ -92,9 +144,18 @@ onMounted(async () => {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
}
|
||||||
|
|
||||||
function updateOptions() {
|
function associateMetrics() {
|
||||||
|
emits("select", currentParams.value);
|
||||||
|
const { dataIndex, seriesIndex } = currentParams.value || {
|
||||||
|
dataIndex: 0,
|
||||||
|
seriesIndex: 0,
|
||||||
|
};
|
||||||
|
updateOptions({ dataIndex, seriesIndex });
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOptions(params?: { dataIndex: number; seriesIndex: number }) {
|
||||||
const instance = getInstance();
|
const instance = getInstance();
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return;
|
return;
|
||||||
@@ -103,60 +164,32 @@ function updateOptions() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (props.filters.isRange) {
|
if (props.filters.isRange) {
|
||||||
|
const { eventAssociate } = associateProcessor(props);
|
||||||
const options = eventAssociate();
|
const options = eventAssociate();
|
||||||
setOptions(options || props.option);
|
setOptions(options || props.option);
|
||||||
} else {
|
} else {
|
||||||
instance.dispatchAction({
|
instance.dispatchAction({
|
||||||
type: "showTip",
|
type: "updateAxisPointer",
|
||||||
dataIndex: props.filters.dataIndex,
|
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||||
seriesIndex: 0,
|
seriesIndex: params ? params.seriesIndex : 0,
|
||||||
|
});
|
||||||
|
const ids = props.option.series.map((_: unknown, index: number) => index);
|
||||||
|
instance.dispatchAction({
|
||||||
|
type: "highlight",
|
||||||
|
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||||
|
seriesIndex: ids,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventAssociate() {
|
function viewTrace() {
|
||||||
if (!props.filters) {
|
const item = associateProcessor(props).traceFilters(currentParams.value);
|
||||||
return;
|
traceOptions.value = {
|
||||||
}
|
...traceOptions.value,
|
||||||
if (!props.filters.duration) {
|
filters: item,
|
||||||
return props.option;
|
|
||||||
}
|
|
||||||
if (!props.option.series[0]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const list = props.option.series[0].data.map(
|
|
||||||
(d: (number | string)[]) => d[0]
|
|
||||||
);
|
|
||||||
if (!list.includes(props.filters.duration.endTime)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const markArea = {
|
|
||||||
silent: true,
|
|
||||||
itemStyle: {
|
|
||||||
opacity: 0.3,
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
xAxis: props.filters.duration.startTime,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
xAxis: props.filters.duration.endTime,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
const series = (window as any).structuredClone(props.option.series);
|
showTrace.value = true;
|
||||||
for (const [key, temp] of series.entries()) {
|
visMenus.value = true;
|
||||||
if (key === 0) {
|
|
||||||
temp.markArea = markArea;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const options = {
|
|
||||||
...props.option,
|
|
||||||
series,
|
|
||||||
};
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -170,6 +203,7 @@ watch(
|
|||||||
}
|
}
|
||||||
let options;
|
let options;
|
||||||
if (props.filters && props.filters.isRange) {
|
if (props.filters && props.filters.isRange) {
|
||||||
|
const { eventAssociate } = associateProcessor(props);
|
||||||
options = eventAssociate();
|
options = eventAssociate();
|
||||||
}
|
}
|
||||||
setOptions(options || props.option);
|
setOptions(options || props.option);
|
||||||
@@ -200,5 +234,30 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
.chart {
|
.chart {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menus {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 9999999;
|
||||||
|
box-shadow: #ddd 1px 2px 10px;
|
||||||
|
transition: all cubic-bezier(0.075, 0.82, 0.165, 1) linear;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: rgb(51, 51, 51);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
padding: 5px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -24,8 +24,8 @@ import { ref } from "vue";
|
|||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
label: string;
|
label: string | number;
|
||||||
value: string;
|
value: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
|
@@ -18,7 +18,6 @@ limitations under the License. -->
|
|||||||
v-model="selected"
|
v-model="selected"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
@change="changeSelected"
|
@change="changeSelected"
|
||||||
filterable
|
|
||||||
:multiple="multiple"
|
:multiple="multiple"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:style="{ borderRadius }"
|
:style="{ borderRadius }"
|
||||||
@@ -26,6 +25,7 @@ limitations under the License. -->
|
|||||||
:remote="isRemote"
|
:remote="isRemote"
|
||||||
:reserve-keyword="isRemote"
|
:reserve-keyword="isRemote"
|
||||||
:remote-method="remoteMethod"
|
:remote-method="remoteMethod"
|
||||||
|
:filterable="filterable"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in options"
|
v-for="item in options"
|
||||||
@@ -66,6 +66,7 @@ const props = defineProps({
|
|||||||
disabled: { type: Boolean, default: false },
|
disabled: { type: Boolean, default: false },
|
||||||
clearable: { type: Boolean, default: false },
|
clearable: { type: Boolean, default: false },
|
||||||
isRemote: { type: Boolean, default: false },
|
isRemote: { type: Boolean, default: false },
|
||||||
|
filterable: { type: Boolean, default: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
const selected = ref<string[] | string>(props.value);
|
const selected = ref<string[] | string>(props.value);
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum TimeType {
|
export enum TimeType {
|
||||||
MINUTE_TIME = "MINUTE",
|
MINUTE_TIME = "MINUTE",
|
||||||
HOUR_TIME = "HOUR",
|
HOUR_TIME = "HOUR",
|
||||||
@@ -25,33 +26,3 @@ export const Languages = [
|
|||||||
{ label: "Chinese", value: "zh" },
|
{ label: "Chinese", value: "zh" },
|
||||||
{ label: "Spanish", value: "es" },
|
{ label: "Spanish", value: "es" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RoutesMap: { [key: string]: string } = {
|
|
||||||
GeneralServices: "GENERAL",
|
|
||||||
GeneralServicesActiveTabIndex: "GENERAL",
|
|
||||||
VirtualDatabase: "VIRTUAL_DATABASE",
|
|
||||||
VirtualDatabaseActiveTabIndex: "VIRTUAL_DATABASE",
|
|
||||||
MeshServices: "MESH",
|
|
||||||
MeshServicesActiveTabIndex: "MESH",
|
|
||||||
ControlPanel: "MESH_CP",
|
|
||||||
ControlPanelActiveTabIndex: "MESH_CP",
|
|
||||||
DataPanel: "MESH_DP",
|
|
||||||
DataPanelActiveTabIndex: "MESH_DP",
|
|
||||||
Linux: "OS_LINUX",
|
|
||||||
SkyWalkingServer: "SO11Y_OAP",
|
|
||||||
SkyWalkingServerActiveTabIndex: "SO11Y_OAP",
|
|
||||||
SatelliteActiveTabIndex: "SO11Y_SATELLITE",
|
|
||||||
Satellite: "SO11Y_SATELLITE",
|
|
||||||
Functions: "FAAS",
|
|
||||||
FunctionsActiveTabIndex: "FAAS",
|
|
||||||
Browser: "BROWSER",
|
|
||||||
BrowserActiveTabIndex: "BROWSER",
|
|
||||||
KubernetesCluster: "K8S",
|
|
||||||
KubernetesClusterActiveTabIndex: "K8S",
|
|
||||||
KubernetesService: "K8S_SERVICE",
|
|
||||||
KubernetesServiceActiveTabIndex: "K8S_SERVICE",
|
|
||||||
MySQL: "MYSQL",
|
|
||||||
MySQLActiveTabIndex: "MYSQL",
|
|
||||||
PostgreSQL: "POSTGRESQL",
|
|
||||||
PostgreSQLActiveTabIndex: "POSTGRESQL",
|
|
||||||
};
|
|
||||||
|
@@ -69,6 +69,25 @@ export const TraceSpans = {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
attachedEvents {
|
||||||
|
startTime {
|
||||||
|
seconds
|
||||||
|
nanos
|
||||||
|
}
|
||||||
|
event
|
||||||
|
endTime {
|
||||||
|
seconds
|
||||||
|
nanos
|
||||||
|
}
|
||||||
|
tags {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
summary {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@@ -32,6 +32,6 @@ export const queryInstances = `query queryInstances(${Instances.variable}) {${In
|
|||||||
export const queryLayers = `query listLayer {${Layers.query}}`;
|
export const queryLayers = `query listLayer {${Layers.query}}`;
|
||||||
export const queryService = `query queryService(${getService.variable}) {${getService.query}}`;
|
export const queryService = `query queryService(${getService.variable}) {${getService.query}}`;
|
||||||
export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`;
|
export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`;
|
||||||
export const queryEndpoint = `query queryInstance(${getEndpoint.variable}) {${getEndpoint.query}}`;
|
export const queryEndpoint = `query queryEndpoint(${getEndpoint.variable}) {${getEndpoint.query}}`;
|
||||||
export const queryProcesses = `query queryProcesses(${Processes.variable}) {${Processes.query}}`;
|
export const queryProcesses = `query queryProcesses(${Processes.variable}) {${Processes.query}}`;
|
||||||
export const queryProcess = `query queryProcess(${getProcess.variable}) {${getProcess.query}}`;
|
export const queryProcess = `query queryProcess(${getProcess.variable}) {${getProcess.query}}`;
|
||||||
|
@@ -21,6 +21,7 @@ export enum MetricQueryTypes {
|
|||||||
ReadLabeledMetricsValues = "readLabeledMetricsValues",
|
ReadLabeledMetricsValues = "readLabeledMetricsValues",
|
||||||
READHEATMAP = "readHeatMap",
|
READHEATMAP = "readHeatMap",
|
||||||
ReadSampledRecords = "readSampledRecords",
|
ReadSampledRecords = "readSampledRecords",
|
||||||
|
ReadRecords = "readRecords",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Calculations {
|
export enum Calculations {
|
||||||
@@ -101,4 +102,10 @@ export const RespFields: any = {
|
|||||||
value
|
value
|
||||||
refId
|
refId
|
||||||
}`,
|
}`,
|
||||||
|
readRecords: `{
|
||||||
|
id
|
||||||
|
name
|
||||||
|
value
|
||||||
|
refId
|
||||||
|
}`,
|
||||||
};
|
};
|
||||||
|
138
src/hooks/useAssociateProcessor.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import dateFormatStep from "@/utils/dateFormat";
|
||||||
|
import getLocalTime from "@/utils/localtime";
|
||||||
|
import { EventParams } from "@/types/app";
|
||||||
|
|
||||||
|
export default function associateProcessor(props: any) {
|
||||||
|
function eventAssociate() {
|
||||||
|
if (!props.filters) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!props.filters.duration) {
|
||||||
|
return props.option;
|
||||||
|
}
|
||||||
|
if (!props.option.series[0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const list = props.option.series[0].data.map(
|
||||||
|
(d: (number | string)[]) => d[0]
|
||||||
|
);
|
||||||
|
if (!list.includes(props.filters.duration.endTime)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const markArea = {
|
||||||
|
silent: true,
|
||||||
|
itemStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
xAxis: props.filters.duration.startTime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xAxis: props.filters.duration.endTime,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const series = (window as any).structuredClone(props.option.series);
|
||||||
|
for (const [key, temp] of series.entries()) {
|
||||||
|
if (key === 0) {
|
||||||
|
temp.markArea = markArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const options = {
|
||||||
|
...props.option,
|
||||||
|
series,
|
||||||
|
};
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
function traceFilters(currentParams: Nullable<EventParams>) {
|
||||||
|
const appStore = useAppStoreWithOut();
|
||||||
|
|
||||||
|
if (!currentParams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const start = appStore.intervalUnix[currentParams.dataIndex];
|
||||||
|
const { step } = appStore.durationRow;
|
||||||
|
let duration = undefined;
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
const end = start;
|
||||||
|
duration = {
|
||||||
|
start: dateFormatStep(
|
||||||
|
getLocalTime(appStore.utc, new Date(start)),
|
||||||
|
step,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
end: dateFormatStep(
|
||||||
|
getLocalTime(appStore.utc, new Date(end)),
|
||||||
|
step,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
step,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const relatedTrace = props.relatedTrace || {};
|
||||||
|
const status = relatedTrace.status;
|
||||||
|
const queryOrder = relatedTrace.queryOrder;
|
||||||
|
const latency = relatedTrace.latency;
|
||||||
|
const series = props.option.series || [];
|
||||||
|
const item: any = {
|
||||||
|
duration,
|
||||||
|
queryOrder,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
if (latency) {
|
||||||
|
const latencyList = series.map(
|
||||||
|
(d: { name: string; data: number[][] }, index: number) => {
|
||||||
|
const data = [
|
||||||
|
d.data[currentParams.dataIndex][1],
|
||||||
|
series[index + 1]
|
||||||
|
? series[index + 1].data[currentParams.dataIndex][1]
|
||||||
|
: Infinity,
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
label:
|
||||||
|
d.name +
|
||||||
|
"--" +
|
||||||
|
(series[index + 1] ? series[index + 1].name : "Infinity"),
|
||||||
|
value: String(index),
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
item.latency = latencyList;
|
||||||
|
}
|
||||||
|
const value = series.map(
|
||||||
|
(d: { name: string; data: number[][] }, index: number) => {
|
||||||
|
return {
|
||||||
|
label: d.name,
|
||||||
|
value: String(index),
|
||||||
|
data: d.data[currentParams.dataIndex][1],
|
||||||
|
date: d.data[currentParams.dataIndex][0],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
item.metricValue = value;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return { eventAssociate, traceFilters };
|
||||||
|
}
|
142
src/hooks/useLegendProcessor.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import { LegendOptions } from "@/types/dashboard";
|
||||||
|
import { isDef } from "@/utils/is";
|
||||||
|
|
||||||
|
export default function useLegendProcess(legend?: LegendOptions) {
|
||||||
|
let isRight = false;
|
||||||
|
if (legend && legend.toTheRight) {
|
||||||
|
isRight = true;
|
||||||
|
}
|
||||||
|
function showEchartsLegend(keys: string[]) {
|
||||||
|
if (legend && isDef(legend.show)) {
|
||||||
|
if (legend.asTable && legend.show) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return legend.show;
|
||||||
|
}
|
||||||
|
if (keys.length === 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (legend && legend.asTable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function aggregations(
|
||||||
|
data: { [key: string]: number[] },
|
||||||
|
intervalTime: string[]
|
||||||
|
) {
|
||||||
|
const source: { [key: string]: unknown }[] = [];
|
||||||
|
const keys = Object.keys(data || {}).filter(
|
||||||
|
(i: any) => Array.isArray(data[i]) && data[i].length
|
||||||
|
);
|
||||||
|
const headers = [];
|
||||||
|
|
||||||
|
for (const [key, value] of keys.entries()) {
|
||||||
|
const arr = JSON.parse(JSON.stringify(data[value]));
|
||||||
|
const item: { [key: string]: unknown } = {
|
||||||
|
name: value,
|
||||||
|
topN: arr
|
||||||
|
.map((d: number, index: number) => {
|
||||||
|
return {
|
||||||
|
key: intervalTime[index],
|
||||||
|
value: d,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort(
|
||||||
|
(
|
||||||
|
a: { key: string; value: number },
|
||||||
|
b: { key: string; value: number }
|
||||||
|
) => b.value - a.value
|
||||||
|
)
|
||||||
|
.filter((_: unknown, index: number) => index < 10),
|
||||||
|
};
|
||||||
|
if (legend) {
|
||||||
|
if (legend.min) {
|
||||||
|
item.min = Math.min(...data[value]).toFixed(2);
|
||||||
|
if (key === 0) {
|
||||||
|
headers.push({ value: "min", label: "Min" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (legend.max) {
|
||||||
|
item.max = Math.max(...data[value]).toFixed(2);
|
||||||
|
if (key === 0) {
|
||||||
|
headers.push({ value: "max", label: "Max" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (legend.mean) {
|
||||||
|
const total = data[value].reduce((prev: number, next: number) => {
|
||||||
|
prev += Number(next);
|
||||||
|
return prev;
|
||||||
|
}, 0);
|
||||||
|
item.mean = (total / data[value].length).toFixed(4);
|
||||||
|
if (key === 0) {
|
||||||
|
headers.push({ value: "mean", label: "Mean" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (legend.total) {
|
||||||
|
item.total = data[value]
|
||||||
|
.reduce((prev: number, next: number) => {
|
||||||
|
prev += Number(next);
|
||||||
|
return prev;
|
||||||
|
}, 0)
|
||||||
|
.toFixed(2);
|
||||||
|
if (key === 0) {
|
||||||
|
headers.push({ value: "total", label: "Total" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { source, headers };
|
||||||
|
}
|
||||||
|
function chartColors(keys: string[]) {
|
||||||
|
let color: string[] = [];
|
||||||
|
switch (keys.length) {
|
||||||
|
case 2:
|
||||||
|
color = ["#FF6A84", "#a0b1e6"];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
color = ["#3f96e3"];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = [
|
||||||
|
"#30A4EB",
|
||||||
|
"#45BFC0",
|
||||||
|
"#FFCC55",
|
||||||
|
"#FF6A84",
|
||||||
|
"#a0a7e6",
|
||||||
|
"#c23531",
|
||||||
|
"#2f4554",
|
||||||
|
"#61a0a8",
|
||||||
|
"#d48265",
|
||||||
|
"#91c7ae",
|
||||||
|
"#749f83",
|
||||||
|
"#ca8622",
|
||||||
|
"#bda29a",
|
||||||
|
"#6e7074",
|
||||||
|
"#546570",
|
||||||
|
"#c4ccd3",
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
return { showEchartsLegend, isRight, aggregations, chartColors };
|
||||||
|
}
|
@@ -26,14 +26,18 @@ export function useListConfig(config: any, index: string) {
|
|||||||
config.metricConfig &&
|
config.metricConfig &&
|
||||||
config.metricConfig[i] &&
|
config.metricConfig[i] &&
|
||||||
config.metricConfig[i].calculation;
|
config.metricConfig[i].calculation;
|
||||||
const line =
|
const isLinear =
|
||||||
config.metricTypes[i] === MetricQueryTypes.ReadMetricsValues &&
|
[
|
||||||
!types.includes(calculation);
|
MetricQueryTypes.ReadMetricsValues,
|
||||||
|
MetricQueryTypes.ReadLabeledMetricsValues,
|
||||||
|
].includes(config.metricTypes[i]) && !types.includes(calculation);
|
||||||
const isAvg =
|
const isAvg =
|
||||||
config.metricTypes[i] === MetricQueryTypes.ReadMetricsValues &&
|
[
|
||||||
types.includes(calculation);
|
MetricQueryTypes.ReadMetricsValues,
|
||||||
|
MetricQueryTypes.ReadLabeledMetricsValues,
|
||||||
|
].includes(config.metricTypes[i]) && types.includes(calculation);
|
||||||
return {
|
return {
|
||||||
isLinear: line,
|
isLinear,
|
||||||
isAvg,
|
isAvg,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -74,18 +74,8 @@ export function useQueryProcessor(config: any) {
|
|||||||
order: c.sortOrder || "DES",
|
order: c.sortOrder || "DES",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
const entity = {
|
||||||
const labels = (c.labelsIndex || "")
|
scope: config.catalog,
|
||||||
.split(",")
|
|
||||||
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
|
||||||
variables.push(`$labels${index}: [String!]!`);
|
|
||||||
conditions[`labels${index}`] = labels;
|
|
||||||
}
|
|
||||||
variables.push(`$condition${index}: MetricsCondition!`);
|
|
||||||
conditions[`condition${index}`] = {
|
|
||||||
name,
|
|
||||||
entity: {
|
|
||||||
scope: dashboardStore.entity,
|
|
||||||
serviceName:
|
serviceName:
|
||||||
dashboardStore.entity === "All"
|
dashboardStore.entity === "All"
|
||||||
? undefined
|
? undefined
|
||||||
@@ -121,15 +111,36 @@ export function useQueryProcessor(config: any) {
|
|||||||
: undefined,
|
: undefined,
|
||||||
destEndpointName:
|
destEndpointName:
|
||||||
dashboardStore.entity === "EndpointRelation"
|
dashboardStore.entity === "EndpointRelation"
|
||||||
? selectorStore.currentDestPod &&
|
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
|
||||||
selectorStore.currentDestPod.value
|
|
||||||
: undefined,
|
: undefined,
|
||||||
destProcessName: dashboardStore.entity.includes("ProcessRelation")
|
destProcessName: dashboardStore.entity.includes("ProcessRelation")
|
||||||
? selectorStore.currentDestProcess &&
|
? selectorStore.currentDestProcess &&
|
||||||
selectorStore.currentDestProcess.value
|
selectorStore.currentDestProcess.value
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
if ([MetricQueryTypes.ReadRecords].includes(metricType)) {
|
||||||
|
variables.push(`$condition${index}: RecordCondition!`);
|
||||||
|
conditions[`condition${index}`] = {
|
||||||
|
name,
|
||||||
|
parentEntity: entity,
|
||||||
|
topN: c.topN || 10,
|
||||||
|
order: c.sortOrder || "DES",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
entity.scope = dashboardStore.entity;
|
||||||
|
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||||
|
const labels = (c.labelsIndex || "")
|
||||||
|
.split(",")
|
||||||
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
|
variables.push(`$labels${index}: [String!]!`);
|
||||||
|
conditions[`labels${index}`] = labels;
|
||||||
|
}
|
||||||
|
variables.push(`$condition${index}: MetricsCondition!`);
|
||||||
|
conditions[`condition${index}`] = {
|
||||||
|
name,
|
||||||
|
entity,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||||
return `${name}${index}: ${metricType}(condition: $condition${index}, labels: $labels${index}, duration: $duration)${RespFields[metricType]}`;
|
return `${name}${index}: ${metricType}(condition: $condition${index}, labels: $labels${index}, duration: $duration)${RespFields[metricType]}`;
|
||||||
@@ -138,6 +149,7 @@ export function useQueryProcessor(config: any) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
queryStr,
|
queryStr,
|
||||||
conditions,
|
conditions,
|
||||||
@@ -167,7 +179,7 @@ export function useSourceProcessor(
|
|||||||
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||||
|
|
||||||
if (type === MetricQueryTypes.ReadMetricsValues) {
|
if (type === MetricQueryTypes.ReadMetricsValues) {
|
||||||
source[m] =
|
source[c.label || m] =
|
||||||
(resp.data[keys[index]] &&
|
(resp.data[keys[index]] &&
|
||||||
calculateExp(resp.data[keys[index]].values.values, c)) ||
|
calculateExp(resp.data[keys[index]].values.values, c)) ||
|
||||||
[];
|
[];
|
||||||
@@ -196,8 +208,13 @@ export function useSourceProcessor(
|
|||||||
source[m] = aggregation(Number(Object.values(resp.data)[0]), c);
|
source[m] = aggregation(Number(Object.values(resp.data)[0]), c);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
type === MetricQueryTypes.SortMetrics ||
|
(
|
||||||
type === MetricQueryTypes.ReadSampledRecords
|
[
|
||||||
|
MetricQueryTypes.ReadRecords,
|
||||||
|
MetricQueryTypes.ReadSampledRecords,
|
||||||
|
MetricQueryTypes.SortMetrics,
|
||||||
|
] as string[]
|
||||||
|
).includes(type)
|
||||||
) {
|
) {
|
||||||
source[m] = (Object.values(resp.data)[0] || []).map(
|
source[m] = (Object.values(resp.data)[0] || []).map(
|
||||||
(d: { value: unknown; name: string }) => {
|
(d: { value: unknown; name: string }) => {
|
||||||
@@ -238,13 +255,19 @@ export function useSourceProcessor(
|
|||||||
|
|
||||||
export function useQueryPodsMetrics(
|
export function useQueryPodsMetrics(
|
||||||
pods: Array<Instance | Endpoint | Service | any>,
|
pods: Array<Instance | Endpoint | Service | any>,
|
||||||
config: { metrics: string[]; metricTypes: string[] },
|
config: {
|
||||||
|
metrics: string[];
|
||||||
|
metricTypes: string[];
|
||||||
|
metricConfig: MetricConfigOpt[];
|
||||||
|
},
|
||||||
scope: string
|
scope: string
|
||||||
) {
|
) {
|
||||||
if (!(config.metrics && config.metrics[0])) {
|
const metricTypes = (config.metricTypes || []).filter((m: string) => m);
|
||||||
|
if (!metricTypes.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(config.metricTypes && config.metricTypes[0])) {
|
const metrics = (config.metrics || []).filter((m: string) => m);
|
||||||
|
if (!metrics.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
@@ -266,14 +289,24 @@ export function useQueryPodsMetrics(
|
|||||||
endpointName: scope === "Endpoint" ? d.label : undefined,
|
endpointName: scope === "Endpoint" ? d.label : undefined,
|
||||||
normal: scope === "Service" ? d.normal : currentService.normal,
|
normal: scope === "Service" ? d.normal : currentService.normal,
|
||||||
};
|
};
|
||||||
const f = config.metrics.map((name: string, idx: number) => {
|
const f = metrics.map((name: string, idx: number) => {
|
||||||
const metricType = config.metricTypes[idx] || "";
|
const metricType = metricTypes[idx] || "";
|
||||||
|
variables.push(`$condition${index}${idx}: MetricsCondition!`);
|
||||||
conditions[`condition${index}${idx}`] = {
|
conditions[`condition${index}${idx}`] = {
|
||||||
name,
|
name,
|
||||||
entity: param,
|
entity: param,
|
||||||
};
|
};
|
||||||
variables.push(`$condition${index}${idx}: MetricsCondition!`);
|
let labelStr = "";
|
||||||
return `${name}${index}${idx}: ${metricType}(condition: $condition${index}${idx}, duration: $duration)${RespFields[metricType]}`;
|
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||||
|
const c = config.metricConfig[idx] || {};
|
||||||
|
variables.push(`$labels${index}${idx}: [String!]!`);
|
||||||
|
labelStr = `labels: $labels${index}${idx}, `;
|
||||||
|
const labels = (c.labelsIndex || "")
|
||||||
|
.split(",")
|
||||||
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
|
conditions[`labels${index}${idx}`] = labels;
|
||||||
|
}
|
||||||
|
return `${name}${index}${idx}: ${metricType}(condition: $condition${index}${idx}, ${labelStr}duration: $duration)${RespFields[metricType]}`;
|
||||||
});
|
});
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@@ -283,6 +316,7 @@ export function useQueryPodsMetrics(
|
|||||||
|
|
||||||
return { queryStr, conditions };
|
return { queryStr, conditions };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePodsSource(
|
export function usePodsSource(
|
||||||
pods: Array<Instance | Endpoint>,
|
pods: Array<Instance | Endpoint>,
|
||||||
resp: { errors: string; data: { [key: string]: any } },
|
resp: { errors: string; data: { [key: string]: any } },
|
||||||
@@ -296,12 +330,20 @@ export function usePodsSource(
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
const names: string[] = [];
|
||||||
|
const metricConfigArr: MetricConfigOpt[] = [];
|
||||||
|
const metricTypesArr: string[] = [];
|
||||||
const data = pods.map((d: Instance | any, idx: number) => {
|
const data = pods.map((d: Instance | any, idx: number) => {
|
||||||
config.metrics.map((name: string, index: number) => {
|
config.metrics.map((name: string, index: number) => {
|
||||||
const c: any = (config.metricConfig && config.metricConfig[index]) || {};
|
const c: any = (config.metricConfig && config.metricConfig[index]) || {};
|
||||||
const key = name + idx + index;
|
const key = name + idx + index;
|
||||||
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
|
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
|
||||||
d[name] = aggregation(resp.data[key], c);
|
d[name] = aggregation(resp.data[key], c);
|
||||||
|
if (idx === 0) {
|
||||||
|
names.push(name);
|
||||||
|
metricConfigArr.push(c);
|
||||||
|
metricTypesArr.push(config.metricTypes[index]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
|
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
|
||||||
d[name] = {};
|
d[name] = {};
|
||||||
@@ -317,12 +359,56 @@ export function usePodsSource(
|
|||||||
d[name]["values"] = resp.data[key].values.values.map(
|
d[name]["values"] = resp.data[key].values.values.map(
|
||||||
(val: { value: number }) => aggregation(val.value, c)
|
(val: { value: number }) => aggregation(val.value, c)
|
||||||
);
|
);
|
||||||
|
if (idx === 0) {
|
||||||
|
names.push(name);
|
||||||
|
metricConfigArr.push(c);
|
||||||
|
metricTypesArr.push(config.metricTypes[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
config.metricTypes[index] === MetricQueryTypes.ReadLabeledMetricsValues
|
||||||
|
) {
|
||||||
|
const resVal = resp.data[key] || [];
|
||||||
|
const labels = (c.label || "")
|
||||||
|
.split(",")
|
||||||
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
|
const labelsIdx = (c.labelsIndex || "")
|
||||||
|
.split(",")
|
||||||
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
|
for (let i = 0; i < resVal.length; i++) {
|
||||||
|
const item = resVal[i];
|
||||||
|
const values = item.values.values.map((d: { value: number }) =>
|
||||||
|
aggregation(Number(d.value), c)
|
||||||
|
);
|
||||||
|
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
|
||||||
|
let key = item.label;
|
||||||
|
if (labels[indexNum] && indexNum > -1) {
|
||||||
|
key = labels[indexNum];
|
||||||
|
}
|
||||||
|
if (!d[key]) {
|
||||||
|
d[key] = {};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
Calculations.Average,
|
||||||
|
Calculations.ApdexAvg,
|
||||||
|
Calculations.PercentageAvg,
|
||||||
|
].includes(c.calculation)
|
||||||
|
) {
|
||||||
|
d[key]["avg"] = calculateExp(item.values.values, c);
|
||||||
|
}
|
||||||
|
d[key]["values"] = values;
|
||||||
|
if (idx === 0) {
|
||||||
|
names.push(key);
|
||||||
|
metricConfigArr.push({ ...c, index: i });
|
||||||
|
metricTypesArr.push(config.metricTypes[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
return data;
|
return { data, names, metricConfigArr, metricTypesArr };
|
||||||
}
|
}
|
||||||
export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
|
export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
@@ -435,6 +521,7 @@ export async function useGetMetricEntity(metric: string, metricType: any) {
|
|||||||
[
|
[
|
||||||
MetricQueryTypes.ReadSampledRecords,
|
MetricQueryTypes.ReadSampledRecords,
|
||||||
MetricQueryTypes.SortMetrics,
|
MetricQueryTypes.SortMetrics,
|
||||||
|
MetricQueryTypes.ReadRecords,
|
||||||
].includes(metricType)
|
].includes(metricType)
|
||||||
) {
|
) {
|
||||||
const res = await dashboardStore.fetchMetricList(metric);
|
const res = await dashboardStore.fetchMetricList(metric);
|
@@ -18,7 +18,7 @@ limitations under the License. -->
|
|||||||
<div class="app-config">
|
<div class="app-config">
|
||||||
<span class="red" v-show="timeRange">{{ t("timeTips") }}</span>
|
<span class="red" v-show="timeRange">{{ t("timeTips") }}</span>
|
||||||
<TimePicker
|
<TimePicker
|
||||||
:value="time"
|
:value="[appStore.durationRow.start, appStore.durationRow.end]"
|
||||||
position="bottom"
|
position="bottom"
|
||||||
format="YYYY-MM-DD HH:mm"
|
format="YYYY-MM-DD HH:mm"
|
||||||
@input="changeTimeRange"
|
@input="changeTimeRange"
|
||||||
@@ -55,17 +55,12 @@ import { useI18n } from "vue-i18n";
|
|||||||
import timeFormat from "@/utils/timeFormat";
|
import timeFormat from "@/utils/timeFormat";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import getLocalTime from "@/utils/localtime";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const pageName = ref<string>("");
|
const pageName = ref<string>("");
|
||||||
const timeRange = ref<number>(0);
|
const timeRange = ref<number>(0);
|
||||||
const time = ref<Date[]>([
|
|
||||||
appStore.durationRow.start,
|
|
||||||
appStore.durationRow.end,
|
|
||||||
]);
|
|
||||||
|
|
||||||
resetDuration();
|
resetDuration();
|
||||||
getVersion();
|
getVersion();
|
||||||
@@ -73,15 +68,13 @@ const setConfig = (value: string) => {
|
|||||||
pageName.value = value || "";
|
pageName.value = value || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReload = () => {
|
function handleReload() {
|
||||||
const gap =
|
const gap =
|
||||||
appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||||
const dates: Date[] = [
|
const dates: Date[] = [new Date(new Date().getTime() - gap), new Date()];
|
||||||
getLocalTime(appStore.utc, new Date(new Date().getTime() - gap)),
|
|
||||||
getLocalTime(appStore.utc, new Date()),
|
|
||||||
];
|
|
||||||
appStore.setDuration(timeFormat(dates));
|
appStore.setDuration(timeFormat(dates));
|
||||||
};
|
}
|
||||||
|
|
||||||
function changeTimeRange(val: Date[] | any) {
|
function changeTimeRange(val: Date[] | any) {
|
||||||
timeRange.value =
|
timeRange.value =
|
||||||
val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
|
val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
|
||||||
@@ -114,7 +107,6 @@ function resetDuration() {
|
|||||||
step: d.step,
|
step: d.step,
|
||||||
});
|
});
|
||||||
appStore.updateUTC(d.utc);
|
appStore.updateUTC(d.utc);
|
||||||
time.value = [new Date(d.start), new Date(d.end)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@@ -33,11 +33,7 @@ limitations under the License. -->
|
|||||||
<template v-for="(menu, index) in routes" :key="index">
|
<template v-for="(menu, index) in routes" :key="index">
|
||||||
<el-sub-menu :index="String(menu.name)" v-if="menu.meta.hasGroup">
|
<el-sub-menu :index="String(menu.name)" v-if="menu.meta.hasGroup">
|
||||||
<template #title>
|
<template #title>
|
||||||
<router-link
|
<router-link class="items" :to="menu.path">
|
||||||
class="items"
|
|
||||||
:to="menu.path"
|
|
||||||
:exact="menu.meta.exact || false"
|
|
||||||
>
|
|
||||||
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
|
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
|
||||||
<Icon size="lg" :iconName="menu.meta.icon" />
|
<Icon size="lg" :iconName="menu.meta.icon" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@@ -52,11 +48,7 @@ limitations under the License. -->
|
|||||||
:index="m.name"
|
:index="m.name"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link class="items" :to="m.path">
|
||||||
class="items"
|
|
||||||
:to="m.path"
|
|
||||||
:exact="(m.meta && m.meta.exact) || false"
|
|
||||||
>
|
|
||||||
<span class="title">{{ m.meta && t(m.meta.title) }}</span>
|
<span class="title">{{ m.meta && t(m.meta.title) }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
@@ -68,20 +60,12 @@ limitations under the License. -->
|
|||||||
v-else
|
v-else
|
||||||
>
|
>
|
||||||
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
|
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
|
||||||
<router-link
|
<router-link class="items" :to="menu.children[0].path">
|
||||||
class="items"
|
|
||||||
:to="menu.children[0].path"
|
|
||||||
:exact="menu.meta.exact"
|
|
||||||
>
|
|
||||||
<Icon size="lg" :iconName="menu.meta.icon" />
|
<Icon size="lg" :iconName="menu.meta.icon" />
|
||||||
</router-link>
|
</router-link>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<template #title>
|
<template #title>
|
||||||
<router-link
|
<router-link class="items" :to="menu.children[0].path">
|
||||||
class="items"
|
|
||||||
:to="menu.children[0].path"
|
|
||||||
:exact="menu.meta.exact"
|
|
||||||
>
|
|
||||||
<span class="title">{{ t(menu.meta.title) }}</span>
|
<span class="title">{{ t(menu.meta.title) }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
@@ -123,7 +107,7 @@ if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
|
|||||||
} else {
|
} else {
|
||||||
appStore.setIsMobile(false);
|
appStore.setIsMobile(false);
|
||||||
}
|
}
|
||||||
const isCollapse = ref(appStore.isMobile ? true : false);
|
const isCollapse = ref(false);
|
||||||
const controlMenu = () => {
|
const controlMenu = () => {
|
||||||
isCollapse.value = !isCollapse.value;
|
isCollapse.value = !isCollapse.value;
|
||||||
};
|
};
|
||||||
@@ -141,13 +125,13 @@ const filterMenus = (menus: any[]) => {
|
|||||||
.side-bar {
|
.side-bar {
|
||||||
background: #252a2f;
|
background: #252a2f;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 700px;
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-vertical:not(.el-menu--collapse) {
|
.el-menu-vertical:not(.el-menu--collapse) {
|
||||||
width: 200px;
|
width: 220px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +157,7 @@ span.collapse {
|
|||||||
.menu-control {
|
.menu-control {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
left: 200px;
|
left: 220px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s linear;
|
transition: all 0.2s linear;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
@@ -52,17 +52,19 @@ const msg = {
|
|||||||
instance: "Instance",
|
instance: "Instance",
|
||||||
create: "Create",
|
create: "Create",
|
||||||
loading: "Loading",
|
loading: "Loading",
|
||||||
selectVisualization: "Visualize your metrics",
|
selectVisualization: "Visualize Metrics",
|
||||||
visualization: "Visualization",
|
visualization: "Visualization",
|
||||||
graphStyles: "Graph styles",
|
graphStyles: "Graph Styles",
|
||||||
widgetOptions: "Widget options",
|
widgetOptions: "Widget Options",
|
||||||
standardOptions: "Standard options",
|
standardOptions: "Standard Options",
|
||||||
max: "Max",
|
max: "Max",
|
||||||
min: "Min",
|
min: "Min",
|
||||||
plus: "Plus",
|
plus: "Plus",
|
||||||
|
mean: "Mean",
|
||||||
minus: "Minus",
|
minus: "Minus",
|
||||||
multiply: "Multiply",
|
multiply: "Multiply",
|
||||||
divide: "Divide",
|
divide: "Divide",
|
||||||
|
total: "Total",
|
||||||
convertToMilliseconds: "Convert Unix Timestamp(milliseconds)",
|
convertToMilliseconds: "Convert Unix Timestamp(milliseconds)",
|
||||||
convertToSeconds: "Convert Unix Timestamp(seconds)",
|
convertToSeconds: "Convert Unix Timestamp(seconds)",
|
||||||
smooth: "Smooth",
|
smooth: "Smooth",
|
||||||
@@ -108,6 +110,7 @@ const msg = {
|
|||||||
showXAxis: "Show X Axis",
|
showXAxis: "Show X Axis",
|
||||||
showYAxis: "Show Y Axis",
|
showYAxis: "Show Y Axis",
|
||||||
nameError: "The dashboard name cannot be duplicate",
|
nameError: "The dashboard name cannot be duplicate",
|
||||||
|
nameEmptyError: "The dashboard name cannot be empty",
|
||||||
showGroup: "Show Group",
|
showGroup: "Show Group",
|
||||||
noRoot: "Please set a root dashboard for",
|
noRoot: "Please set a root dashboard for",
|
||||||
noWidget: "Please add widgets.",
|
noWidget: "Please add widgets.",
|
||||||
@@ -123,6 +126,7 @@ const msg = {
|
|||||||
editWarning: "You are entering edit mode",
|
editWarning: "You are entering edit mode",
|
||||||
viewWarning: "You are entering view mode",
|
viewWarning: "You are entering view mode",
|
||||||
virtualDatabase: "Virtual Database",
|
virtualDatabase: "Virtual Database",
|
||||||
|
virtualCache: "Virtual Cache",
|
||||||
reloadDashboards: "Reload dashboards",
|
reloadDashboards: "Reload dashboards",
|
||||||
kubernetesService: "Service",
|
kubernetesService: "Service",
|
||||||
kubernetesCluster: "Cluster",
|
kubernetesCluster: "Cluster",
|
||||||
@@ -144,6 +148,7 @@ const msg = {
|
|||||||
pause: "Pause",
|
pause: "Pause",
|
||||||
begin: "Start",
|
begin: "Start",
|
||||||
associateOptions: "Association Options",
|
associateOptions: "Association Options",
|
||||||
|
associateMetrics: "Association Metrics",
|
||||||
widget: "Widget",
|
widget: "Widget",
|
||||||
nameTip:
|
nameTip:
|
||||||
"The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
|
"The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
|
||||||
@@ -152,6 +157,30 @@ const msg = {
|
|||||||
text: "Text",
|
text: "Text",
|
||||||
query: "Query",
|
query: "Query",
|
||||||
postgreSQL: "PostgreSQL",
|
postgreSQL: "PostgreSQL",
|
||||||
|
endpointTips: "The table shows up to 20 pieces of endpoints.",
|
||||||
|
apisix: "APISIX",
|
||||||
|
viewTrace: "View Related Traces",
|
||||||
|
relatedTraceOptions: "Related Trace Options",
|
||||||
|
setLatencyDuration: "Latency Related Metrics",
|
||||||
|
queryOrder: "Query By Duration",
|
||||||
|
setOrder: "Query Order",
|
||||||
|
latency: "Latency",
|
||||||
|
metricValues: "Metric Values",
|
||||||
|
queryConditions: "Query Conditions",
|
||||||
|
enableRelatedTrace: "Enable Related Trace",
|
||||||
|
maxTraceDuration: "Maximum Duration",
|
||||||
|
minTraceDuration: "Minimum Duration",
|
||||||
|
legendOptions: "Legend Options",
|
||||||
|
showLegend: "Show Legend",
|
||||||
|
asTable: "As Table",
|
||||||
|
toTheRight: "To The Right",
|
||||||
|
legendValues: "Legend Values",
|
||||||
|
minDuration: "Minimal Request Duration",
|
||||||
|
when4xx:
|
||||||
|
"Sample HTTP requests and responses with tracing when response code between 400 and 499",
|
||||||
|
when5xx:
|
||||||
|
"Sample HTTP requests and responses with tracing when response code between 500 and 599",
|
||||||
|
taskTitle: "HTTP request and response collecting rules",
|
||||||
seconds: "Seconds",
|
seconds: "Seconds",
|
||||||
hourTip: "Select Hour",
|
hourTip: "Select Hour",
|
||||||
minuteTip: "Select Minute",
|
minuteTip: "Select Minute",
|
||||||
@@ -244,7 +273,7 @@ const msg = {
|
|||||||
entityType: "Entity Type",
|
entityType: "Entity Type",
|
||||||
maxItemNum: "Max number of Item",
|
maxItemNum: "Max number of Item",
|
||||||
unknownMetrics: "Unknown Metrics",
|
unknownMetrics: "Unknown Metrics",
|
||||||
labels: "Labels",
|
labels: "Label",
|
||||||
aggregation: "Calculation",
|
aggregation: "Calculation",
|
||||||
unit: "Unit",
|
unit: "Unit",
|
||||||
labelsIndex: "Label Subscript",
|
labelsIndex: "Label Subscript",
|
||||||
@@ -305,6 +334,7 @@ const msg = {
|
|||||||
eventsParameters: "Event Parameters",
|
eventsParameters: "Event Parameters",
|
||||||
eventDetail: "Event Detail",
|
eventDetail: "Event Detail",
|
||||||
value: "Value",
|
value: "Value",
|
||||||
|
key: "Key",
|
||||||
show: "Show",
|
show: "Show",
|
||||||
hide: "Hide",
|
hide: "Hide",
|
||||||
statistics: "Statistics",
|
statistics: "Statistics",
|
||||||
@@ -343,5 +373,7 @@ const msg = {
|
|||||||
conditionNotice:
|
conditionNotice:
|
||||||
"Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
|
"Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
|
||||||
language: "Language",
|
language: "Language",
|
||||||
|
gateway: "Gateway",
|
||||||
|
virtualMQ: "Virtual MQ",
|
||||||
};
|
};
|
||||||
export default msg;
|
export default msg;
|
||||||
|
@@ -59,9 +59,11 @@ const msg = {
|
|||||||
standardOptions: "Opciones estandar",
|
standardOptions: "Opciones estandar",
|
||||||
max: "Máx",
|
max: "Máx",
|
||||||
min: "Mín",
|
min: "Mín",
|
||||||
|
mean: "Promedio",
|
||||||
plus: "Más",
|
plus: "Más",
|
||||||
minus: "Menoss",
|
minus: "Menoss",
|
||||||
multiply: "Multiplcar",
|
multiply: "Multiplcar",
|
||||||
|
total: "Todo",
|
||||||
divide: "Dividir",
|
divide: "Dividir",
|
||||||
convertToMilliseconds: "Convertir Unix Timestamp(milisegundos)",
|
convertToMilliseconds: "Convertir Unix Timestamp(milisegundos)",
|
||||||
convertToSeconds: "Convertir Unix Timestamp(segundos)",
|
convertToSeconds: "Convertir Unix Timestamp(segundos)",
|
||||||
@@ -110,6 +112,7 @@ const msg = {
|
|||||||
showXAxis: "Mostrar Eje X",
|
showXAxis: "Mostrar Eje X",
|
||||||
showYAxis: "Mostrar Eje Y",
|
showYAxis: "Mostrar Eje Y",
|
||||||
nameError: "El nombre del panel no puede ser duplicado",
|
nameError: "El nombre del panel no puede ser duplicado",
|
||||||
|
nameEmptyError: "El nombre del panel no puede estar vacío",
|
||||||
showGroup: "Mostrar Grupo",
|
showGroup: "Mostrar Grupo",
|
||||||
noRoot: "Por favor ponga la raíz del panel",
|
noRoot: "Por favor ponga la raíz del panel",
|
||||||
noWidget: "Por favor añada widgets.",
|
noWidget: "Por favor añada widgets.",
|
||||||
@@ -125,6 +128,7 @@ const msg = {
|
|||||||
editWarning: "Estás entrando en modo edición",
|
editWarning: "Estás entrando en modo edición",
|
||||||
viewWarning: "Estás entrando en modo visualización",
|
viewWarning: "Estás entrando en modo visualización",
|
||||||
virtualDatabase: "Base de Datos Virtual",
|
virtualDatabase: "Base de Datos Virtual",
|
||||||
|
virtualCache: "Caché virtual",
|
||||||
reloadDashboards: "Recargar Panel",
|
reloadDashboards: "Recargar Panel",
|
||||||
kubernetesService: "Servicio",
|
kubernetesService: "Servicio",
|
||||||
kubernetesCluster: "Cluster",
|
kubernetesCluster: "Cluster",
|
||||||
@@ -144,6 +148,7 @@ const msg = {
|
|||||||
pause: "Pausa",
|
pause: "Pausa",
|
||||||
begin: "Inicio",
|
begin: "Inicio",
|
||||||
associateOptions: "Opciones de asociación",
|
associateOptions: "Opciones de asociación",
|
||||||
|
associateMetrics: "Índice de correlación",
|
||||||
widget: "Dispositivo pequeño",
|
widget: "Dispositivo pequeño",
|
||||||
text: "Texto",
|
text: "Texto",
|
||||||
duplicateName: "Nombre duplicado",
|
duplicateName: "Nombre duplicado",
|
||||||
@@ -152,10 +157,34 @@ const msg = {
|
|||||||
enableAssociate: "Activar asociación",
|
enableAssociate: "Activar asociación",
|
||||||
query: "Consulta",
|
query: "Consulta",
|
||||||
postgreSQL: "PostgreSQL",
|
postgreSQL: "PostgreSQL",
|
||||||
|
endpointTips: "Aquí, la tabla muestra hasta 20 punto final.",
|
||||||
|
apisix: "APISIX",
|
||||||
|
queryOrder: "Consulta por duración",
|
||||||
|
setOrder: "Orden de consulta",
|
||||||
|
latency: "Retraso",
|
||||||
|
metricValues: "Valor métrico",
|
||||||
|
legendValues: "Valor de la leyenda",
|
||||||
seconds: "Segundos",
|
seconds: "Segundos",
|
||||||
hourTip: "Seleccione Hora",
|
hourTip: "Seleccione Hora",
|
||||||
minuteTip: "Seleccione Minuto",
|
minuteTip: "Seleccione Minuto",
|
||||||
secondTip: "Seleccione Segundo",
|
secondTip: "Seleccione Segundo",
|
||||||
|
viewTrace: "Ver trazas relacionadas",
|
||||||
|
relatedTraceOptions: "Opciones de seguimiento relacionadas",
|
||||||
|
setLatencyDuration: "Índice de correlación retardada",
|
||||||
|
enableRelatedTrace: "Activar trazas relacionadas",
|
||||||
|
queryConditions: "Condiciones de consulta",
|
||||||
|
maxTraceDuration: "Duración máxima",
|
||||||
|
minTraceDuration: "Duración mínima",
|
||||||
|
legendOptions: "Opciones de leyenda",
|
||||||
|
showLegend: "Mostrar leyenda",
|
||||||
|
asTable: "Como tabla",
|
||||||
|
toTheRight: "Derecha",
|
||||||
|
minDuration: "Duración mínima de la solicitud",
|
||||||
|
when4xx:
|
||||||
|
"Ejemplo de solicitud y respuesta http con seguimiento cuando el Código de respuesta está entre 400 y 499",
|
||||||
|
when5xx:
|
||||||
|
"Ejemplo de solicitud y respuesta http con seguimiento cuando el Código de respuesta está entre 500 y 599",
|
||||||
|
taskTitle: "Reglas de recolección de peticiones y respuestas HTTP",
|
||||||
second: "s",
|
second: "s",
|
||||||
yearSuffix: "Año",
|
yearSuffix: "Año",
|
||||||
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
||||||
@@ -305,6 +334,7 @@ const msg = {
|
|||||||
eventsParameters: "Parámetro del Evento",
|
eventsParameters: "Parámetro del Evento",
|
||||||
eventDetail: "Detalle del Evento",
|
eventDetail: "Detalle del Evento",
|
||||||
value: "Valor",
|
value: "Valor",
|
||||||
|
key: "Clave",
|
||||||
show: "Mostrar",
|
show: "Mostrar",
|
||||||
hide: "Oculatr",
|
hide: "Oculatr",
|
||||||
statistics: "Estadísticas",
|
statistics: "Estadísticas",
|
||||||
@@ -346,5 +376,7 @@ const msg = {
|
|||||||
conditionNotice:
|
conditionNotice:
|
||||||
"Aviso: Por favor presione Intro después de introducir una clave de contenido, excluir clave de contenido(clave=valor).",
|
"Aviso: Por favor presione Intro después de introducir una clave de contenido, excluir clave de contenido(clave=valor).",
|
||||||
language: "Lenguaje",
|
language: "Lenguaje",
|
||||||
|
gateway: "Puerta",
|
||||||
|
virtualMQ: "MQ virtual",
|
||||||
};
|
};
|
||||||
export default msg;
|
export default msg;
|
||||||
|
@@ -56,10 +56,12 @@ const msg = {
|
|||||||
standardOptions: "标准选项",
|
standardOptions: "标准选项",
|
||||||
max: "最大值",
|
max: "最大值",
|
||||||
min: "最小值",
|
min: "最小值",
|
||||||
|
mean: "平均值",
|
||||||
plus: "加法",
|
plus: "加法",
|
||||||
minus: "减法",
|
minus: "减法",
|
||||||
multiply: "乘法",
|
multiply: "乘法",
|
||||||
divide: "除法",
|
divide: "除法",
|
||||||
|
total: "总计",
|
||||||
convertToMilliseconds: "转换Unix时间戳(毫秒)",
|
convertToMilliseconds: "转换Unix时间戳(毫秒)",
|
||||||
convertToSeconds: "转换Unix时间戳(秒)",
|
convertToSeconds: "转换Unix时间戳(秒)",
|
||||||
smooth: "光滑的",
|
smooth: "光滑的",
|
||||||
@@ -106,6 +108,7 @@ const msg = {
|
|||||||
showXAxis: "显示X轴",
|
showXAxis: "显示X轴",
|
||||||
showYAxis: "显示Y轴",
|
showYAxis: "显示Y轴",
|
||||||
nameError: "仪表板名称不能重复",
|
nameError: "仪表板名称不能重复",
|
||||||
|
nameEmptyError: "仪表板名称不能为空",
|
||||||
noRoot: "请设置根仪表板,为",
|
noRoot: "请设置根仪表板,为",
|
||||||
showGroup: "显示分组",
|
showGroup: "显示分组",
|
||||||
noWidget: "请添加组件",
|
noWidget: "请添加组件",
|
||||||
@@ -121,6 +124,7 @@ const msg = {
|
|||||||
editWarning: "你正在进入编辑模式",
|
editWarning: "你正在进入编辑模式",
|
||||||
viewWarning: "你正在进入预览模式",
|
viewWarning: "你正在进入预览模式",
|
||||||
virtualDatabase: "虚拟数据库",
|
virtualDatabase: "虚拟数据库",
|
||||||
|
virtualCache: "虚拟缓存",
|
||||||
reloadDashboards: "重新加载仪表盘",
|
reloadDashboards: "重新加载仪表盘",
|
||||||
kubernetesService: "服务",
|
kubernetesService: "服务",
|
||||||
kubernetesCluster: "集群",
|
kubernetesCluster: "集群",
|
||||||
@@ -142,6 +146,7 @@ const msg = {
|
|||||||
pause: "暂停",
|
pause: "暂停",
|
||||||
begin: "开始",
|
begin: "开始",
|
||||||
associateOptions: "关联选项",
|
associateOptions: "关联选项",
|
||||||
|
associateMetrics: "关联指标",
|
||||||
widget: "部件",
|
widget: "部件",
|
||||||
enableAssociate: "启用关联",
|
enableAssociate: "启用关联",
|
||||||
nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
|
nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
|
||||||
@@ -149,6 +154,28 @@ const msg = {
|
|||||||
text: "文本",
|
text: "文本",
|
||||||
query: "查询",
|
query: "查询",
|
||||||
postgreSQL: "PostgreSQL",
|
postgreSQL: "PostgreSQL",
|
||||||
|
endpointTips: "这里最多展示20条endpoints。",
|
||||||
|
apisix: "APISIX",
|
||||||
|
viewTrace: "查看相关Trace",
|
||||||
|
relatedTraceOptions: "相关的Trace选项",
|
||||||
|
setLatencyDuration: "延迟相关指标",
|
||||||
|
queryOrder: "按持续时间查询",
|
||||||
|
setOrder: "查询顺序",
|
||||||
|
latency: "延迟",
|
||||||
|
metricValues: "指标值",
|
||||||
|
enableRelatedTrace: "启用相关Trace",
|
||||||
|
queryConditions: "查询条件",
|
||||||
|
maxTraceDuration: "最大持续时间",
|
||||||
|
minTraceDuration: "最小持续时间",
|
||||||
|
legendOptions: "图例选项",
|
||||||
|
showLegend: "显示图例",
|
||||||
|
asTable: "作为表格",
|
||||||
|
toTheRight: "在右边",
|
||||||
|
legendValues: "图例值",
|
||||||
|
minDuration: "最小请求持续时间",
|
||||||
|
when4xx: "当响应代码介于400和499之间时,带有跟踪的HTTP请求和响应示例",
|
||||||
|
when5xx: "当响应代码介于500和599之间时,带有跟踪的HTTP请求和响应示例",
|
||||||
|
taskTitle: "HTTP请求和响应收集规则",
|
||||||
seconds: "秒",
|
seconds: "秒",
|
||||||
hourTip: "选择小时",
|
hourTip: "选择小时",
|
||||||
minuteTip: "选择分钟",
|
minuteTip: "选择分钟",
|
||||||
@@ -303,6 +330,7 @@ const msg = {
|
|||||||
eventsParameters: "事件参数",
|
eventsParameters: "事件参数",
|
||||||
eventDetail: "事件详情",
|
eventDetail: "事件详情",
|
||||||
value: "数值",
|
value: "数值",
|
||||||
|
key: "Key",
|
||||||
tableHeader: "表头名称",
|
tableHeader: "表头名称",
|
||||||
tableValues: "表值",
|
tableValues: "表值",
|
||||||
show: "展示",
|
show: "展示",
|
||||||
@@ -343,5 +371,7 @@ const msg = {
|
|||||||
conditionNotice:
|
conditionNotice:
|
||||||
"请输入一个内容关键词或者内容不包含的关键词(key=value)之后回车",
|
"请输入一个内容关键词或者内容不包含的关键词(key=value)之后回车",
|
||||||
language: "语言",
|
language: "语言",
|
||||||
|
gateway: "网关",
|
||||||
|
virtualMQ: "虚拟消息队列",
|
||||||
};
|
};
|
||||||
export default msg;
|
export default msg;
|
||||||
|
@@ -25,16 +25,12 @@ export const routesAlarm: Array<RouteRecordRaw> = [
|
|||||||
title: "alarm",
|
title: "alarm",
|
||||||
icon: "spam",
|
icon: "spam",
|
||||||
hasGroup: false,
|
hasGroup: false,
|
||||||
exact: true,
|
|
||||||
},
|
},
|
||||||
component: Layout,
|
component: Layout,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/alerting",
|
path: "/alerting",
|
||||||
name: "Alarm",
|
name: "Alarm",
|
||||||
meta: {
|
|
||||||
exact: false,
|
|
||||||
},
|
|
||||||
component: () =>
|
component: () =>
|
||||||
import(/* webpackChunkName: "alerting" */ "@/views/Alarm.vue"),
|
import(/* webpackChunkName: "alerting" */ "@/views/Alarm.vue"),
|
||||||
},
|
},
|
||||||
|
@@ -26,7 +26,6 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
|||||||
title: "dashboards",
|
title: "dashboards",
|
||||||
icon: "dashboard_customize",
|
icon: "dashboard_customize",
|
||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
exact: true,
|
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -38,7 +37,6 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
|||||||
name: "List",
|
name: "List",
|
||||||
meta: {
|
meta: {
|
||||||
title: "dashboardList",
|
title: "dashboardList",
|
||||||
exact: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +48,6 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
|||||||
name: "New",
|
name: "New",
|
||||||
meta: {
|
meta: {
|
||||||
title: "dashboardNew",
|
title: "dashboardNew",
|
||||||
exact: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -14,10 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesBrowser: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "Browser",
|
name: "Browser",
|
||||||
@@ -26,26 +24,22 @@ export const routesBrowser: Array<RouteRecordRaw> = [
|
|||||||
icon: "language",
|
icon: "language",
|
||||||
},
|
},
|
||||||
redirect: "/browser",
|
redirect: "/browser",
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/browser",
|
path: "/browser",
|
||||||
name: "Browser",
|
name: "Browser",
|
||||||
meta: {
|
meta: {
|
||||||
title: "browser",
|
title: "browser",
|
||||||
exact: true,
|
layer: "BROWSER",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/browser/tab/:activeTabIndex",
|
path: "/browser/tab/:activeTabIndex",
|
||||||
name: "BrowserActiveTabIndex",
|
name: "BrowserActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "BROWSER",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
@@ -14,10 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesDatabase: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "Database",
|
name: "Database",
|
||||||
@@ -27,45 +25,38 @@ export const routesDatabase: Array<RouteRecordRaw> = [
|
|||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
},
|
},
|
||||||
redirect: "/mySQL",
|
redirect: "/mySQL",
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/mySQL",
|
path: "/mySQL",
|
||||||
name: "MySQL",
|
name: "MySQL",
|
||||||
meta: {
|
meta: {
|
||||||
title: "mySQL",
|
title: "mySQL",
|
||||||
exact: true,
|
layer: "MYSQL",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/mySQL/tab/:activeTabIndex",
|
path: "/mySQL/tab/:activeTabIndex",
|
||||||
name: "MySQLActiveTabIndex",
|
name: "MySQLActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "MYSQL",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/postgreSQL",
|
path: "/postgreSQL",
|
||||||
name: "PostgreSQL",
|
name: "PostgreSQL",
|
||||||
meta: {
|
meta: {
|
||||||
title: "postgreSQL",
|
title: "postgreSQL",
|
||||||
exact: true,
|
layer: "POSTGRESQL",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/postgreSQL/tab/:activeTabIndex",
|
path: "/postgreSQL/tab/:activeTabIndex",
|
||||||
name: "PostgreSQLActiveTabIndex",
|
name: "PostgreSQLActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "POSTGRESQL",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
@@ -14,34 +14,25 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesFunctions: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "Functions",
|
name: "Functions",
|
||||||
meta: {
|
meta: {
|
||||||
title: "functions",
|
title: "functions",
|
||||||
icon: "cloud_queue",
|
icon: "cloud_queue",
|
||||||
|
layer: "FAAS",
|
||||||
},
|
},
|
||||||
redirect: "/functions",
|
redirect: "/functions",
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/functions",
|
path: "/functions",
|
||||||
name: "Functions",
|
name: "Functions",
|
||||||
meta: {
|
|
||||||
exact: true,
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/functions/tab/:activeTabIndex",
|
path: "/functions/tab/:activeTabIndex",
|
||||||
name: "FunctionsActiveTabIndex",
|
name: "FunctionsActiveTabIndex",
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
47
src/router/data/gateway.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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 default [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "Gateway",
|
||||||
|
meta: {
|
||||||
|
title: "gateway",
|
||||||
|
icon: "gateway",
|
||||||
|
hasGroup: true,
|
||||||
|
},
|
||||||
|
redirect: "/apisix",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/apisix",
|
||||||
|
name: "APISIX",
|
||||||
|
meta: {
|
||||||
|
title: "apisix",
|
||||||
|
layer: "APISIX",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/apisix/tab/:activeTabIndex",
|
||||||
|
name: "APISIXActiveTabIndex",
|
||||||
|
meta: {
|
||||||
|
notShow: true,
|
||||||
|
layer: "APISIX",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
@@ -14,10 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesGen: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "General",
|
name: "General",
|
||||||
@@ -25,48 +23,71 @@ export const routesGen: Array<RouteRecordRaw> = [
|
|||||||
title: "general",
|
title: "general",
|
||||||
icon: "chart",
|
icon: "chart",
|
||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
exact: true,
|
|
||||||
},
|
},
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/general",
|
path: "/general",
|
||||||
name: "GeneralServices",
|
name: "GeneralServices",
|
||||||
meta: {
|
meta: {
|
||||||
exact: true,
|
|
||||||
title: "services",
|
title: "services",
|
||||||
|
layer: "GENERAL",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layers" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/general/tab/:activeTabIndex",
|
path: "/general/tab/:activeTabIndex",
|
||||||
name: "GeneralServicesActiveTabIndex",
|
name: "GeneralServicesActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
exact: true,
|
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "GENERAL",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layers" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/database",
|
path: "/database",
|
||||||
name: "VirtualDatabase",
|
name: "VirtualDatabase",
|
||||||
meta: {
|
meta: {
|
||||||
title: "virtualDatabase",
|
title: "virtualDatabase",
|
||||||
exact: true,
|
layer: "VIRTUAL_DATABASE",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/database/tab/:activeTabIndex",
|
path: "/database/tab/:activeTabIndex",
|
||||||
name: "VirtualDatabaseActiveTabIndex",
|
name: "VirtualDatabaseActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "VIRTUAL_DATABASE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/cache",
|
||||||
|
name: "VirtualCache",
|
||||||
|
meta: {
|
||||||
|
title: "virtualCache",
|
||||||
|
layer: "VIRTUAL_CACHE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/cache/tab/:activeTabIndex",
|
||||||
|
name: "VirtualCacheActiveTabIndex",
|
||||||
|
meta: {
|
||||||
|
notShow: true,
|
||||||
|
layer: "VIRTUAL_CACHE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/mq",
|
||||||
|
name: "VirtualMQ",
|
||||||
|
meta: {
|
||||||
|
title: "virtualMQ",
|
||||||
|
layer: "VIRTUAL_MQ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/mq/tab/:activeTabIndex",
|
||||||
|
name: "VirtualMQActiveTabIndex",
|
||||||
|
meta: {
|
||||||
|
notShow: true,
|
||||||
|
layer: "VIRTUAL_MQ",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
37
src/router/data/index.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import general from "./general";
|
||||||
|
import serviceMesh from "./serviceMesh";
|
||||||
|
import database from "./database";
|
||||||
|
import infrastructure from "./infrastructure";
|
||||||
|
import selfObservability from "./selfObservability";
|
||||||
|
import functions from "./functions";
|
||||||
|
import browser from "./browser";
|
||||||
|
import k8s from "./k8s";
|
||||||
|
import gateway from "./gateway";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...general,
|
||||||
|
...serviceMesh,
|
||||||
|
...functions,
|
||||||
|
...k8s,
|
||||||
|
...infrastructure,
|
||||||
|
...browser,
|
||||||
|
...gateway,
|
||||||
|
...database,
|
||||||
|
...selfObservability,
|
||||||
|
];
|
@@ -14,30 +14,25 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesInfra: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "Infrastructure",
|
name: "Infrastructure",
|
||||||
meta: {
|
meta: {
|
||||||
title: "infrastructure",
|
title: "infrastructure",
|
||||||
icon: "scatter_plot",
|
icon: "scatter_plot",
|
||||||
exact: true,
|
|
||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
},
|
},
|
||||||
redirect: "/linux",
|
redirect: "/linux",
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/linux",
|
path: "/linux",
|
||||||
name: "Linux",
|
name: "Linux",
|
||||||
meta: {
|
meta: {
|
||||||
title: "linux",
|
title: "linux",
|
||||||
|
layer: "OS_LINUX",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/linux/tab/:activeTabIndex",
|
path: "/linux/tab/:activeTabIndex",
|
||||||
@@ -45,26 +40,9 @@ export const routesInfra: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: "linux",
|
title: "linux",
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "OS_LINUX",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// path: "/infrastructure/vm",
|
|
||||||
// name: "VirtualMachine",
|
|
||||||
// meta: {
|
|
||||||
// title: "virtualMachine",
|
|
||||||
// },
|
|
||||||
// component: () => import("@/views/infrastructure/Infrastructure.vue"),
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: "/infrastructure/k8s",
|
|
||||||
// name: "Kubernetes",
|
|
||||||
// meta: {
|
|
||||||
// title: "kubernetes",
|
|
||||||
// },
|
|
||||||
// component: () => import("@/views/infrastructure/Infrastructure.vue"),
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
@@ -14,10 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesK8s: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "Kubernetes",
|
name: "Kubernetes",
|
||||||
@@ -27,7 +25,6 @@ export const routesK8s: Array<RouteRecordRaw> = [
|
|||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
},
|
},
|
||||||
redirect: "/kubernetes/cluster",
|
redirect: "/kubernetes/cluster",
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/kubernetes/cluster",
|
path: "/kubernetes/cluster",
|
||||||
@@ -35,9 +32,8 @@ export const routesK8s: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: false,
|
notShow: false,
|
||||||
title: "kubernetesCluster",
|
title: "kubernetesCluster",
|
||||||
|
layer: "K8S",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/kubernetes/cluster/tab/:activeTabIndex",
|
path: "/kubernetes/cluster/tab/:activeTabIndex",
|
||||||
@@ -45,9 +41,8 @@ export const routesK8s: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
title: "kubernetesClusterActiveTabIndex",
|
title: "kubernetesClusterActiveTabIndex",
|
||||||
|
layer: "K8S",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/kubernetes/service",
|
path: "/kubernetes/service",
|
||||||
@@ -55,9 +50,8 @@ export const routesK8s: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: false,
|
notShow: false,
|
||||||
title: "kubernetesService",
|
title: "kubernetesService",
|
||||||
|
layer: "K8S_SERVICE",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/kubernetes/service/tab/:activeTabIndex",
|
path: "/kubernetes/service/tab/:activeTabIndex",
|
||||||
@@ -65,9 +59,8 @@ export const routesK8s: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
title: "kubernetesServiceActiveTabIndex",
|
title: "kubernetesServiceActiveTabIndex",
|
||||||
|
layer: "K8S_SERVICE",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
@@ -14,10 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesSelf: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "SelfObservability",
|
name: "SelfObservability",
|
||||||
@@ -27,43 +25,38 @@ export const routesSelf: Array<RouteRecordRaw> = [
|
|||||||
icon: "logo",
|
icon: "logo",
|
||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
},
|
},
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/self/skyWalkingServer",
|
path: "/self/skyWalkingServer",
|
||||||
name: "SkyWalkingServer",
|
name: "SkyWalkingServer",
|
||||||
meta: {
|
meta: {
|
||||||
title: "skyWalkingServer",
|
title: "skyWalkingServer",
|
||||||
|
layer: "SO11Y_OAP",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/self/skyWalkingServer/tab/:activeTabIndex",
|
path: "/self/skyWalkingServer/tab/:activeTabIndex",
|
||||||
name: "SkyWalkingServerActiveTabIndex",
|
name: "SkyWalkingServerActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "SO11Y_OAP",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/self/satellite",
|
path: "/self/satellite",
|
||||||
name: "Satellite",
|
name: "Satellite",
|
||||||
meta: {
|
meta: {
|
||||||
title: "satellite",
|
title: "satellite",
|
||||||
|
layer: "SO11Y_SATELLITE",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/self/satellite/tab/:activeTabIndex",
|
path: "/self/satellite/tab/:activeTabIndex",
|
||||||
name: "SatelliteActiveTabIndex",
|
name: "SatelliteActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "SO11Y_SATELLITE",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
@@ -14,10 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { RouteRecordRaw } from "vue-router";
|
|
||||||
import Layout from "@/layout/Index.vue";
|
|
||||||
|
|
||||||
export const routesMesh: Array<RouteRecordRaw> = [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
name: "ServiceMesh",
|
name: "ServiceMesh",
|
||||||
@@ -27,7 +25,6 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
|||||||
icon: "epic",
|
icon: "epic",
|
||||||
hasGroup: true,
|
hasGroup: true,
|
||||||
},
|
},
|
||||||
component: Layout,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/mesh/services",
|
path: "/mesh/services",
|
||||||
@@ -35,18 +32,16 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: false,
|
notShow: false,
|
||||||
title: "services",
|
title: "services",
|
||||||
|
layer: "MESH",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/mesh/services/tab/:activeTabIndex",
|
path: "/mesh/services/tab/:activeTabIndex",
|
||||||
name: "MeshServicesActiveTabIndex",
|
name: "MeshServicesActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "MESH",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/mesh/controlPanel",
|
path: "/mesh/controlPanel",
|
||||||
@@ -54,18 +49,16 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: false,
|
notShow: false,
|
||||||
title: "controlPanel",
|
title: "controlPanel",
|
||||||
|
layer: "MESH_CP",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/mesh/controlPanel/tab/:activeTabIndex",
|
path: "/mesh/controlPanel/tab/:activeTabIndex",
|
||||||
name: "ControlPanelActiveTabIndex",
|
name: "ControlPanelActiveTabIndex",
|
||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
|
layer: "MESH_CP",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/mesh/dataPanel",
|
path: "/mesh/dataPanel",
|
||||||
@@ -73,9 +66,8 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: false,
|
notShow: false,
|
||||||
title: "dataPanel",
|
title: "dataPanel",
|
||||||
|
layer: "MESH_DP",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/mesh/dataPanel/tab/:activeTabIndex",
|
path: "/mesh/dataPanel/tab/:activeTabIndex",
|
||||||
@@ -83,9 +75,8 @@ export const routesMesh: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
notShow: true,
|
notShow: true,
|
||||||
title: "dataPanelActiveTabIndex",
|
title: "dataPanelActiveTabIndex",
|
||||||
|
layer: "MESH_DP",
|
||||||
},
|
},
|
||||||
component: () =>
|
|
||||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue"),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
@@ -15,27 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
|
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
|
||||||
import { routesGen } from "./general";
|
|
||||||
import { routesMesh } from "./serviceMesh";
|
|
||||||
import { routesDatabase } from "./database";
|
|
||||||
import { routesInfra } from "./infrastructure";
|
|
||||||
import { routesDashboard } from "./dashboard";
|
import { routesDashboard } from "./dashboard";
|
||||||
import { routesSetting } from "./setting";
|
import { routesSetting } from "./setting";
|
||||||
import { routesAlarm } from "./alarm";
|
import { routesAlarm } from "./alarm";
|
||||||
import { routesSelf } from "./selfObservability";
|
import routesLayers from "./layer";
|
||||||
import { routesFunctions } from "./functions";
|
|
||||||
import { routesBrowser } from "./browser";
|
|
||||||
import { routesK8s } from "./k8s";
|
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
...routesGen,
|
...routesLayers,
|
||||||
...routesMesh,
|
|
||||||
...routesFunctions,
|
|
||||||
...routesK8s,
|
|
||||||
...routesInfra,
|
|
||||||
...routesBrowser,
|
|
||||||
...routesDatabase,
|
|
||||||
...routesSelf,
|
|
||||||
...routesDashboard,
|
...routesDashboard,
|
||||||
...routesAlarm,
|
...routesAlarm,
|
||||||
...routesSetting,
|
...routesSetting,
|
||||||
|
35
src/router/layer.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
import LayerJson from "./data";
|
||||||
|
import Layout from "@/layout/Index.vue";
|
||||||
|
|
||||||
|
function layerDashboards() {
|
||||||
|
const routes = LayerJson.map((item: any) => {
|
||||||
|
item.component = Layout;
|
||||||
|
if (item.children) {
|
||||||
|
item.children = item.children.map((d: any) => {
|
||||||
|
d.component = () =>
|
||||||
|
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue");
|
||||||
|
return d;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default layerDashboards();
|
@@ -25,7 +25,6 @@ export const routesSetting: Array<RouteRecordRaw> = [
|
|||||||
title: "settings",
|
title: "settings",
|
||||||
icon: "settings",
|
icon: "settings",
|
||||||
hasGroup: false,
|
hasGroup: false,
|
||||||
exact: false,
|
|
||||||
},
|
},
|
||||||
component: Layout,
|
component: Layout,
|
||||||
children: [
|
children: [
|
||||||
@@ -36,7 +35,6 @@ export const routesSetting: Array<RouteRecordRaw> = [
|
|||||||
title: "settings",
|
title: "settings",
|
||||||
icon: "settings",
|
icon: "settings",
|
||||||
hasGroup: false,
|
hasGroup: false,
|
||||||
exact: false,
|
|
||||||
},
|
},
|
||||||
component: () =>
|
component: () =>
|
||||||
import(/* webpackChunkName: "settings" */ "@/views/Settings.vue"),
|
import(/* webpackChunkName: "settings" */ "@/views/Settings.vue"),
|
||||||
|
@@ -71,7 +71,7 @@ export const appStore = defineStore({
|
|||||||
step: this.duration.step,
|
step: this.duration.step,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
intervalTime(): string[] {
|
intervalUnix(): number[] {
|
||||||
let interval = 946080000000;
|
let interval = 946080000000;
|
||||||
switch (this.duration.step) {
|
switch (this.duration.step) {
|
||||||
case "MINUTE":
|
case "MINUTE":
|
||||||
@@ -97,12 +97,17 @@ export const appStore = defineStore({
|
|||||||
this.utcMin * 60000;
|
this.utcMin * 60000;
|
||||||
const startUnix: number = this.duration.start.getTime();
|
const startUnix: number = this.duration.start.getTime();
|
||||||
const endUnix: number = this.duration.end.getTime();
|
const endUnix: number = this.duration.end.getTime();
|
||||||
const timeIntervals: string[] = [];
|
const timeIntervals: number[] = [];
|
||||||
for (let i = 0; i <= endUnix - startUnix; i += interval) {
|
for (let i = 0; i <= endUnix - startUnix; i += interval) {
|
||||||
const temp: string = dateFormatTime(
|
timeIntervals.push(startUnix + i - utcSpace);
|
||||||
new Date(startUnix + i - utcSpace),
|
}
|
||||||
this.duration.step
|
return timeIntervals;
|
||||||
);
|
},
|
||||||
|
intervalTime(): string[] {
|
||||||
|
const arr = this.intervalUnix;
|
||||||
|
const timeIntervals: string[] = [];
|
||||||
|
for (const item of arr) {
|
||||||
|
const temp: string = dateFormatTime(new Date(item), this.duration.step);
|
||||||
timeIntervals.push(temp);
|
timeIntervals.push(temp);
|
||||||
}
|
}
|
||||||
return timeIntervals;
|
return timeIntervals;
|
||||||
|
@@ -92,25 +92,41 @@ export const networkProfilingStore = defineStore({
|
|||||||
}
|
}
|
||||||
return prev;
|
return prev;
|
||||||
}, []);
|
}, []);
|
||||||
calls = calls.map((d: any) => {
|
const param = {} as any;
|
||||||
d.sourceId = d.source;
|
calls = data.calls.reduce((prev: (Call | any)[], next: Call | any) => {
|
||||||
d.targetId = d.target;
|
if (param[next.targetId + next.sourceId]) {
|
||||||
d.source = d.sourceObj;
|
next.lowerArc = true;
|
||||||
d.target = d.targetObj;
|
}
|
||||||
delete d.sourceObj;
|
param[next.sourceId + next.targetId] = true;
|
||||||
delete d.targetObj;
|
next.sourceId = next.source;
|
||||||
return d;
|
next.targetId = next.target;
|
||||||
});
|
next.source = next.sourceObj;
|
||||||
|
next.target = next.targetObj;
|
||||||
|
delete next.sourceObj;
|
||||||
|
delete next.targetObj;
|
||||||
|
prev.push(next);
|
||||||
|
return prev;
|
||||||
|
}, []);
|
||||||
this.calls = calls;
|
this.calls = calls;
|
||||||
this.nodes = data.nodes;
|
this.nodes = data.nodes;
|
||||||
},
|
},
|
||||||
async createNetworkTask(param: {
|
async createNetworkTask(
|
||||||
serviceId: string;
|
instanceId: string,
|
||||||
serviceInstanceId: string;
|
params: {
|
||||||
}) {
|
uriRegex: string;
|
||||||
|
when4xx: string;
|
||||||
|
when5xx: string;
|
||||||
|
minDuration: number;
|
||||||
|
}[]
|
||||||
|
) {
|
||||||
const res: AxiosResponse = await graphql
|
const res: AxiosResponse = await graphql
|
||||||
.query("newNetworkProfiling")
|
.query("newNetworkProfiling")
|
||||||
.params({ request: { instanceId: param.serviceInstanceId } });
|
.params({
|
||||||
|
request: {
|
||||||
|
instanceId,
|
||||||
|
samplings: params,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (res.data.errors) {
|
if (res.data.errors) {
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -136,6 +152,10 @@ export const networkProfilingStore = defineStore({
|
|||||||
this.networkTasks = res.data.data.queryEBPFTasks || [];
|
this.networkTasks = res.data.data.queryEBPFTasks || [];
|
||||||
this.selectedNetworkTask = this.networkTasks[0] || {};
|
this.selectedNetworkTask = this.networkTasks[0] || {};
|
||||||
this.setSelectedNetworkTask(this.selectedNetworkTask);
|
this.setSelectedNetworkTask(this.selectedNetworkTask);
|
||||||
|
if (!this.networkTasks.length) {
|
||||||
|
this.nodes = [];
|
||||||
|
this.calls = [];
|
||||||
|
}
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
async keepNetworkProfiling(taskId: string) {
|
async keepNetworkProfiling(taskId: string) {
|
||||||
|
@@ -83,7 +83,7 @@ export const profileStore = defineStore({
|
|||||||
if (res.data.errors) {
|
if (res.data.errors) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
|
this.endpoints = res.data.data.pods || [];
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
async getTaskEndpoints(serviceId: string, keyword?: string) {
|
async getTaskEndpoints(serviceId: string, keyword?: string) {
|
||||||
|
@@ -23,7 +23,7 @@ import { useSelectorStore } from "@/store/modules/selectors";
|
|||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import query from "@/graphql/fetch";
|
import query from "@/graphql/fetch";
|
||||||
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
|
import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
|
|
||||||
interface MetricVal {
|
interface MetricVal {
|
||||||
|
@@ -22,6 +22,7 @@ import graphql from "@/graphql";
|
|||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
|
import { QueryOrders } from "@/views/dashboard/data";
|
||||||
|
|
||||||
interface TraceState {
|
interface TraceState {
|
||||||
services: Service[];
|
services: Service[];
|
||||||
@@ -47,7 +48,7 @@ export const traceStore = defineStore({
|
|||||||
conditions: {
|
conditions: {
|
||||||
queryDuration: useAppStoreWithOut().durationTime,
|
queryDuration: useAppStoreWithOut().durationTime,
|
||||||
traceState: "ALL",
|
traceState: "ALL",
|
||||||
queryOrder: "BY_START_TIME",
|
queryOrder: QueryOrders[0].value,
|
||||||
paging: { pageNum: 1, pageSize: 20 },
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
},
|
},
|
||||||
traceSpanLogs: [],
|
traceSpanLogs: [],
|
||||||
@@ -71,7 +72,7 @@ export const traceStore = defineStore({
|
|||||||
queryDuration: useAppStoreWithOut().durationTime,
|
queryDuration: useAppStoreWithOut().durationTime,
|
||||||
paging: { pageNum: 1, pageSize: 20 },
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
traceState: "ALL",
|
traceState: "ALL",
|
||||||
queryOrder: "BY_START_TIME",
|
queryOrder: QueryOrders[0].value,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async getServices(layer: string) {
|
async getServices(layer: string) {
|
||||||
@@ -84,6 +85,36 @@ export const traceStore = defineStore({
|
|||||||
this.services = res.data.data.services;
|
this.services = res.data.data.services;
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
async getService(serviceId: string) {
|
||||||
|
if (!serviceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res: AxiosResponse = await graphql.query("queryService").params({
|
||||||
|
serviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
async getInstance(instanceId: string) {
|
||||||
|
if (!instanceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res: AxiosResponse = await graphql.query("queryInstance").params({
|
||||||
|
instanceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
async getEndpoint(endpointId: string) {
|
||||||
|
if (!endpointId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res: AxiosResponse = await graphql.query("queryEndpoint").params({
|
||||||
|
endpointId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
async getInstances(id: string) {
|
async getInstances(id: string) {
|
||||||
const serviceId = this.selectorStore.currentService
|
const serviceId = this.selectorStore.currentService
|
||||||
? this.selectorStore.currentService.id
|
? this.selectorStore.currentService.id
|
||||||
@@ -144,7 +175,8 @@ export const traceStore = defineStore({
|
|||||||
if (res.data.errors) {
|
if (res.data.errors) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
this.setTraceSpans(res.data.data.trace.spans || []);
|
const data = res.data.data.trace.spans;
|
||||||
|
this.setTraceSpans(data || []);
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
async getSpanLogs(params: any) {
|
async getSpanLogs(params: any) {
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
import "element-plus/es/components/message/style/css";
|
import "element-plus/es/components/message/style/css";
|
||||||
import "element-plus/es/components/message-box/style/css";
|
import "element-plus/es/components/message-box/style/css";
|
||||||
import "element-plus/es/components/notification/style/css";
|
import "element-plus/es/components/notification/style/css";
|
||||||
|
import "element-plus/es/components/drawer/style/css";
|
||||||
import "./grid.scss";
|
import "./grid.scss";
|
||||||
import "./lib.scss";
|
import "./lib.scss";
|
||||||
import "./reset.scss";
|
import "./reset.scss";
|
||||||
|
@@ -173,7 +173,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scroll_bar_style::-webkit-scrollbar {
|
.scroll_bar_style::-webkit-scrollbar {
|
||||||
width: 9px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
@@ -153,6 +153,20 @@ pre {
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-switch__label * {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
@@ -167,7 +181,7 @@ div.vis-tooltip {
|
|||||||
|
|
||||||
.vis-item {
|
.vis-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 17px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vis-item.Error {
|
.vis-item.Error {
|
||||||
@@ -184,7 +198,7 @@ div.vis-tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vis-item .vis-item-content {
|
.vis-item .vis-item-content {
|
||||||
padding: 0 5px !important;
|
padding: 0 3px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vis-item.vis-selected.Error,
|
.vis-item.vis-selected.Error,
|
||||||
|
15
src/types/app.d.ts
vendored
@@ -32,3 +32,18 @@ export type Paging = {
|
|||||||
pageNum: number;
|
pageNum: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EventParams = {
|
||||||
|
componentType: string;
|
||||||
|
seriesType: string;
|
||||||
|
seriesIndex: number;
|
||||||
|
seriesName: string;
|
||||||
|
name: string;
|
||||||
|
dataIndex: number;
|
||||||
|
data: unknown;
|
||||||
|
dataType: string;
|
||||||
|
value: number | Array;
|
||||||
|
color: string;
|
||||||
|
event: any;
|
||||||
|
dataIndex: number;
|
||||||
|
};
|
||||||
|
2
src/types/components.d.ts
vendored
@@ -9,6 +9,7 @@ declare module '@vue/runtime-core' {
|
|||||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||||
@@ -24,6 +25,7 @@ declare module '@vue/runtime-core' {
|
|||||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||||
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
ElSlider: typeof import('element-plus/es')['ElSlider']
|
ElSlider: typeof import('element-plus/es')['ElSlider']
|
||||||
|
33
src/types/dashboard.d.ts
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
import { DurationTime } from "@/types/app";
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
@@ -39,7 +40,18 @@ export interface LayoutConfig {
|
|||||||
id?: string;
|
id?: string;
|
||||||
associate?: { widgetId: string }[];
|
associate?: { widgetId: string }[];
|
||||||
eventAssociate?: boolean;
|
eventAssociate?: boolean;
|
||||||
filters?: {
|
filters?: Filters;
|
||||||
|
relatedTrace?: RelatedTrace;
|
||||||
|
}
|
||||||
|
export type RelatedTrace = {
|
||||||
|
duration: DurationTime;
|
||||||
|
status: string;
|
||||||
|
queryOrder: string;
|
||||||
|
latency: boolean;
|
||||||
|
enableRelate: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Filters = {
|
||||||
dataIndex: number;
|
dataIndex: number;
|
||||||
sourceId: string;
|
sourceId: string;
|
||||||
isRange?: boolean;
|
isRange?: boolean;
|
||||||
@@ -50,8 +62,10 @@ export interface LayoutConfig {
|
|||||||
traceId?: string;
|
traceId?: string;
|
||||||
spanId?: string;
|
spanId?: string;
|
||||||
segmentId?: string;
|
segmentId?: string;
|
||||||
};
|
id?: string;
|
||||||
}
|
queryOrder?: string;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type MetricConfigOpt = {
|
export type MetricConfigOpt = {
|
||||||
unit?: string;
|
unit?: string;
|
||||||
@@ -60,6 +74,7 @@ export type MetricConfigOpt = {
|
|||||||
labelsIndex: string;
|
labelsIndex: string;
|
||||||
sortOrder: string;
|
sortOrder: string;
|
||||||
topN?: number;
|
topN?: number;
|
||||||
|
index?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface WidgetConfig {
|
export interface WidgetConfig {
|
||||||
@@ -80,6 +95,7 @@ export type GraphConfig =
|
|||||||
export interface BarConfig {
|
export interface BarConfig {
|
||||||
type?: string;
|
type?: string;
|
||||||
showBackground?: boolean;
|
showBackground?: boolean;
|
||||||
|
legend?: LegendOptions;
|
||||||
}
|
}
|
||||||
export interface LineConfig extends AreaConfig {
|
export interface LineConfig extends AreaConfig {
|
||||||
type?: string;
|
type?: string;
|
||||||
@@ -95,6 +111,7 @@ export interface LineConfig extends AreaConfig {
|
|||||||
export interface AreaConfig {
|
export interface AreaConfig {
|
||||||
type?: string;
|
type?: string;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
|
legend?: LegendOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CardConfig {
|
export interface CardConfig {
|
||||||
@@ -165,3 +182,13 @@ export type EventParams = {
|
|||||||
value: number | number[];
|
value: number | number[];
|
||||||
color: string;
|
color: string;
|
||||||
};
|
};
|
||||||
|
export type LegendOptions = {
|
||||||
|
show: boolean;
|
||||||
|
total: boolean;
|
||||||
|
min: boolean;
|
||||||
|
max: boolean;
|
||||||
|
mean: boolean;
|
||||||
|
asTable: boolean;
|
||||||
|
toTheRight: boolean;
|
||||||
|
width: number;
|
||||||
|
};
|
||||||
|
8
src/types/ebpf.d.ts
vendored
@@ -74,4 +74,12 @@ export type ProcessNode = {
|
|||||||
serviceInstanceName: string;
|
serviceInstanceName: string;
|
||||||
name: string;
|
name: string;
|
||||||
isReal: boolean;
|
isReal: boolean;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
};
|
};
|
||||||
|
export interface NetworkProfilingRequest {
|
||||||
|
uriRegex: string;
|
||||||
|
when4xx: string;
|
||||||
|
when5xx: string;
|
||||||
|
minDuration: number;
|
||||||
|
}
|
||||||
|
17
src/types/trace.d.ts
vendored
@@ -14,7 +14,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface Trace {
|
export interface Trace {
|
||||||
duration: number;
|
duration: number;
|
||||||
isError: boolean;
|
isError: boolean;
|
||||||
@@ -85,3 +84,19 @@ export class TraceTreeRef {
|
|||||||
segmentMap: Map<string, Span>;
|
segmentMap: Map<string, Span>;
|
||||||
segmentIdGroup: string[];
|
segmentIdGroup: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Instant = {
|
||||||
|
seconds: number;
|
||||||
|
nanos: number;
|
||||||
|
};
|
||||||
|
type KeyValue = {
|
||||||
|
key: string;
|
||||||
|
value: string | number;
|
||||||
|
};
|
||||||
|
export interface SpanAttachedEvent {
|
||||||
|
startTime: Instant;
|
||||||
|
endTime: Instant;
|
||||||
|
event: string;
|
||||||
|
tags: KeyValue[];
|
||||||
|
summary: KeyValue[];
|
||||||
|
}
|
||||||
|
@@ -27,7 +27,6 @@ import { useDashboardStore } from "@/store/modules/dashboard";
|
|||||||
import Dashboard from "./dashboard/Edit.vue";
|
import Dashboard from "./dashboard/Edit.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { RoutesMap } from "@/constants/data";
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -38,7 +37,7 @@ const layer = ref<string>("GENERAL");
|
|||||||
getDashboard();
|
getDashboard();
|
||||||
|
|
||||||
async function getDashboard() {
|
async function getDashboard() {
|
||||||
layer.value = RoutesMap[String(route.name)];
|
layer.value = String(route.meta.layer);
|
||||||
dashboardStore.setLayer(layer.value);
|
dashboardStore.setLayer(layer.value);
|
||||||
dashboardStore.setMode(false);
|
dashboardStore.setMode(false);
|
||||||
await dashboardStore.setDashboards();
|
await dashboardStore.setDashboards();
|
||||||
|
@@ -35,9 +35,10 @@ limitations under the License. -->
|
|||||||
size="small"
|
size="small"
|
||||||
v-model="tags"
|
v-model="tags"
|
||||||
class="trace-new-tag"
|
class="trace-new-tag"
|
||||||
@input="searchTags"
|
@input="inputTags"
|
||||||
@blur="visible = false"
|
@blur="visible = false"
|
||||||
@focus="visible = true"
|
@focus="visible = true"
|
||||||
|
@change="addTags"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -92,6 +93,7 @@ const tagsList = ref<string[]>([]);
|
|||||||
const tagArr = ref<string[]>([]);
|
const tagArr = ref<string[]>([]);
|
||||||
const tagList = ref<string[]>([]);
|
const tagList = ref<string[]>([]);
|
||||||
const tagKeys = ref<string[]>([]);
|
const tagKeys = ref<string[]>([]);
|
||||||
|
const keysList = ref<string[]>([]);
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
const tipsMap = {
|
const tipsMap = {
|
||||||
LOG: "logTagsTip",
|
LOG: "logTagsTip",
|
||||||
@@ -137,6 +139,7 @@ async function fetchTagKeys() {
|
|||||||
}
|
}
|
||||||
tagArr.value = resp.data.tagKeys;
|
tagArr.value = resp.data.tagKeys;
|
||||||
tagKeys.value = resp.data.tagKeys;
|
tagKeys.value = resp.data.tagKeys;
|
||||||
|
keysList.value = resp.data.tagKeys;
|
||||||
searchTags();
|
searchTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,13 +160,37 @@ async function fetchTagValues() {
|
|||||||
searchTags();
|
searchTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inputTags() {
|
||||||
|
if (!tags.value) {
|
||||||
|
tagArr.value = keysList.value;
|
||||||
|
tagKeys.value = keysList.value;
|
||||||
|
tagList.value = tagArr.value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let search = "";
|
||||||
|
if (tags.value.includes("=")) {
|
||||||
|
search = tags.value.split("=")[1];
|
||||||
|
fetchTagValues();
|
||||||
|
} else {
|
||||||
|
search = tags.value;
|
||||||
|
}
|
||||||
|
tagList.value = tagArr.value.filter((d: string) => d.includes(search));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTags() {
|
||||||
|
if (!tags.value.includes("=")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addLabels();
|
||||||
|
tagArr.value = tagKeys.value;
|
||||||
|
searchTags();
|
||||||
|
}
|
||||||
|
|
||||||
function selectTag(item: string) {
|
function selectTag(item: string) {
|
||||||
if (tags.value.includes("=")) {
|
if (tags.value.includes("=")) {
|
||||||
const key = tags.value.split("=")[0];
|
const key = tags.value.split("=")[0];
|
||||||
tags.value = key + "=" + item;
|
tags.value = key + "=" + item;
|
||||||
addLabels();
|
addTags();
|
||||||
tagArr.value = tagKeys.value;
|
|
||||||
searchTags();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tags.value = item + "=";
|
tags.value = item + "=";
|
||||||
@@ -171,10 +198,6 @@ function selectTag(item: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function searchTags() {
|
function searchTags() {
|
||||||
if (!tags.value) {
|
|
||||||
tagList.value = tagArr.value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let search = "";
|
let search = "";
|
||||||
if (tags.value.includes("=")) {
|
if (tags.value.includes("=")) {
|
||||||
search = tags.value.split("=")[1];
|
search = tags.value.split("=")[1];
|
||||||
|
@@ -46,7 +46,7 @@ limitations under the License. -->
|
|||||||
ref="multipleTableRef"
|
ref="multipleTableRef"
|
||||||
:default-sort="{ prop: 'name', order: 'ascending' }"
|
:default-sort="{ prop: 'name', order: 'ascending' }"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
height="637px"
|
height="calc(100% - 60px)"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="55" />
|
<el-table-column type="selection" width="55" />
|
||||||
@@ -156,7 +156,7 @@ import { isEmptyObject } from "@/utils/is";
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const pageSize = 18;
|
const pageSize = 20;
|
||||||
const dashboards = ref<DashboardItem[]>([]);
|
const dashboards = ref<DashboardItem[]>([]);
|
||||||
const searchText = ref<string>("");
|
const searchText = ref<string>("");
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
@@ -500,12 +500,13 @@ function changePage(pageIndex: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
padding: 20px;
|
padding: 20px 10px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0px 1px 4px 0px #00000029;
|
box-shadow: 0px 1px 4px 0px #00000029;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-selection {
|
.toggle-selection {
|
||||||
|
@@ -81,6 +81,10 @@ const onCreate = () => {
|
|||||||
states.entity === d.entity &&
|
states.entity === d.entity &&
|
||||||
states.selectedLayer === d.layer
|
states.selectedLayer === d.layer
|
||||||
);
|
);
|
||||||
|
if (!states.name) {
|
||||||
|
ElMessage.error(t("nameEmptyError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
ElMessage.error(t("nameError"));
|
ElMessage.error(t("nameError"));
|
||||||
return;
|
return;
|
||||||
|
@@ -32,10 +32,12 @@ limitations under the License. -->
|
|||||||
:data="states.source"
|
:data="states.source"
|
||||||
:config="{
|
:config="{
|
||||||
...graph,
|
...graph,
|
||||||
|
legend: (dashboardStore.selectedGrid.graph || {}).legend,
|
||||||
i: dashboardStore.selectedGrid.i,
|
i: dashboardStore.selectedGrid.i,
|
||||||
metrics: dashboardStore.selectedGrid.metrics,
|
metrics: dashboardStore.selectedGrid.metrics,
|
||||||
metricTypes: dashboardStore.selectedGrid.metricTypes,
|
metricTypes: dashboardStore.selectedGrid.metricTypes,
|
||||||
metricConfig: dashboardStore.selectedGrid.metricConfig,
|
metricConfig: dashboardStore.selectedGrid.metricConfig,
|
||||||
|
relatedTrace: dashboardStore.selectedGrid.relatedTrace,
|
||||||
}"
|
}"
|
||||||
:needQuery="true"
|
:needQuery="true"
|
||||||
/>
|
/>
|
||||||
@@ -65,6 +67,13 @@ limitations under the License. -->
|
|||||||
>
|
>
|
||||||
<AssociateOptions />
|
<AssociateOptions />
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
:title="t('relatedTraceOptions')"
|
||||||
|
name="5"
|
||||||
|
v-if="hasAssociate"
|
||||||
|
>
|
||||||
|
<RelatedTraceOptions />
|
||||||
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
@@ -93,7 +102,7 @@ export default defineComponent({
|
|||||||
...CustomOptions,
|
...CustomOptions,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const configHeight = document.documentElement.clientHeight - 520;
|
const configHeight = document.documentElement.clientHeight - 540;
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const appStoreWithOut = useAppStoreWithOut();
|
const appStoreWithOut = useAppStoreWithOut();
|
||||||
@@ -115,7 +124,7 @@ export default defineComponent({
|
|||||||
const title = computed(() => encodeURIComponent(widget.value.title || ""));
|
const title = computed(() => encodeURIComponent(widget.value.title || ""));
|
||||||
const tips = computed(() => encodeURIComponent(widget.value.tips || ""));
|
const tips = computed(() => encodeURIComponent(widget.value.tips || ""));
|
||||||
const hasAssociate = computed(() =>
|
const hasAssociate = computed(() =>
|
||||||
["Bar", "Line", "Area"].includes(
|
["Bar", "Line", "Area", "TopList"].includes(
|
||||||
dashboardStore.selectedGrid.graph &&
|
dashboardStore.selectedGrid.graph &&
|
||||||
dashboardStore.selectedGrid.graph.type
|
dashboardStore.selectedGrid.graph.type
|
||||||
)
|
)
|
||||||
|
@@ -23,6 +23,7 @@ limitations under the License. -->
|
|||||||
placeholder="Select a widget"
|
placeholder="Select a widget"
|
||||||
class="selectors"
|
class="selectors"
|
||||||
@change="updateWidgetConfig"
|
@change="updateWidgetConfig"
|
||||||
|
:filterable="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
119
src/views/dashboard/configuration/widget/RelatedTraceOptions.vue
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<template>
|
||||||
|
<div v-if="type === 'TopList'" class="item">
|
||||||
|
<span class="label">RefId</span>
|
||||||
|
<Selector
|
||||||
|
size="small"
|
||||||
|
:value="refIdType"
|
||||||
|
:options="RefIdTypes"
|
||||||
|
placeholder="Select a refId"
|
||||||
|
@change="updateConfig({ refIdType: $event[0].value })"
|
||||||
|
class="selector"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else class="item">
|
||||||
|
<span class="label">{{ t("enableRelatedTrace") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="enableRelate"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateConfig({ enableRelate })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="enableRelate">
|
||||||
|
<div class="item">
|
||||||
|
<span class="label">{{ t("status") }}</span>
|
||||||
|
<Selector
|
||||||
|
size="small"
|
||||||
|
:value="status"
|
||||||
|
:options="Status"
|
||||||
|
placeholder="Select a status"
|
||||||
|
@change="updateConfig({ status: $event[0].value })"
|
||||||
|
class="selector"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="label">{{ t("setOrder") }}</span>
|
||||||
|
<Selector
|
||||||
|
size="small"
|
||||||
|
:value="queryOrder"
|
||||||
|
:options="QueryOrders"
|
||||||
|
placeholder="Select a option"
|
||||||
|
class="selector"
|
||||||
|
@change="updateConfig({ queryOrder: $event[0].value })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="label">{{ t("setLatencyDuration") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="latency"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateConfig({ latency })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import { RefIdTypes } from "../../data";
|
||||||
|
|
||||||
|
const QueryOrders = [
|
||||||
|
{ label: "None", value: "BY_START_TIME" },
|
||||||
|
{ label: "Duration", value: "BY_DURATION" },
|
||||||
|
];
|
||||||
|
const Status = [
|
||||||
|
{ label: "None", value: "ALL" },
|
||||||
|
{ label: "Success", value: "SUCCESS" },
|
||||||
|
{ label: "Error", value: "ERROR" },
|
||||||
|
];
|
||||||
|
const { t } = useI18n();
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const { graph, relatedTrace } = dashboardStore.selectedGrid;
|
||||||
|
const traceOpt = relatedTrace || {};
|
||||||
|
const status = ref<string>(traceOpt.status || Status[0].value);
|
||||||
|
const queryOrder = ref<string>(traceOpt.queryOrder || QueryOrders[0].value);
|
||||||
|
const latency = ref<boolean>(traceOpt.latency || false);
|
||||||
|
const enableRelate = ref<boolean>(traceOpt.enableRelate || false);
|
||||||
|
const type = ref<string>((graph && graph.type) || "");
|
||||||
|
const refIdType = ref<string>(traceOpt.refIdType || "traceId");
|
||||||
|
|
||||||
|
function updateConfig(param: { [key: string]: unknown }) {
|
||||||
|
const relatedTrace = {
|
||||||
|
...dashboardStore.selectedGrid.relatedTrace,
|
||||||
|
...param,
|
||||||
|
};
|
||||||
|
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, relatedTrace });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
|
<Legend />
|
||||||
<div>
|
<div>
|
||||||
<span class="label">{{ t("areaOpacity") }}</span>
|
<span class="label">{{ t("areaOpacity") }}</span>
|
||||||
<el-slider
|
<el-slider
|
||||||
@@ -31,6 +32,7 @@ limitations under the License. -->
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import Legend from "./components/Legend.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
|
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
|
<Legend />
|
||||||
<div>
|
<div>
|
||||||
<span class="label">{{ t("showBackground") }}</span>
|
<span class="label">{{ t("showBackground") }}</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
@@ -27,6 +28,7 @@ limitations under the License. -->
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import Legend from "./components/Legend.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
|
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
|
<Legend />
|
||||||
<div>
|
<div>
|
||||||
<span class="label">{{ t("showXAxis") }}</span>
|
<span class="label">{{ t("showXAxis") }}</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
@@ -63,6 +64,7 @@ limitations under the License. -->
|
|||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import Legend from "./components/Legend.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
@@ -82,8 +84,8 @@ function updateConfig(param: { [key: string]: unknown }) {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.label {
|
.label {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 5px;
|
margin-top: 5px;
|
||||||
|
margin-bottom: -5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -0,0 +1,138 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<span class="label mr-5">{{ t("showLegend") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.show"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ show: legend.show })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="label">{{ t("asTable") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.asTable"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ asTable: legend.asTable })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-show="legend.asTable">
|
||||||
|
<span class="label">{{ t("legendOptions") }}</span>
|
||||||
|
<span class="title mr-5">{{ t("toTheRight") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.toTheRight"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ toTheRight: legend.toTheRight })"
|
||||||
|
/>
|
||||||
|
<span class="title ml-20 mr-5">{{ t("width") }}</span>
|
||||||
|
<el-input
|
||||||
|
v-model="legend.width"
|
||||||
|
class="inputs"
|
||||||
|
size="small"
|
||||||
|
placeholder="Please input the width"
|
||||||
|
@change="updateLegendConfig({ width: legend.width })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-show="legend.asTable">
|
||||||
|
<span class="label">{{ t("legendValues") }}</span>
|
||||||
|
<span class="title mr-5">{{ t("min") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.min"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ min: legend.min })"
|
||||||
|
/>
|
||||||
|
<span class="title ml-20 mr-5">{{ t("max") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.max"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ max: legend.max })"
|
||||||
|
/>
|
||||||
|
<span class="title ml-20 mr-5">{{ t("mean") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.mean"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ mean: legend.mean })"
|
||||||
|
/>
|
||||||
|
<span class="title ml-20 mr-5">{{ t("total") }}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="legend.total"
|
||||||
|
active-text="Yes"
|
||||||
|
inactive-text="No"
|
||||||
|
@change="updateLegendConfig({ total: legend.total })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, reactive } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import { LegendOptions } from "@/types/dashboard";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const graph = computed(() => dashboardStore.selectedGrid.graph || {});
|
||||||
|
const legend = reactive<LegendOptions>({
|
||||||
|
show: true,
|
||||||
|
total: false,
|
||||||
|
min: false,
|
||||||
|
max: false,
|
||||||
|
mean: false,
|
||||||
|
asTable: false,
|
||||||
|
toTheRight: false,
|
||||||
|
width: 130,
|
||||||
|
...graph.value.legend,
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateLegendConfig(param: { [key: string]: unknown }) {
|
||||||
|
const g = {
|
||||||
|
...dashboardStore.selectedGrid.graph,
|
||||||
|
legend: {
|
||||||
|
...dashboardStore.selectedGrid.graph.legend,
|
||||||
|
...param,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
dashboardStore.selectWidget({
|
||||||
|
...dashboardStore.selectedGrid,
|
||||||
|
graph: g,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.label {
|
||||||
|
font-size: 13px;
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 12px;
|
||||||
|
display: inline-flex;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 34px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -19,10 +19,12 @@ import StyleOptions from "./graph-styles";
|
|||||||
import WidgetOptions from "./WidgetOptions.vue";
|
import WidgetOptions from "./WidgetOptions.vue";
|
||||||
import MetricOptions from "./metric/Index.vue";
|
import MetricOptions from "./metric/Index.vue";
|
||||||
import AssociateOptions from "./AssociateOptions.vue";
|
import AssociateOptions from "./AssociateOptions.vue";
|
||||||
|
import RelatedTraceOptions from "./RelatedTraceOptions.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...StyleOptions,
|
...StyleOptions,
|
||||||
WidgetOptions,
|
WidgetOptions,
|
||||||
MetricOptions,
|
MetricOptions,
|
||||||
AssociateOptions,
|
AssociateOptions,
|
||||||
|
RelatedTraceOptions,
|
||||||
};
|
};
|
||||||
|
@@ -60,7 +60,10 @@ limitations under the License. -->
|
|||||||
/>
|
/>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<span
|
<span
|
||||||
v-show="states.isList || states.metricTypes[0] === 'readMetricsValues'"
|
v-show="
|
||||||
|
states.isList ||
|
||||||
|
states.metricTypes[0] === ProtocolTypes.ReadMetricsValues
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
class="cp mr-5"
|
class="cp mr-5"
|
||||||
@@ -106,6 +109,7 @@ import {
|
|||||||
ChartTypes,
|
ChartTypes,
|
||||||
PodsChartTypes,
|
PodsChartTypes,
|
||||||
MetricsType,
|
MetricsType,
|
||||||
|
ProtocolTypes,
|
||||||
} from "../../../data";
|
} from "../../../data";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import Icon from "@/components/Icon.vue";
|
import Icon from "@/components/Icon.vue";
|
||||||
@@ -113,7 +117,7 @@ import {
|
|||||||
useQueryProcessor,
|
useQueryProcessor,
|
||||||
useSourceProcessor,
|
useSourceProcessor,
|
||||||
useGetMetricEntity,
|
useGetMetricEntity,
|
||||||
} from "@/hooks/useProcessor";
|
} from "@/hooks/useMetricsProcessor";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
|
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
|
||||||
import Standard from "./Standard.vue";
|
import Standard from "./Standard.vue";
|
||||||
@@ -191,7 +195,10 @@ async function setMetricType(chart?: any) {
|
|||||||
states.metricList = (arr || []).filter(
|
states.metricList = (arr || []).filter(
|
||||||
(d: { catalog: string; type: string }) => {
|
(d: { catalog: string; type: string }) => {
|
||||||
if (states.isList) {
|
if (states.isList) {
|
||||||
if (d.type === MetricsType.REGULAR_VALUE) {
|
if (
|
||||||
|
d.type === MetricsType.REGULAR_VALUE ||
|
||||||
|
d.type === MetricsType.LABELED_VALUE
|
||||||
|
) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
} else if (g.type === "Table") {
|
} else if (g.type === "Table") {
|
||||||
@@ -239,7 +246,10 @@ async function setMetricType(chart?: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setDashboards(type?: string) {
|
function setDashboards(type?: string) {
|
||||||
const chart = type || dashboardStore.selectedGrid.graph || {};
|
const chart =
|
||||||
|
type ||
|
||||||
|
(dashboardStore.selectedGrid.graph &&
|
||||||
|
dashboardStore.selectedGrid.graph.type);
|
||||||
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
||||||
const arr = list.reduce(
|
const arr = list.reduce(
|
||||||
(
|
(
|
||||||
@@ -248,9 +258,9 @@ function setDashboards(type?: string) {
|
|||||||
) => {
|
) => {
|
||||||
if (d.layer === dashboardStore.layerId) {
|
if (d.layer === dashboardStore.layerId) {
|
||||||
if (
|
if (
|
||||||
(d.entity === EntityType[0].value && chart.type === "ServiceList") ||
|
(d.entity === EntityType[0].value && chart === "ServiceList") ||
|
||||||
(d.entity === EntityType[2].value && chart.type === "EndpointList") ||
|
(d.entity === EntityType[2].value && chart === "EndpointList") ||
|
||||||
(d.entity === EntityType[3].value && chart.type === "InstanceList")
|
(d.entity === EntityType[3].value && chart === "InstanceList")
|
||||||
) {
|
) {
|
||||||
prev.push({
|
prev.push({
|
||||||
...d,
|
...d,
|
||||||
@@ -419,13 +429,7 @@ function setMetricTypeList(type: string) {
|
|||||||
return MetricTypes[type];
|
return MetricTypes[type];
|
||||||
}
|
}
|
||||||
if (states.isList || graph.value.type === "Table") {
|
if (states.isList || graph.value.type === "Table") {
|
||||||
return [
|
return [MetricTypes.REGULAR_VALUE[0], MetricTypes.REGULAR_VALUE[1]];
|
||||||
{ label: "read all values in the duration", value: "readMetricsValues" },
|
|
||||||
{
|
|
||||||
label: "read the single value in the duration",
|
|
||||||
value: "readMetricsValue",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
return MetricTypes[type];
|
return MetricTypes[type];
|
||||||
}
|
}
|
||||||
|
@@ -97,7 +97,7 @@ import { useI18n } from "vue-i18n";
|
|||||||
import { SortOrder, CalculationOpts } from "../../../data";
|
import { SortOrder, CalculationOpts } from "../../../data";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { MetricConfigOpt } from "@/types/dashboard";
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
import { ListChartTypes } from "../../../data";
|
import { ListChartTypes, ProtocolTypes } from "../../../data";
|
||||||
|
|
||||||
/*global defineEmits, defineProps */
|
/*global defineEmits, defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -115,16 +115,25 @@ const currentMetric = ref<MetricConfigOpt>({
|
|||||||
topN: props.currentMetricConfig.topN || 10,
|
topN: props.currentMetricConfig.topN || 10,
|
||||||
});
|
});
|
||||||
const metricTypes = dashboardStore.selectedGrid.metricTypes || [];
|
const metricTypes = dashboardStore.selectedGrid.metricTypes || [];
|
||||||
const metricType = ref<string>(metricTypes[props.index]);
|
const metricType = computed(
|
||||||
|
() => (dashboardStore.selectedGrid.metricTypes || [])[props.index]
|
||||||
|
);
|
||||||
const hasLabel = computed(() => {
|
const hasLabel = computed(() => {
|
||||||
const graph = dashboardStore.selectedGrid.graph || {};
|
const graph = dashboardStore.selectedGrid.graph || {};
|
||||||
return (
|
return (
|
||||||
ListChartTypes.includes(graph.type) ||
|
ListChartTypes.includes(graph.type) ||
|
||||||
metricType.value === "readLabeledMetricsValues"
|
[
|
||||||
|
ProtocolTypes.ReadLabeledMetricsValues,
|
||||||
|
ProtocolTypes.ReadMetricsValues,
|
||||||
|
].includes(metricType.value)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const isTopn = computed(() =>
|
const isTopn = computed(() =>
|
||||||
["sortMetrics", "readSampledRecords"].includes(metricTypes[props.index])
|
[
|
||||||
|
ProtocolTypes.SortMetrics,
|
||||||
|
ProtocolTypes.ReadSampledRecords,
|
||||||
|
ProtocolTypes.ReadRecords,
|
||||||
|
].includes(metricTypes[props.index])
|
||||||
);
|
);
|
||||||
function updateConfig(index: number, param: { [key: string]: string }) {
|
function updateConfig(index: number, param: { [key: string]: string }) {
|
||||||
const key = Object.keys(param)[0];
|
const key = Object.keys(param)[0];
|
||||||
|
@@ -40,7 +40,7 @@ limitations under the License. -->
|
|||||||
<span class="tab-icons">
|
<span class="tab-icons">
|
||||||
<el-tooltip content="Copy Link" placement="bottom">
|
<el-tooltip content="Copy Link" placement="bottom">
|
||||||
<i @click="copyLink">
|
<i @click="copyLink">
|
||||||
<Icon size="middle" iconName="review-list" class="tab-icon" />
|
<Icon size="middle" iconName="copy" class="tab-icon" />
|
||||||
</i>
|
</i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</span>
|
</span>
|
||||||
@@ -194,6 +194,7 @@ export default defineComponent({
|
|||||||
editTabIndex.value = index;
|
editTabIndex.value = index;
|
||||||
}
|
}
|
||||||
function handleClick(el: any) {
|
function handleClick(el: any) {
|
||||||
|
el.stopPropagation();
|
||||||
needQuery.value = true;
|
needQuery.value = true;
|
||||||
if (["tab-name", "edit-tab"].includes(el.target.className)) {
|
if (["tab-name", "edit-tab"].includes(el.target.className)) {
|
||||||
return;
|
return;
|
||||||
|
@@ -62,8 +62,10 @@ limitations under the License. -->
|
|||||||
metricTypes: data.metricTypes || [''],
|
metricTypes: data.metricTypes || [''],
|
||||||
i: data.i,
|
i: data.i,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
metricConfig: data.metricConfig,
|
metricConfig: data.metricConfig || [],
|
||||||
filters: data.filters || {},
|
filters: data.filters || {},
|
||||||
|
relatedTrace: data.relatedTrace || {},
|
||||||
|
associate: data.associate || [],
|
||||||
}"
|
}"
|
||||||
:needQuery="needQuery"
|
:needQuery="needQuery"
|
||||||
@click="clickHandle"
|
@click="clickHandle"
|
||||||
@@ -85,7 +87,7 @@ import {
|
|||||||
useQueryProcessor,
|
useQueryProcessor,
|
||||||
useSourceProcessor,
|
useSourceProcessor,
|
||||||
useGetMetricEntity,
|
useGetMetricEntity,
|
||||||
} from "@/hooks/useProcessor";
|
} from "@/hooks/useMetricsProcessor";
|
||||||
import { EntityType, ListChartTypes } from "../data";
|
import { EntityType, ListChartTypes } from "../data";
|
||||||
import { EventParams } from "@/types/dashboard";
|
import { EventParams } from "@/types/dashboard";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
@@ -228,6 +230,11 @@ export default defineComponent({
|
|||||||
watch(
|
watch(
|
||||||
() => [selectorStore.currentProcess, selectorStore.currentDestProcess],
|
() => [selectorStore.currentProcess, selectorStore.currentDestProcess],
|
||||||
() => {
|
() => {
|
||||||
|
if (
|
||||||
|
!(selectorStore.currentDestProcess && selectorStore.currentProcess)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (dashboardStore.entity === EntityType[7].value) {
|
if (dashboardStore.entity === EntityType[7].value) {
|
||||||
queryMetrics();
|
queryMetrics();
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,17 @@ export const MetricChartType: any = {
|
|||||||
readLabeledMetricsValues: [{ label: "Line", value: "Line" }],
|
readLabeledMetricsValues: [{ label: "Line", value: "Line" }],
|
||||||
readHeatMap: [{ label: "Heat Map", value: "HeatMap" }],
|
readHeatMap: [{ label: "Heat Map", value: "HeatMap" }],
|
||||||
readSampledRecords: [{ label: "Top List", value: "TopList" }],
|
readSampledRecords: [{ label: "Top List", value: "TopList" }],
|
||||||
|
readRecords: [{ label: "Top List", value: "TopList" }],
|
||||||
};
|
};
|
||||||
|
export enum ProtocolTypes {
|
||||||
|
ReadRecords = "readRecords",
|
||||||
|
ReadSampledRecords = "readSampledRecords",
|
||||||
|
SortMetrics = "sortMetrics",
|
||||||
|
ReadLabeledMetricsValues = "readLabeledMetricsValues",
|
||||||
|
ReadHeatMap = "readHeatMap",
|
||||||
|
ReadMetricsValues = "readMetricsValues",
|
||||||
|
ReadMetricsValue = "readMetricsValue",
|
||||||
|
}
|
||||||
export const DefaultGraphConfig: { [key: string]: any } = {
|
export const DefaultGraphConfig: { [key: string]: any } = {
|
||||||
Bar: {
|
Bar: {
|
||||||
type: "Bar",
|
type: "Bar",
|
||||||
@@ -133,9 +143,7 @@ export const MetricTypes: {
|
|||||||
HEATMAP: [
|
HEATMAP: [
|
||||||
{ label: "read heatmap values in the duration", value: "readHeatMap" },
|
{ label: "read heatmap values in the duration", value: "readHeatMap" },
|
||||||
],
|
],
|
||||||
SAMPLED_RECORD: [
|
SAMPLED_RECORD: [{ label: "get sorted topN values", value: "readRecords" }],
|
||||||
{ label: "get sorted topN values", value: "readSampledRecords" },
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum MetricCatalog {
|
export enum MetricCatalog {
|
||||||
@@ -146,6 +154,7 @@ export enum MetricCatalog {
|
|||||||
SERVICE_RELATION = "ServiceRelation",
|
SERVICE_RELATION = "ServiceRelation",
|
||||||
SERVICE_INSTANCE_RELATION = "ServiceInstanceRelation",
|
SERVICE_INSTANCE_RELATION = "ServiceInstanceRelation",
|
||||||
ENDPOINT_RELATION = "EndpointRelation",
|
ENDPOINT_RELATION = "EndpointRelation",
|
||||||
|
PROCESS_RELATION = "ProcessRelation",
|
||||||
}
|
}
|
||||||
export const EntityType = [
|
export const EntityType = [
|
||||||
{ value: "Service", label: "Service", key: 1 },
|
{ value: "Service", label: "Service", key: 1 },
|
||||||
@@ -172,7 +181,7 @@ export const SortOrder = [
|
|||||||
];
|
];
|
||||||
export const AllTools = [
|
export const AllTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
@@ -180,7 +189,7 @@ export const AllTools = [
|
|||||||
];
|
];
|
||||||
export const ServiceTools = [
|
export const ServiceTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
@@ -192,7 +201,7 @@ export const ServiceTools = [
|
|||||||
];
|
];
|
||||||
export const InstanceTools = [
|
export const InstanceTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
{ name: "assignment", content: "Add Log", id: "addLog" },
|
{ name: "assignment", content: "Add Log", id: "addLog" },
|
||||||
@@ -206,7 +215,7 @@ export const InstanceTools = [
|
|||||||
];
|
];
|
||||||
export const EndpointTools = [
|
export const EndpointTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
{ name: "merge", content: "Add Trace", id: "addTrace" },
|
||||||
@@ -215,25 +224,25 @@ export const EndpointTools = [
|
|||||||
];
|
];
|
||||||
export const ProcessTools = [
|
export const ProcessTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "time_range", content: "Add Time Range Text", id: "addTimeRange" },
|
{ name: "time_range", content: "Add Time Range Text", id: "addTimeRange" },
|
||||||
];
|
];
|
||||||
export const ServiceRelationTools = [
|
export const ServiceRelationTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EndpointRelationTools = [
|
export const EndpointRelationTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
];
|
];
|
||||||
export const InstanceRelationTools = [
|
export const InstanceRelationTools = [
|
||||||
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
{ name: "playlist_add", content: "Add Widget", id: "addWidget" },
|
||||||
{ name: "all_inbox", content: "Add Tab", id: "addTab" },
|
{ name: "all_inbox", content: "Add Tabs", id: "addTab" },
|
||||||
{ name: "library_books", content: "Add Text", id: "addText" },
|
{ name: "library_books", content: "Add Text", id: "addText" },
|
||||||
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
{ name: "device_hub", content: "Add Topology", id: "addTopology" },
|
||||||
];
|
];
|
||||||
@@ -299,3 +308,7 @@ export const CalculationOpts = [
|
|||||||
{ label: "Seconds to days", value: "secondToDay" },
|
{ label: "Seconds to days", value: "secondToDay" },
|
||||||
{ label: "Nanoseconds to milliseconds", value: "nanosecondToMillisecond" },
|
{ label: "Nanoseconds to milliseconds", value: "nanosecondToMillisecond" },
|
||||||
];
|
];
|
||||||
|
export const RefIdTypes = [
|
||||||
|
{ label: "Trace ID", value: "traceId" },
|
||||||
|
{ label: "None", value: "none" },
|
||||||
|
];
|
||||||
|
@@ -24,7 +24,12 @@ limitations under the License. -->
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import Line from "./Line.vue";
|
import Line from "./Line.vue";
|
||||||
import { AreaConfig, EventParams } from "@/types/dashboard";
|
import {
|
||||||
|
AreaConfig,
|
||||||
|
EventParams,
|
||||||
|
RelatedTrace,
|
||||||
|
Filters,
|
||||||
|
} from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
const emits = defineEmits(["click"]);
|
const emits = defineEmits(["click"]);
|
||||||
@@ -37,16 +42,11 @@ defineProps({
|
|||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<
|
||||||
AreaConfig & {
|
AreaConfig & {
|
||||||
filters: {
|
filters: Filters;
|
||||||
sourceId: string;
|
relatedTrace: RelatedTrace;
|
||||||
duration: {
|
id: string;
|
||||||
startTime: string;
|
associate: { widgetId: string }[];
|
||||||
endTime: string;
|
}
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
};
|
|
||||||
} & { id: string }
|
|
||||||
>,
|
>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
@@ -13,12 +13,26 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
<div class="graph" :class="isRight ? 'flex-h' : 'flex-v'">
|
||||||
|
<Graph
|
||||||
|
:option="option"
|
||||||
|
@select="clickEvent"
|
||||||
|
:filters="config.filters"
|
||||||
|
:associate="config.associate || []"
|
||||||
|
/>
|
||||||
|
<Legend :config="config.legend" :data="data" :intervalTime="intervalTime" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { BarConfig, EventParams } from "@/types/dashboard";
|
import {
|
||||||
|
BarConfig,
|
||||||
|
EventParams,
|
||||||
|
RelatedTrace,
|
||||||
|
Filters,
|
||||||
|
} from "@/types/dashboard";
|
||||||
|
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
const emits = defineEmits(["click"]);
|
const emits = defineEmits(["click"]);
|
||||||
@@ -32,20 +46,18 @@ const props = defineProps({
|
|||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<
|
||||||
BarConfig & {
|
BarConfig & {
|
||||||
filters: {
|
filters: Filters;
|
||||||
sourceId: string;
|
relatedTrace: RelatedTrace;
|
||||||
duration: {
|
id: string;
|
||||||
startTime: string;
|
associate: { widgetId: string }[];
|
||||||
endTime: string;
|
}
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
};
|
|
||||||
} & { id: string }
|
|
||||||
>,
|
>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(
|
||||||
|
props.config.legend
|
||||||
|
);
|
||||||
const option = computed(() => getOption());
|
const option = computed(() => getOption());
|
||||||
|
|
||||||
function getOption() {
|
function getOption() {
|
||||||
@@ -75,52 +87,20 @@ function getOption() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
let color: string[] = [];
|
const color: string[] = chartColors(keys);
|
||||||
switch (keys.length) {
|
|
||||||
case 2:
|
|
||||||
color = ["#FF6A84", "#a0b1e6"];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
color = ["#3f96e3"];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = [
|
|
||||||
"#30A4EB",
|
|
||||||
"#45BFC0",
|
|
||||||
"#FFCC55",
|
|
||||||
"#FF6A84",
|
|
||||||
"#a0a7e6",
|
|
||||||
"#c23531",
|
|
||||||
"#2f4554",
|
|
||||||
"#61a0a8",
|
|
||||||
"#d48265",
|
|
||||||
"#91c7ae",
|
|
||||||
"#749f83",
|
|
||||||
"#ca8622",
|
|
||||||
"#bda29a",
|
|
||||||
"#6e7074",
|
|
||||||
"#546570",
|
|
||||||
"#c4ccd3",
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
color,
|
color,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "none",
|
||||||
zlevel: 1000,
|
axisPointer: {
|
||||||
z: 60,
|
type: "cross",
|
||||||
confine: true,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: 13,
|
|
||||||
color: "#333",
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
},
|
},
|
||||||
enterable: true,
|
|
||||||
extraCssText: "max-height: 300px; overflow: auto; border: none",
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
type: "scroll",
|
type: "scroll",
|
||||||
show: keys.length === 1 ? false : true,
|
show: showEchartsLegend(keys),
|
||||||
icon: "circle",
|
icon: "circle",
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -136,6 +116,12 @@ function getOption() {
|
|||||||
bottom: 5,
|
bottom: 5,
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
|
axisPointer: {
|
||||||
|
label: {
|
||||||
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: "category",
|
||||||
axisTick: {
|
axisTick: {
|
||||||
@@ -160,3 +146,9 @@ function clickEvent(params: EventParams) {
|
|||||||
emits("click", params);
|
emits("click", params);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.graph {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -17,21 +17,21 @@ limitations under the License. -->
|
|||||||
<div class="search">
|
<div class="search">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
placeholder="Please input endpoint name"
|
placeholder="Search for more endpoints"
|
||||||
size="small"
|
|
||||||
@change="searchList"
|
@change="searchList"
|
||||||
class="inputs"
|
class="inputs"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button size="small" @click="searchList">
|
<el-button @click="searchList" class="btn">
|
||||||
<Icon size="sm" iconName="search" />
|
<Icon size="middle" iconName="search" />
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
<span class="ml-5 tips">{{ t("endpointTips") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
|
<el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
|
||||||
<el-table-column label="Endpoints">
|
<el-table-column label="Endpoints" fixed min-width="220">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
@@ -45,7 +45,12 @@ limitations under the License. -->
|
|||||||
<ColumnGraph
|
<ColumnGraph
|
||||||
:intervalTime="intervalTime"
|
:intervalTime="intervalTime"
|
||||||
:colMetrics="colMetrics"
|
:colMetrics="colMetrics"
|
||||||
:config="config"
|
:config="{
|
||||||
|
...config,
|
||||||
|
metrics: colMetrics,
|
||||||
|
metricConfig,
|
||||||
|
metricTypes,
|
||||||
|
}"
|
||||||
v-if="colMetrics.length"
|
v-if="colMetrics.length"
|
||||||
/>
|
/>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -53,14 +58,18 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, computed } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { EndpointListConfig } from "@/types/dashboard";
|
import { EndpointListConfig } from "@/types/dashboard";
|
||||||
import { Endpoint } from "@/types/selector";
|
import { Endpoint } from "@/types/selector";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
|
import {
|
||||||
|
useQueryPodsMetrics,
|
||||||
|
usePodsSource,
|
||||||
|
} from "@/hooks/useMetricsProcessor";
|
||||||
import { EntityType } from "../data";
|
import { EntityType } from "../data";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
@@ -92,14 +101,15 @@ const props = defineProps({
|
|||||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const selectorStore = useSelectorStore();
|
const selectorStore = useSelectorStore();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const chartLoading = ref<boolean>(false);
|
const chartLoading = ref<boolean>(false);
|
||||||
const endpoints = ref<Endpoint[]>([]);
|
const endpoints = ref<Endpoint[]>([]);
|
||||||
const searchText = ref<string>("");
|
const searchText = ref<string>("");
|
||||||
const colMetrics = computed(() =>
|
const colMetrics = ref<string[]>([]);
|
||||||
(props.config.metrics || []).filter((d: string) => d)
|
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||||
);
|
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||||
|
|
||||||
if (props.needQuery) {
|
if (props.needQuery) {
|
||||||
queryEndpoints();
|
queryEndpoints();
|
||||||
@@ -123,8 +133,8 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metrics = props.config.metrics || [];
|
const metrics = props.config.metrics || [];
|
||||||
const metricTypes = props.config.metricTypes || [];
|
const types = props.config.metricTypes || [];
|
||||||
if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
|
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||||
const params = await useQueryPodsMetrics(
|
const params = await useQueryPodsMetrics(
|
||||||
currentPods,
|
currentPods,
|
||||||
props.config,
|
props.config,
|
||||||
@@ -136,12 +146,18 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metricConfig = props.config.metricConfig || [];
|
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
|
||||||
|
currentPods,
|
||||||
endpoints.value = usePodsSource(currentPods, json, {
|
json,
|
||||||
|
{
|
||||||
...props.config,
|
...props.config,
|
||||||
metricConfig: metricConfig,
|
metricConfig: metricConfig.value,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
endpoints.value = data;
|
||||||
|
colMetrics.value = names;
|
||||||
|
metricTypes.value = metricTypesArr;
|
||||||
|
metricConfig.value = metricConfigArr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
endpoints.value = currentPods;
|
endpoints.value = currentPods;
|
||||||
@@ -164,11 +180,16 @@ async function searchList() {
|
|||||||
await queryEndpoints();
|
await queryEndpoints();
|
||||||
}
|
}
|
||||||
watch(
|
watch(
|
||||||
() => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
|
() => [
|
||||||
|
...(props.config.metricTypes || []),
|
||||||
|
...(props.config.metrics || []),
|
||||||
|
...(props.config.metricConfig || []),
|
||||||
|
],
|
||||||
(data, old) => {
|
(data, old) => {
|
||||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
metricConfig.value = props.config.metricConfig;
|
||||||
queryEndpointMetrics(endpoints.value);
|
queryEndpointMetrics(endpoints.value);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -178,24 +199,11 @@ watch(
|
|||||||
queryEndpoints();
|
queryEndpoints();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
watch(
|
|
||||||
() => [...(props.config.metricConfig || [])],
|
|
||||||
(data, old) => {
|
|
||||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
queryEndpointMetrics(endpoints.value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./style.scss";
|
@import "./style.scss";
|
||||||
|
|
||||||
.chart {
|
.tips {
|
||||||
height: 60px;
|
color: rgba(255, 0, 0, 0.5);
|
||||||
}
|
|
||||||
|
|
||||||
.inputs {
|
|
||||||
width: 300px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -18,12 +18,11 @@ limitations under the License. -->
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
placeholder="Please input instance name"
|
placeholder="Please input instance name"
|
||||||
size="small"
|
|
||||||
@change="searchList"
|
@change="searchList"
|
||||||
class="inputs"
|
class="inputs"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button size="small" @click="searchList">
|
<el-button class="btn" @click="searchList">
|
||||||
<Icon size="sm" iconName="search" />
|
<Icon size="sm" iconName="search" />
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -31,7 +30,7 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<el-table v-loading="chartLoading" :data="instances" style="width: 100%">
|
<el-table v-loading="chartLoading" :data="instances" style="width: 100%">
|
||||||
<el-table-column label="Service Instances">
|
<el-table-column label="Service Instances" fixed min-width="320">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
@@ -43,12 +42,17 @@ limitations under the License. -->
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<ColumnGraph
|
<ColumnGraph
|
||||||
v-if="colMetrics.length"
|
|
||||||
:intervalTime="intervalTime"
|
:intervalTime="intervalTime"
|
||||||
:colMetrics="colMetrics"
|
:colMetrics="colMetrics"
|
||||||
:config="config"
|
:config="{
|
||||||
|
...config,
|
||||||
|
metrics: colMetrics,
|
||||||
|
metricConfig,
|
||||||
|
metricTypes,
|
||||||
|
}"
|
||||||
|
v-if="colMetrics.length"
|
||||||
/>
|
/>
|
||||||
<el-table-column label="Attributes">
|
<el-table-column label="Attributes" fixed="right" min-width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-popover placement="left" :width="400" trigger="click">
|
<el-popover placement="left" :width="400" trigger="click">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
@@ -83,7 +87,7 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, computed } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
@@ -91,7 +95,10 @@ import { useSelectorStore } from "@/store/modules/selectors";
|
|||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { InstanceListConfig } from "@/types/dashboard";
|
import { InstanceListConfig } from "@/types/dashboard";
|
||||||
import { Instance } from "@/types/selector";
|
import { Instance } from "@/types/selector";
|
||||||
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
|
import {
|
||||||
|
useQueryPodsMetrics,
|
||||||
|
usePodsSource,
|
||||||
|
} from "@/hooks/useMetricsProcessor";
|
||||||
import { EntityType } from "../data";
|
import { EntityType } from "../data";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
@@ -127,9 +134,9 @@ const chartLoading = ref<boolean>(false);
|
|||||||
const instances = ref<Instance[]>([]); // current instances
|
const instances = ref<Instance[]>([]); // current instances
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const searchText = ref<string>("");
|
const searchText = ref<string>("");
|
||||||
const colMetrics = computed(() =>
|
const colMetrics = ref<string[]>([]);
|
||||||
(props.config.metrics || []).filter((d: string) => d)
|
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||||
);
|
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||||
if (props.needQuery) {
|
if (props.needQuery) {
|
||||||
queryInstance();
|
queryInstance();
|
||||||
}
|
}
|
||||||
@@ -155,9 +162,9 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metrics = props.config.metrics || [];
|
const metrics = props.config.metrics || [];
|
||||||
const metricTypes = props.config.metricTypes || [];
|
const types = props.config.metricTypes || [];
|
||||||
|
|
||||||
if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
|
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||||
const params = await useQueryPodsMetrics(
|
const params = await useQueryPodsMetrics(
|
||||||
currentInstances,
|
currentInstances,
|
||||||
props.config,
|
props.config,
|
||||||
@@ -169,11 +176,18 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metricConfig = props.config.metricConfig || [];
|
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
|
||||||
instances.value = usePodsSource(currentInstances, json, {
|
currentInstances,
|
||||||
|
json,
|
||||||
|
{
|
||||||
...props.config,
|
...props.config,
|
||||||
metricConfig,
|
metricConfig: metricConfig.value,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
instances.value = data;
|
||||||
|
colMetrics.value = names;
|
||||||
|
metricTypes.value = metricTypesArr;
|
||||||
|
metricConfig.value = metricConfigArr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instances.value = currentInstances;
|
instances.value = currentInstances;
|
||||||
@@ -216,11 +230,16 @@ function searchList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
|
() => [
|
||||||
|
...(props.config.metricTypes || []),
|
||||||
|
...(props.config.metrics || []),
|
||||||
|
...(props.config.metricConfig || []),
|
||||||
|
],
|
||||||
(data, old) => {
|
(data, old) => {
|
||||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
metricConfig.value = props.config.metricConfig;
|
||||||
queryInstanceMetrics(instances.value);
|
queryInstanceMetrics(instances.value);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -230,27 +249,10 @@ watch(
|
|||||||
queryInstance();
|
queryInstance();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
watch(
|
|
||||||
() => [...(props.config.metricConfig || [])],
|
|
||||||
(data, old) => {
|
|
||||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
queryInstanceMetrics(instances.value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./style.scss";
|
@import "./style.scss";
|
||||||
|
|
||||||
.chart {
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputs {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attributes {
|
.attributes {
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@@ -13,12 +13,28 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
<Graph :option="option" @select="clickEvent" :filters="config.filters" />
|
<div class="graph flex-v" :class="setRight ? 'flex-h' : 'flex-v'">
|
||||||
|
<Graph
|
||||||
|
:option="option"
|
||||||
|
@select="clickEvent"
|
||||||
|
:filters="config.filters"
|
||||||
|
:relatedTrace="config.relatedTrace"
|
||||||
|
:associate="config.associate || []"
|
||||||
|
/>
|
||||||
|
<Legend :config="config.legend" :data="data" :intervalTime="intervalTime" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { LineConfig, EventParams } from "@/types/dashboard";
|
import {
|
||||||
|
LineConfig,
|
||||||
|
EventParams,
|
||||||
|
RelatedTrace,
|
||||||
|
Filters,
|
||||||
|
} from "@/types/dashboard";
|
||||||
|
import Legend from "./components/Legend.vue";
|
||||||
|
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||||
|
|
||||||
/*global defineProps, defineEmits */
|
/*global defineProps, defineEmits */
|
||||||
const emits = defineEmits(["click"]);
|
const emits = defineEmits(["click"]);
|
||||||
@@ -32,16 +48,11 @@ const props = defineProps({
|
|||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<
|
||||||
LineConfig & {
|
LineConfig & {
|
||||||
filters: {
|
filters?: Filters;
|
||||||
sourceId: string;
|
relatedTrace?: RelatedTrace;
|
||||||
duration: {
|
id?: string;
|
||||||
startTime: string;
|
associate: { widgetId: string }[];
|
||||||
endTime: string;
|
}
|
||||||
};
|
|
||||||
isRange: boolean;
|
|
||||||
dataIndex?: number;
|
|
||||||
};
|
|
||||||
} & { id: string }
|
|
||||||
>,
|
>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
step: false,
|
step: false,
|
||||||
@@ -55,8 +66,13 @@ const props = defineProps({
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const setRight = ref<boolean>(false);
|
||||||
const option = computed(() => getOption());
|
const option = computed(() => getOption());
|
||||||
function getOption() {
|
function getOption() {
|
||||||
|
const { showEchartsLegend, isRight, chartColors } = useLegendProcess(
|
||||||
|
props.config.legend
|
||||||
|
);
|
||||||
|
setRight.value = isRight;
|
||||||
const keys = Object.keys(props.data || {}).filter(
|
const keys = Object.keys(props.data || {}).filter(
|
||||||
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||||
);
|
);
|
||||||
@@ -85,43 +101,21 @@ function getOption() {
|
|||||||
}
|
}
|
||||||
return serie;
|
return serie;
|
||||||
});
|
});
|
||||||
let color: string[] = [];
|
const color: string[] = chartColors(keys);
|
||||||
switch (keys.length) {
|
|
||||||
case 2:
|
|
||||||
color = ["#FF6A84", "#a0b1e6"];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
color = ["#3f96e3"];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = [
|
|
||||||
"#30A4EB",
|
|
||||||
"#45BFC0",
|
|
||||||
"#FFCC55",
|
|
||||||
"#FF6A84",
|
|
||||||
"#a0a7e6",
|
|
||||||
"#c23531",
|
|
||||||
"#2f4554",
|
|
||||||
"#61a0a8",
|
|
||||||
"#d48265",
|
|
||||||
"#91c7ae",
|
|
||||||
"#749f83",
|
|
||||||
"#ca8622",
|
|
||||||
"#bda29a",
|
|
||||||
"#6e7074",
|
|
||||||
"#546570",
|
|
||||||
"#c4ccd3",
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const tooltip = {
|
const tooltip = {
|
||||||
trigger: "axis",
|
trigger: "none",
|
||||||
textStyle: {
|
axisPointer: {
|
||||||
fontSize: 12,
|
type: "cross",
|
||||||
color: "#333",
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
},
|
},
|
||||||
enterable: true,
|
// trigger: "axis",
|
||||||
confine: true,
|
// textStyle: {
|
||||||
|
// fontSize: 12,
|
||||||
|
// color: "#333",
|
||||||
|
// },
|
||||||
|
// enterable: true,
|
||||||
|
// confine: true,
|
||||||
extraCssText: "max-height: 300px; overflow: auto; border: none;",
|
extraCssText: "max-height: 300px; overflow: auto; border: none;",
|
||||||
};
|
};
|
||||||
const tips = {
|
const tips = {
|
||||||
@@ -142,7 +136,7 @@ function getOption() {
|
|||||||
tooltip: props.config.smallTips ? tips : tooltip,
|
tooltip: props.config.smallTips ? tips : tooltip,
|
||||||
legend: {
|
legend: {
|
||||||
type: "scroll",
|
type: "scroll",
|
||||||
show: keys.length === 1 ? false : true,
|
show: showEchartsLegend(keys),
|
||||||
icon: "circle",
|
icon: "circle",
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -151,8 +145,14 @@ function getOption() {
|
|||||||
color: props.theme === "dark" ? "#fff" : "#333",
|
color: props.theme === "dark" ? "#fff" : "#333",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
axisPointer: {
|
||||||
|
label: {
|
||||||
|
color: "#333",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||||||
|
},
|
||||||
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: keys.length === 1 ? 15 : 55,
|
top: showEchartsLegend(keys) ? 35 : 10,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 10,
|
right: 10,
|
||||||
bottom: 5,
|
bottom: 5,
|
||||||
@@ -190,3 +190,9 @@ function clickEvent(params: EventParams) {
|
|||||||
emits("click", params);
|
emits("click", params);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.graph {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -18,12 +18,11 @@ limitations under the License. -->
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
placeholder="Please input service name"
|
placeholder="Please input service name"
|
||||||
size="small"
|
|
||||||
@change="searchList"
|
@change="searchList"
|
||||||
class="inputs mt-5"
|
class="inputs mt-5"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button size="small" @click="searchList">
|
<el-button class="btn" @click="searchList">
|
||||||
<Icon size="sm" iconName="search" />
|
<Icon size="sm" iconName="search" />
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -38,12 +37,17 @@ limitations under the License. -->
|
|||||||
:border="true"
|
:border="true"
|
||||||
:style="{ fontSize: '14px' }"
|
:style="{ fontSize: '14px' }"
|
||||||
>
|
>
|
||||||
<el-table-column label="Service Groups" v-if="config.showGroup">
|
<el-table-column
|
||||||
|
fixed
|
||||||
|
label="Service Groups"
|
||||||
|
v-if="config.showGroup"
|
||||||
|
min-width="150"
|
||||||
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ scope.row.group }}
|
{{ scope.row.group }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="Service Names">
|
<el-table-column fixed label="Service Names" min-width="220">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
@@ -57,7 +61,12 @@ limitations under the License. -->
|
|||||||
<ColumnGraph
|
<ColumnGraph
|
||||||
:intervalTime="intervalTime"
|
:intervalTime="intervalTime"
|
||||||
:colMetrics="colMetrics"
|
:colMetrics="colMetrics"
|
||||||
:config="config"
|
:config="{
|
||||||
|
...config,
|
||||||
|
metrics: colMetrics,
|
||||||
|
metricConfig,
|
||||||
|
metricTypes,
|
||||||
|
}"
|
||||||
v-if="colMetrics.length"
|
v-if="colMetrics.length"
|
||||||
/>
|
/>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -76,7 +85,7 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { watch, ref, computed } from "vue";
|
import { watch, ref } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { ServiceListConfig } from "@/types/dashboard";
|
import { ServiceListConfig } from "@/types/dashboard";
|
||||||
@@ -84,7 +93,10 @@ import { useSelectorStore } from "@/store/modules/selectors";
|
|||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { Service } from "@/types/selector";
|
import { Service } from "@/types/selector";
|
||||||
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
|
import {
|
||||||
|
useQueryPodsMetrics,
|
||||||
|
usePodsSource,
|
||||||
|
} from "@/hooks/useMetricsProcessor";
|
||||||
import { EntityType } from "../data";
|
import { EntityType } from "../data";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
@@ -103,7 +115,9 @@ const props = defineProps({
|
|||||||
metrics: string[];
|
metrics: string[];
|
||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
} & { metricConfig: MetricConfigOpt[] }
|
names: string[];
|
||||||
|
metricConfig: MetricConfigOpt[];
|
||||||
|
}
|
||||||
>,
|
>,
|
||||||
default: () => ({ dashboardName: "", fontSize: 12 }),
|
default: () => ({ dashboardName: "", fontSize: 12 }),
|
||||||
},
|
},
|
||||||
@@ -116,12 +130,13 @@ const appStore = useAppStoreWithOut();
|
|||||||
const chartLoading = ref<boolean>(false);
|
const chartLoading = ref<boolean>(false);
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const services = ref<Service[]>([]);
|
const services = ref<Service[]>([]);
|
||||||
|
const colMetrics = ref<string[]>([]);
|
||||||
const searchText = ref<string>("");
|
const searchText = ref<string>("");
|
||||||
const groups = ref<any>({});
|
const groups = ref<any>({});
|
||||||
const sortServices = ref<(Service & { merge: boolean })[]>([]);
|
const sortServices = ref<(Service & { merge: boolean })[]>([]);
|
||||||
const colMetrics = computed(() =>
|
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
|
||||||
(props.config.metrics || []).filter((d: string) => d)
|
const metricTypes = ref<string[]>(props.config.metricTypes || []);
|
||||||
);
|
|
||||||
queryServices();
|
queryServices();
|
||||||
|
|
||||||
async function queryServices() {
|
async function queryServices() {
|
||||||
@@ -199,12 +214,12 @@ async function queryServiceMetrics(currentServices: Service[]) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metrics = props.config.metrics || [];
|
const metrics = props.config.metrics || [];
|
||||||
const metricTypes = props.config.metricTypes || [];
|
const types = props.config.metricTypes || [];
|
||||||
|
|
||||||
if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
|
if (metrics.length && metrics[0] && types.length && types[0]) {
|
||||||
const params = await useQueryPodsMetrics(
|
const params = await useQueryPodsMetrics(
|
||||||
currentServices,
|
currentServices,
|
||||||
props.config,
|
{ ...props.config, metricConfig: metricConfig.value || [] },
|
||||||
EntityType[0].value
|
EntityType[0].value
|
||||||
);
|
);
|
||||||
const json = await dashboardStore.fetchMetricValue(params);
|
const json = await dashboardStore.fetchMetricValue(params);
|
||||||
@@ -213,14 +228,22 @@ async function queryServiceMetrics(currentServices: Service[]) {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metricConfig = props.config.metricConfig || [];
|
|
||||||
services.value = usePodsSource(currentServices, json, {
|
const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
|
||||||
|
currentServices,
|
||||||
|
json,
|
||||||
|
{
|
||||||
...props.config,
|
...props.config,
|
||||||
metricConfig: metricConfig || [],
|
metricConfig: metricConfig.value || [],
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
services.value = data;
|
||||||
|
colMetrics.value = names;
|
||||||
|
metricTypes.value = metricTypesArr;
|
||||||
|
metricConfig.value = metricConfigArr;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
services.value = currentServices;
|
services.value = currentServices;
|
||||||
}
|
}
|
||||||
function objectSpanMethod(param: any): any {
|
function objectSpanMethod(param: any): any {
|
||||||
@@ -258,20 +281,16 @@ function searchList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
|
() => [
|
||||||
(data, old) => {
|
...(props.config.metricTypes || []),
|
||||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
...(props.config.metrics || []),
|
||||||
return;
|
...(props.config.metricConfig || []),
|
||||||
}
|
],
|
||||||
queryServiceMetrics(services.value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
watch(
|
|
||||||
() => [...(props.config.metricConfig || [])],
|
|
||||||
(data, old) => {
|
(data, old) => {
|
||||||
if (JSON.stringify(data) === JSON.stringify(old)) {
|
if (JSON.stringify(data) === JSON.stringify(old)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
metricConfig.value = props.config.metricConfig;
|
||||||
queryServiceMetrics(services.value);
|
queryServiceMetrics(services.value);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -286,8 +305,4 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./style.scss";
|
@import "./style.scss";
|
||||||
|
|
||||||
.inputs {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -23,44 +23,84 @@ limitations under the License. -->
|
|||||||
{{ i.name }}
|
{{ i.name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="copy">
|
<el-popover placement="bottom" trigger="click">
|
||||||
<Icon
|
<template #reference>
|
||||||
iconName="review-list"
|
<div class="operation-icon cp ml-10">
|
||||||
size="middle"
|
<Icon iconName="ellipsis_v" size="middle" />
|
||||||
class="cp"
|
|
||||||
@click="handleClick(i.name)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="operation" @click="handleClick(i.name)">
|
||||||
|
<span>{{ t("copy") }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="operation"
|
||||||
|
@click="viewTrace(i)"
|
||||||
|
v-show="refIdType === RefIdTypes[0].value"
|
||||||
|
>
|
||||||
|
<span>{{ t("viewTrace") }}</span>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
<el-progress
|
<el-progress
|
||||||
:stroke-width="6"
|
:stroke-width="6"
|
||||||
:percentage="(i.value / maxValue) * 100"
|
:percentage="
|
||||||
|
isNaN(Number(i.value) / maxValue)
|
||||||
|
? 0
|
||||||
|
: (Number(i.value) / maxValue) * 100
|
||||||
|
"
|
||||||
:color="TextColors[config.color || 'purple']"
|
:color="TextColors[config.color || 'purple']"
|
||||||
:show-text="false"
|
:show-text="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<el-drawer
|
||||||
|
v-model="showTrace"
|
||||||
|
size="100%"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="() => (showTrace = false)"
|
||||||
|
:append-to-body="true"
|
||||||
|
title="The Related Traces"
|
||||||
|
>
|
||||||
|
<Trace :data="traceOptions" />
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
<div class="center no-data" v-else>No Data</div>
|
<div class="center no-data" v-else>No Data</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { computed } from "vue";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { computed, ref } from "vue";
|
||||||
import copy from "@/utils/copy";
|
import copy from "@/utils/copy";
|
||||||
import { TextColors } from "@/views/dashboard/data";
|
import { TextColors } from "@/views/dashboard/data";
|
||||||
|
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||||
|
import { QueryOrders, Status, RefIdTypes } from "../data";
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object as PropType<{
|
type: Object as PropType<{
|
||||||
[key: string]: { name: string; value: number; traceIds: string[] }[];
|
[key: string]: { name: string; value: number; id: string }[];
|
||||||
}>,
|
}>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object as PropType<{ color: string }>,
|
type: Object as PropType<{
|
||||||
|
color: string;
|
||||||
|
metrics: string[];
|
||||||
|
relatedTrace: any;
|
||||||
|
}>,
|
||||||
default: () => ({ color: "purple" }),
|
default: () => ({ color: "purple" }),
|
||||||
},
|
},
|
||||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||||
});
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const showTrace = ref<boolean>(false);
|
||||||
|
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||||
|
type: "Trace",
|
||||||
|
});
|
||||||
|
const refIdType = computed(
|
||||||
|
() =>
|
||||||
|
(props.config.relatedTrace && props.config.relatedTrace.refIdType) ||
|
||||||
|
RefIdTypes[0].value
|
||||||
|
);
|
||||||
const key = computed(() => Object.keys(props.data)[0] || "");
|
const key = computed(() => Object.keys(props.data)[0] || "");
|
||||||
const available = computed(
|
const available = computed(
|
||||||
() =>
|
() =>
|
||||||
@@ -78,6 +118,22 @@ const maxValue = computed(() => {
|
|||||||
function handleClick(i: string) {
|
function handleClick(i: string) {
|
||||||
copy(i);
|
copy(i);
|
||||||
}
|
}
|
||||||
|
function viewTrace(item: { name: string; id: string; value: unknown }) {
|
||||||
|
const filters = {
|
||||||
|
...item,
|
||||||
|
queryOrder: QueryOrders[1].value,
|
||||||
|
status: Status[2].value,
|
||||||
|
id: item.id || item.name,
|
||||||
|
metricValue: [
|
||||||
|
{ label: props.config.metrics[0], data: item.value, value: item.name },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
traceOptions.value = {
|
||||||
|
...traceOptions.value,
|
||||||
|
filters,
|
||||||
|
};
|
||||||
|
showTrace.value = true;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.top-list {
|
.top-list {
|
||||||
@@ -109,10 +165,6 @@ function handleClick(i: string) {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy {
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calls {
|
.calls {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
@@ -141,4 +193,22 @@ function handleClick(i: string) {
|
|||||||
-webkit-box-pack: center;
|
-webkit-box-pack: center;
|
||||||
-webkit-box-align: center;
|
-webkit-box-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operation-icon {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
padding: 5px 0;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -20,6 +20,7 @@ limitations under the License. -->
|
|||||||
getLabel(metric, index)
|
getLabel(metric, index)
|
||||||
)} ${decodeURIComponent(getUnit(index))}`"
|
)} ${decodeURIComponent(getUnit(index))}`"
|
||||||
:key="metric + index"
|
:key="metric + index"
|
||||||
|
min-width="150"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="chart">
|
<div class="chart">
|
||||||
@@ -90,18 +91,18 @@ import { MetricConfigOpt } from "@/types/dashboard";
|
|||||||
import { useListConfig } from "@/hooks/useListConfig";
|
import { useListConfig } from "@/hooks/useListConfig";
|
||||||
import Line from "../Line.vue";
|
import Line from "../Line.vue";
|
||||||
import Card from "../Card.vue";
|
import Card from "../Card.vue";
|
||||||
|
import { MetricQueryTypes } from "@/hooks/data";
|
||||||
|
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
colMetrics: { type: Object },
|
colMetrics: { type: Object },
|
||||||
config: {
|
config: {
|
||||||
type: Object as PropType<
|
type: Object as PropType<{
|
||||||
{
|
|
||||||
i: string;
|
i: string;
|
||||||
metrics: string[];
|
metrics: string[];
|
||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
} & { metricConfig: MetricConfigOpt[] }
|
metricConfig: MetricConfigOpt[];
|
||||||
>,
|
}>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||||
@@ -125,6 +126,16 @@ function getLabel(metric: string, index: string) {
|
|||||||
props.config.metricConfig[i] &&
|
props.config.metricConfig[i] &&
|
||||||
props.config.metricConfig[i].label;
|
props.config.metricConfig[i].label;
|
||||||
if (label) {
|
if (label) {
|
||||||
|
if (
|
||||||
|
props.config.metricTypes[i] === MetricQueryTypes.ReadLabeledMetricsValues
|
||||||
|
) {
|
||||||
|
const name = (label || "")
|
||||||
|
.split(",")
|
||||||
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""))[
|
||||||
|
props.config.metricConfig[i].index || 0
|
||||||
|
];
|
||||||
|
return encodeURIComponent(name || "");
|
||||||
|
}
|
||||||
return encodeURIComponent(label);
|
return encodeURIComponent(label);
|
||||||
}
|
}
|
||||||
return encodeURIComponent(metric);
|
return encodeURIComponent(metric);
|
||||||
@@ -157,5 +168,6 @@ function getLabel(metric: string, index: string) {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: calc(100% - 30px);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
195
src/views/dashboard/graphs/components/Legend.vue
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="tableData.length && config.asTable"
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="caption"
|
||||||
|
tabindex="0"
|
||||||
|
:style="`width: ${width}; maxHeight:${isRight ? '100%' : 130}`"
|
||||||
|
class="scroll_bar_style"
|
||||||
|
>
|
||||||
|
<table v-if="tableData.length === 1">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th v-show="headerRow.length"></th>
|
||||||
|
<th>
|
||||||
|
{{ tableData[0].name }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="h in headerRow" :key="h.value">
|
||||||
|
<th>
|
||||||
|
{{ h.label }}
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
{{ tableData[0][h.value] }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table v-else>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th v-for="h in headerRow" :key="h.value">
|
||||||
|
{{ h.label }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, index) in tableData" :key="index">
|
||||||
|
<th>
|
||||||
|
<el-popover placement="bottom" :width="230" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<div class="name">
|
||||||
|
<Icon iconName="circle" :style="`color: ${colors[index]};`" />
|
||||||
|
<i>{{ item.name }}</i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="list">
|
||||||
|
<div class="value">
|
||||||
|
<span>{{ t("key") }}</span>
|
||||||
|
<span>{{ t("value") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="value" v-for="(d, index) in item.topN" :key="index">
|
||||||
|
<span>{{ d.key }}</span>
|
||||||
|
<span>{{ d.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</th>
|
||||||
|
<td v-for="h in headerRow" :key="h.value">
|
||||||
|
{{ item[h.value] }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { LegendOptions } from "@/types/dashboard";
|
||||||
|
import useLegendProcess from "@/hooks/useLegendProcessor";
|
||||||
|
|
||||||
|
/*global defineProps */
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<{ [key: string]: number[] }>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: Object as PropType<LegendOptions>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const tableData: any = computed(() => {
|
||||||
|
const { aggregations } = useLegendProcess(props.config);
|
||||||
|
return aggregations(props.data, props.intervalTime).source;
|
||||||
|
});
|
||||||
|
const headerRow = computed(() => {
|
||||||
|
const { aggregations } = useLegendProcess(props.config);
|
||||||
|
return aggregations(props.data, props.intervalTime).headers;
|
||||||
|
});
|
||||||
|
const isRight = computed(() => useLegendProcess(props.config).isRight);
|
||||||
|
const width = computed(() =>
|
||||||
|
props.config.width
|
||||||
|
? props.config.width + "px"
|
||||||
|
: isRight.value
|
||||||
|
? "150px"
|
||||||
|
: "100%"
|
||||||
|
);
|
||||||
|
const colors = computed(() => {
|
||||||
|
const keys = Object.keys(props.data || {}).filter(
|
||||||
|
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
|
||||||
|
);
|
||||||
|
const { chartColors } = useLegendProcess(props.config);
|
||||||
|
return chartColors(keys);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
table {
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: 25vw;
|
||||||
|
background: #fff;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead th:first-child {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody th {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
text-align: left;
|
||||||
|
background: #fff;
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="region"][aria-labelledby][tabindex] {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
height: 360px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -23,6 +23,7 @@
|
|||||||
.list {
|
.list {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
height: calc(100% - 90px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
@@ -40,9 +41,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
text-align: right;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-with-search {
|
.input-with-search {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-top: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
@@ -19,11 +19,11 @@ export default (d3: any, graph: any, diff: number[]) =>
|
|||||||
d3
|
d3
|
||||||
.zoom()
|
.zoom()
|
||||||
.scaleExtent([0.3, 10])
|
.scaleExtent([0.3, 10])
|
||||||
.on("zoom", (event: any) => {
|
.on("zoom", (d: any) => {
|
||||||
graph.attr(
|
graph.attr(
|
||||||
"transform",
|
"transform",
|
||||||
`translate(${event.transform.x + diff[0]},${
|
`translate(${d.transform.x + diff[0]},${
|
||||||
event.transform.y + diff[1]
|
d.transform.y + diff[1]
|
||||||
})scale(${event.transform.k})`
|
})scale(${d.transform.k})`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -193,22 +193,22 @@ async function init() {
|
|||||||
state.endpoint = { value: "0", label: "All" };
|
state.endpoint = { value: "0", label: "All" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchSelectors() {
|
async function fetchSelectors() {
|
||||||
if (dashboardStore.entity === EntityType[1].value) {
|
if (dashboardStore.entity === EntityType[1].value) {
|
||||||
getServices();
|
await getServices();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (dashboardStore.entity === EntityType[2].value) {
|
if (dashboardStore.entity === EntityType[2].value) {
|
||||||
getInstances();
|
await getInstances();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (dashboardStore.entity === EntityType[3].value) {
|
if (dashboardStore.entity === EntityType[3].value) {
|
||||||
getEndpoints();
|
await getEndpoints();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (dashboardStore.entity === EntityType[0].value) {
|
if (dashboardStore.entity === EntityType[0].value) {
|
||||||
getInstances();
|
await getInstances();
|
||||||
getEndpoints();
|
await getEndpoints();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -53,7 +53,7 @@ const { t } = useI18n();
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
min-width: 700px;
|
min-width: 700px;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: calc(100% - 330px);
|
width: calc(100% - 330px);
|
||||||
}
|
}
|
||||||
|
@@ -17,96 +17,6 @@
|
|||||||
import icons from "@/assets/img/icons";
|
import icons from "@/assets/img/icons";
|
||||||
import { Call } from "@/types/topology";
|
import { Call } from "@/types/topology";
|
||||||
|
|
||||||
export const linkElement = (graph: any) => {
|
|
||||||
const linkEnter = graph
|
|
||||||
.append("path")
|
|
||||||
.attr("class", "topo-call")
|
|
||||||
.attr("marker-end", "url(#arrow)")
|
|
||||||
.attr("stroke", "#97B0F8")
|
|
||||||
.attr("d", (d: Call) => {
|
|
||||||
const controlPos = computeControlPoint(
|
|
||||||
[d.source.x, d.source.y - 5],
|
|
||||||
[d.target.x, d.target.y - 5],
|
|
||||||
0.5
|
|
||||||
);
|
|
||||||
if (d.lowerArc) {
|
|
||||||
controlPos[1] =
|
|
||||||
Math.abs(controlPos[1]) < 50
|
|
||||||
? -controlPos[1] + 90
|
|
||||||
: -controlPos[1] - 10;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
"M" +
|
|
||||||
d.source.x +
|
|
||||||
" " +
|
|
||||||
(d.source.y - 5) +
|
|
||||||
" " +
|
|
||||||
"Q" +
|
|
||||||
controlPos[0] +
|
|
||||||
" " +
|
|
||||||
controlPos[1] +
|
|
||||||
" " +
|
|
||||||
d.target.x +
|
|
||||||
" " +
|
|
||||||
(d.target.y - 5)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return linkEnter;
|
|
||||||
};
|
|
||||||
export const anchorElement = (graph: any, funcs: any, tip: any) => {
|
|
||||||
const linkEnter = graph
|
|
||||||
.append("g")
|
|
||||||
.attr("class", "topo-line-anchor")
|
|
||||||
.on("mouseover", function (event: unknown, d: unknown) {
|
|
||||||
tip.html(funcs.tipHtml).show(d, this);
|
|
||||||
})
|
|
||||||
.on("mouseout", function () {
|
|
||||||
tip.hide(this);
|
|
||||||
})
|
|
||||||
.on("click", (event: unknown, d: unknown) => {
|
|
||||||
funcs.handleLinkClick(event, d);
|
|
||||||
});
|
|
||||||
linkEnter
|
|
||||||
.append("image")
|
|
||||||
.attr("width", 15)
|
|
||||||
.attr("height", 15)
|
|
||||||
.attr("x", (d: Call) => {
|
|
||||||
const p = getMidpoint(d);
|
|
||||||
return p[0] - 8;
|
|
||||||
})
|
|
||||||
.attr("y", (d: Call) => {
|
|
||||||
const p = getMidpoint(d);
|
|
||||||
return p[1] - 13;
|
|
||||||
})
|
|
||||||
.attr("xlink:href", (d: Call) => {
|
|
||||||
const types = [...d.sourceComponents, ...d.targetComponents];
|
|
||||||
if (types.includes("tcp") || types.includes("http")) {
|
|
||||||
return icons.HTTPDARK;
|
|
||||||
}
|
|
||||||
if (types.includes("https") || types.includes("tls")) {
|
|
||||||
return icons.HTTPS;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return linkEnter;
|
|
||||||
};
|
|
||||||
export const arrowMarker = (graph: any) => {
|
|
||||||
const defs = graph.append("defs");
|
|
||||||
const arrow = defs
|
|
||||||
.append("marker")
|
|
||||||
.attr("id", "arrow")
|
|
||||||
.attr("class", "topo-line-arrow")
|
|
||||||
.attr("markerUnits", "strokeWidth")
|
|
||||||
.attr("markerWidth", "8")
|
|
||||||
.attr("markerHeight", "8")
|
|
||||||
.attr("viewBox", "0 0 12 12")
|
|
||||||
.attr("refX", "10")
|
|
||||||
.attr("refY", "6")
|
|
||||||
.attr("orient", "auto");
|
|
||||||
const arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2";
|
|
||||||
|
|
||||||
arrow.append("path").attr("d", arrowPath).attr("fill", "#97B0F8");
|
|
||||||
return arrow;
|
|
||||||
};
|
|
||||||
// Control Point coordinates of quadratic Bezier curve
|
// Control Point coordinates of quadratic Bezier curve
|
||||||
function computeControlPoint(ps: number[], pe: number[], arc = 0.5) {
|
function computeControlPoint(ps: number[], pe: number[], arc = 0.5) {
|
||||||
const deltaX = pe[0] - ps[0];
|
const deltaX = pe[0] - ps[0];
|
||||||
@@ -137,15 +47,20 @@ function quadraticBezier(
|
|||||||
const y = (1 - t) * (1 - t) * ps.y + 2 * t * (1 - t) * pc.y + t * t * pe.y;
|
const y = (1 - t) * (1 - t) * ps.y + 2 * t * (1 - t) * pc.y + t * t * pe.y;
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
function getMidpoint(d: Call) {
|
export function getMidpoint(d: Call) {
|
||||||
|
if (isNaN(d.source.x) || isNaN(d.source.y)) {
|
||||||
|
return [0, 0];
|
||||||
|
}
|
||||||
|
if (isNaN(d.target.x) || isNaN(d.target.y)) {
|
||||||
|
return [0, 0];
|
||||||
|
}
|
||||||
const controlPos = computeControlPoint(
|
const controlPos = computeControlPoint(
|
||||||
[d.source.x, d.source.y],
|
[d.source.x, d.source.y],
|
||||||
[d.target.x, d.target.y],
|
[d.target.x, d.target.y],
|
||||||
0.5
|
0.5
|
||||||
);
|
);
|
||||||
if (d.lowerArc) {
|
if (d.lowerArc) {
|
||||||
controlPos[1] =
|
controlPos[1] = -controlPos[1];
|
||||||
Math.abs(controlPos[1]) < 50 ? -controlPos[1] + 100 : -controlPos[1] - 10;
|
|
||||||
}
|
}
|
||||||
const p = quadraticBezier(
|
const p = quadraticBezier(
|
||||||
0.5,
|
0.5,
|
||||||
@@ -155,3 +70,43 @@ function getMidpoint(d: Call) {
|
|||||||
);
|
);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
export function linkPath(d: Call) {
|
||||||
|
if (isNaN(d.source.x) || isNaN(d.source.y)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isNaN(d.target.x) || isNaN(d.target.y)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const controlPos = computeControlPoint(
|
||||||
|
[d.source.x, d.source.y - 5],
|
||||||
|
[d.target.x, d.target.y - 5],
|
||||||
|
0.5
|
||||||
|
);
|
||||||
|
if (d.lowerArc) {
|
||||||
|
controlPos[1] = -controlPos[1] - 10;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
"M" +
|
||||||
|
d.source.x +
|
||||||
|
" " +
|
||||||
|
(d.source.y - 5) +
|
||||||
|
" " +
|
||||||
|
"Q" +
|
||||||
|
controlPos[0] +
|
||||||
|
" " +
|
||||||
|
controlPos[1] +
|
||||||
|
" " +
|
||||||
|
d.target.x +
|
||||||
|
" " +
|
||||||
|
(d.target.y - 5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export function getAnchor(d: Call) {
|
||||||
|
const types = [...d.sourceComponents, ...d.targetComponents];
|
||||||
|
if (types.includes("tcp") || types.includes("http")) {
|
||||||
|
return icons.HTTPDARK;
|
||||||
|
}
|
||||||
|
if (types.includes("https") || types.includes("tls")) {
|
||||||
|
return icons.HTTPS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
import icons from "@/assets/img/icons";
|
|
||||||
import { Node } from "@/types/topology";
|
|
||||||
|
|
||||||
export default (d3: any, graph: any, funcs: any, tip: any) => {
|
|
||||||
const nodeEnter = graph
|
|
||||||
.append("g")
|
|
||||||
.call(
|
|
||||||
d3
|
|
||||||
.drag()
|
|
||||||
.on("start", funcs.dragstart)
|
|
||||||
.on("drag", funcs.dragged)
|
|
||||||
.on("end", funcs.dragended)
|
|
||||||
)
|
|
||||||
.on("mouseover", function (event: unknown, d: Node) {
|
|
||||||
tip.html(funcs.tipHtml).show(d, this);
|
|
||||||
})
|
|
||||||
.on("mouseout", function () {
|
|
||||||
tip.hide(this);
|
|
||||||
});
|
|
||||||
nodeEnter
|
|
||||||
.append("image")
|
|
||||||
.attr("width", 35)
|
|
||||||
.attr("height", 35)
|
|
||||||
.attr("x", (d: any) => d.x - 15)
|
|
||||||
.attr("y", (d: any) => d.y - 15)
|
|
||||||
.attr("style", "cursor: move;")
|
|
||||||
.attr("xlink:href", icons.CUBE);
|
|
||||||
nodeEnter
|
|
||||||
.append("text")
|
|
||||||
.attr("fill", "#000")
|
|
||||||
.attr("text-anchor", "middle")
|
|
||||||
.attr("x", (d: any) => d.x)
|
|
||||||
.attr("y", (d: any) => d.y + 28)
|
|
||||||
.text((d: { name: string }) =>
|
|
||||||
d.name.length > 10 ? `${d.name.substring(0, 10)}...` : d.name
|
|
||||||
);
|
|
||||||
return nodeEnter;
|
|
||||||
};
|
|
@@ -0,0 +1,96 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="profile-task">
|
||||||
|
<div>
|
||||||
|
<div class="label">URI Regex</div>
|
||||||
|
<el-input size="small" class="profile-input" v-model="states.uriRegex" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="label">{{ t("minDuration") }} (ms)</div>
|
||||||
|
<el-input
|
||||||
|
size="small"
|
||||||
|
class="profile-input"
|
||||||
|
:min="0"
|
||||||
|
v-model="states.minDuration"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="label">{{ t("when4xx") }}</div>
|
||||||
|
<Radio
|
||||||
|
class="mb-5"
|
||||||
|
:value="states.when4xx"
|
||||||
|
:options="InitTaskField.Whenxx"
|
||||||
|
@change="changeWhen4xx"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="label">{{ t("when5xx") }}</div>
|
||||||
|
<Radio
|
||||||
|
class="mb-5"
|
||||||
|
:value="states.when5xx"
|
||||||
|
:options="InitTaskField.Whenxx"
|
||||||
|
@change="changeWhen5xx"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { defineProps, reactive } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import { InitTaskField } from "./data";
|
||||||
|
import { NetworkProfilingRequest } from "@/types/ebpf";
|
||||||
|
|
||||||
|
/* global defineEmits */
|
||||||
|
const emits = defineEmits(["change"]);
|
||||||
|
const props = defineProps({
|
||||||
|
condition: {
|
||||||
|
type: Object as PropType<NetworkProfilingRequest>,
|
||||||
|
default: () => ({ children: [] }),
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
type: Number,
|
||||||
|
default: () => 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const states = reactive<NetworkProfilingRequest>(props.condition);
|
||||||
|
|
||||||
|
function changeWhen5xx(value: string) {
|
||||||
|
states.when5xx = value;
|
||||||
|
emits("change", states, props.key);
|
||||||
|
}
|
||||||
|
function changeWhen4xx(value: string) {
|
||||||
|
states.when4xx = value;
|
||||||
|
emits("change", states, props.key);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.date {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-input {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,139 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="profile-task">
|
||||||
|
<el-collapse v-model="activeNames">
|
||||||
|
<el-collapse-item
|
||||||
|
v-for="(item, index) in conditionsList"
|
||||||
|
:key="index"
|
||||||
|
:name="index"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div>
|
||||||
|
<span class="title">{{ `Rule - ${index + 1}` }}</span>
|
||||||
|
<Icon
|
||||||
|
class="mr-5 cp"
|
||||||
|
iconName="remove_circle_outline"
|
||||||
|
size="middle"
|
||||||
|
v-show="conditionsList.length !== 1"
|
||||||
|
@click="removeConditions($event, index)"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
class="cp"
|
||||||
|
v-show="index === conditionsList.length - 1"
|
||||||
|
iconName="add_circle_outlinecontrol_point"
|
||||||
|
size="middle"
|
||||||
|
@click="createConditions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<NewCondition
|
||||||
|
:name="index"
|
||||||
|
:condition="item"
|
||||||
|
:key="index"
|
||||||
|
@change="changeConfig"
|
||||||
|
/>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
<div>
|
||||||
|
<el-button @click="createTask" type="primary" class="create-task-btn">
|
||||||
|
{{ t("createTask") }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { InitTaskField } from "./data";
|
||||||
|
import NewCondition from "./NewConditions.vue";
|
||||||
|
import { NetworkProfilingRequest } from "@/types/ebpf";
|
||||||
|
|
||||||
|
/* global defineEmits */
|
||||||
|
const emits = defineEmits(["create"]);
|
||||||
|
const { t } = useI18n();
|
||||||
|
const activeNames = ref([0]);
|
||||||
|
const conditionsList = ref<NetworkProfilingRequest[]>([
|
||||||
|
{
|
||||||
|
uriRegex: "",
|
||||||
|
when4xx: InitTaskField.Whenxx[1].value,
|
||||||
|
when5xx: InitTaskField.Whenxx[0].value,
|
||||||
|
minDuration: NaN,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function changeConfig(
|
||||||
|
params: { [key: string]: number | string },
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
const key: string = Object.keys(params)[0];
|
||||||
|
(conditionsList.value[index] as any)[key] = params[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTask() {
|
||||||
|
const list = conditionsList.value.map((d: NetworkProfilingRequest) => {
|
||||||
|
return {
|
||||||
|
uriRegex: d.uriRegex || undefined,
|
||||||
|
when4xx: d.when4xx === InitTaskField.Whenxx[0].value ? true : false,
|
||||||
|
when5xx: d.when5xx === InitTaskField.Whenxx[0].value ? true : false,
|
||||||
|
minDuration: isNaN(Number(d.minDuration))
|
||||||
|
? undefined
|
||||||
|
: Number(d.minDuration),
|
||||||
|
settings: {
|
||||||
|
requireCompleteRequest: true,
|
||||||
|
requireCompleteResponse: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
emits("create", list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createConditions(e: any) {
|
||||||
|
e.stopPropagation();
|
||||||
|
conditionsList.value.push({
|
||||||
|
uriRegex: "",
|
||||||
|
when4xx: InitTaskField.Whenxx[0].value,
|
||||||
|
when5xx: InitTaskField.Whenxx[1].value,
|
||||||
|
minDuration: NaN,
|
||||||
|
});
|
||||||
|
activeNames.value = [conditionsList.value.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeConditions(e: any, key: number) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (conditionsList.value.length === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conditionsList.value = conditionsList.value.filter(
|
||||||
|
(_, index: number) => index !== key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.profile-task {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-task-btn {
|
||||||
|
width: 300px;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -13,13 +13,103 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
<div ref="chart" class="process-topo"></div>
|
<div ref="chart" class="process-topo">
|
||||||
<el-popover
|
<svg
|
||||||
placement="bottom"
|
class="process-svg"
|
||||||
:width="295"
|
:width="width"
|
||||||
trigger="click"
|
:height="height"
|
||||||
v-if="dashboardStore.editMode"
|
@click="clickTopology"
|
||||||
>
|
>
|
||||||
|
<g class="svg-graph" :transform="`translate(${diff[0]}, ${diff[1]})`">
|
||||||
|
<g class="hex-polygon">
|
||||||
|
<path
|
||||||
|
:d="getHexPolygonVertices()"
|
||||||
|
stroke="#D5DDF6"
|
||||||
|
stroke-width="2"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<text :x="0" :y="radius - 15" fill="#000" text-anchor="middle">
|
||||||
|
{{ selectorStore.currentPod.label }}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g class="nodes">
|
||||||
|
<g
|
||||||
|
v-for="(node, index) in nodeList"
|
||||||
|
:key="index"
|
||||||
|
class="node"
|
||||||
|
@mouseover="showNodeTip(node, $event)"
|
||||||
|
@mouseout="hideNodeTip"
|
||||||
|
@mousedown="startMoveNode($event, node)"
|
||||||
|
@mouseup="stopMoveNode($event)"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
:href="icons.CUBE"
|
||||||
|
style="cursor: 'move'"
|
||||||
|
width="35"
|
||||||
|
height="35"
|
||||||
|
:x="(node.x || 0) - 15"
|
||||||
|
:y="(node.y || 0) - 15"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
:x="node.x"
|
||||||
|
:y="(node.y || 0) + 28"
|
||||||
|
fill="#000"
|
||||||
|
text-anchor="middle"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
node.name.length > 10
|
||||||
|
? `${node.name.substring(0, 10)}...`
|
||||||
|
: node.name
|
||||||
|
}}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="calls">
|
||||||
|
<path
|
||||||
|
v-for="(call, index) in networkProfilingStore.calls"
|
||||||
|
:key="index"
|
||||||
|
class="topo-call"
|
||||||
|
marker-end="url(#arrow)"
|
||||||
|
stroke="#97B0F8"
|
||||||
|
:d="linkPath(call)"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g class="anchors">
|
||||||
|
<image
|
||||||
|
v-for="(call, index) in networkProfilingStore.calls"
|
||||||
|
:key="index"
|
||||||
|
class="topo-line-anchor"
|
||||||
|
:href="getAnchor(call)"
|
||||||
|
width="15"
|
||||||
|
height="15"
|
||||||
|
:x="getMidpoint(call)[0] - 8"
|
||||||
|
:y="getMidpoint(call)[1] - 13"
|
||||||
|
@click="handleLinkClick($event, call)"
|
||||||
|
@mouseover="showLinkTip(call, $event)"
|
||||||
|
@mouseout="hideLinkTip"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g class="arrows">
|
||||||
|
<defs v-for="(_, index) in networkProfilingStore.calls" :key="index">
|
||||||
|
<marker
|
||||||
|
id="arrow"
|
||||||
|
markerUnits="strokeWidth"
|
||||||
|
markerWidth="8"
|
||||||
|
markerHeight="8"
|
||||||
|
viewBox="0 0 12 12"
|
||||||
|
refX="10"
|
||||||
|
refY="6"
|
||||||
|
orient="auto"
|
||||||
|
>
|
||||||
|
<path d="M2,2 L10,6 L2,10 L6,6 L2,2" fill="#97B0F8" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<div id="tooltip"></div>
|
||||||
|
</div>
|
||||||
|
<el-popover placement="bottom" :width="295" trigger="click">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div class="switch-icon-edit ml-5" title="Settings" @click="setConfig">
|
<div class="switch-icon-edit ml-5" title="Settings" @click="setConfig">
|
||||||
<Icon size="middle" iconName="setting_empty" />
|
<Icon size="middle" iconName="setting_empty" />
|
||||||
@@ -39,9 +129,7 @@ import router from "@/router";
|
|||||||
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
|
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import d3tip from "d3-tip";
|
import { linkPath, getAnchor, getMidpoint } from "./Graph/linkProcess";
|
||||||
import { linkElement, anchorElement, arrowMarker } from "./Graph/linkProcess";
|
|
||||||
import nodeElement from "./Graph/nodeProcess";
|
|
||||||
import { Call } from "@/types/topology";
|
import { Call } from "@/types/topology";
|
||||||
import zoom from "../../components/utils/zoom";
|
import zoom from "../../components/utils/zoom";
|
||||||
import { ProcessNode } from "@/types/ebpf";
|
import { ProcessNode } from "@/types/ebpf";
|
||||||
@@ -52,6 +140,7 @@ import getDashboard from "@/hooks/useDashboardsSession";
|
|||||||
import { Layout } from "./Graph/layout";
|
import { Layout } from "./Graph/layout";
|
||||||
import TimeLine from "./TimeLine.vue";
|
import TimeLine from "./TimeLine.vue";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import icons from "@/assets/img/icons";
|
||||||
|
|
||||||
/*global Nullable, defineProps */
|
/*global Nullable, defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -67,19 +156,18 @@ const selectorStore = useSelectorStore();
|
|||||||
const networkProfilingStore = useNetworkProfilingStore();
|
const networkProfilingStore = useNetworkProfilingStore();
|
||||||
const height = ref<number>(100);
|
const height = ref<number>(100);
|
||||||
const width = ref<number>(100);
|
const width = ref<number>(100);
|
||||||
const svg = ref<Nullable<any>>(null);
|
|
||||||
const chart = ref<Nullable<HTMLDivElement>>(null);
|
const chart = ref<Nullable<HTMLDivElement>>(null);
|
||||||
const tip = ref<Nullable<HTMLDivElement>>(null);
|
const tooltip = ref<Nullable<any>>(null);
|
||||||
const graph = ref<any>(null);
|
const svg = ref<Nullable<any>>(null);
|
||||||
const node = ref<any>(null);
|
const graph = ref<Nullable<any>>(null);
|
||||||
const link = ref<any>(null);
|
|
||||||
const anchor = ref<any>(null);
|
|
||||||
const arrow = ref<any>(null);
|
|
||||||
const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
|
const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
|
||||||
const config = ref<any>(props.config || {});
|
const config = ref<any>(props.config || {});
|
||||||
const diff = ref<number[]>([220, 200]);
|
const diff = ref<number[]>([220, 200]);
|
||||||
const radius = 210;
|
const radius = 210;
|
||||||
const dates = ref<Nullable<{ start: number; end: number }>>(null);
|
const dates = ref<Nullable<{ start: number; end: number }>>(null);
|
||||||
|
const nodeList = ref<ProcessNode[]>([]);
|
||||||
|
const currentNode = ref<Nullable<ProcessNode>>(null);
|
||||||
|
const origin = [0, 0];
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init();
|
init();
|
||||||
@@ -90,12 +178,14 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
svg.value = d3.select(chart.value).append("svg").attr("class", "process-svg");
|
|
||||||
if (!networkProfilingStore.nodes.length) {
|
if (!networkProfilingStore.nodes.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
drawGraph();
|
svg.value = d3.select(".process-svg");
|
||||||
createLayout();
|
graph.value = d3.select(".svg-graph");
|
||||||
|
tooltip.value = d3.select("#tooltip");
|
||||||
|
freshNodes();
|
||||||
|
useThrottleFn(resize, 500)();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawGraph() {
|
function drawGraph() {
|
||||||
@@ -105,27 +195,16 @@ function drawGraph() {
|
|||||||
};
|
};
|
||||||
height.value = (dom.height || 40) - 20;
|
height.value = (dom.height || 40) - 20;
|
||||||
width.value = dom.width;
|
width.value = dom.width;
|
||||||
svg.value.attr("height", height.value).attr("width", width.value);
|
|
||||||
tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]);
|
|
||||||
diff.value[0] = (dom.width - radius * 2) / 2 + radius;
|
diff.value[0] = (dom.width - radius * 2) / 2 + radius;
|
||||||
graph.value = svg.value
|
|
||||||
.append("g")
|
|
||||||
.attr("class", "svg-graph")
|
|
||||||
.attr("transform", `translate(${diff.value[0]}, ${diff.value[1]})`);
|
|
||||||
graph.value.call(tip.value);
|
|
||||||
node.value = graph.value.append("g").selectAll(".topo-node");
|
|
||||||
link.value = graph.value.append("g").selectAll(".topo-call");
|
|
||||||
anchor.value = graph.value.append("g").selectAll(".topo-line-anchor");
|
|
||||||
arrow.value = graph.value.append("g").selectAll(".topo-line-arrow");
|
|
||||||
svg.value.call(zoom(d3, graph.value, diff.value));
|
svg.value.call(zoom(d3, graph.value, diff.value));
|
||||||
svg.value.on("click", (event: any) => {
|
}
|
||||||
|
|
||||||
|
function clickTopology(event: MouseEvent) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
networkProfilingStore.setNode(null);
|
networkProfilingStore.setNode(null);
|
||||||
networkProfilingStore.setLink(null);
|
networkProfilingStore.setLink(null);
|
||||||
dashboardStore.selectWidget(props.config);
|
dashboardStore.selectWidget(props.config);
|
||||||
});
|
|
||||||
useThrottleFn(resize, 500)();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hexGrid(n = 1, radius = 1, origin = [0, 0]) {
|
function hexGrid(n = 1, radius = 1, origin = [0, 0]) {
|
||||||
@@ -157,7 +236,6 @@ function createPolygon(radius: number, sides = 6, offset = 0) {
|
|||||||
}
|
}
|
||||||
function getCirclePoint(radius: number, p = 1) {
|
function getCirclePoint(radius: number, p = 1) {
|
||||||
const data = [];
|
const data = [];
|
||||||
const origin = [0, 0];
|
|
||||||
for (let index = 0; index < 360; index = index + p) {
|
for (let index = 0; index < 360; index = index + p) {
|
||||||
if (index < 230 || index > 310) {
|
if (index < 230 || index > 310) {
|
||||||
let x = radius * Math.cos((Math.PI * 2 * index) / 360);
|
let x = radius * Math.cos((Math.PI * 2 * index) / 360);
|
||||||
@@ -167,10 +245,22 @@ function getCirclePoint(radius: number, p = 1) {
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
function createLayout() {
|
|
||||||
if (!node.value || !link.value) {
|
function getHexPolygonVertices() {
|
||||||
return;
|
const p = {
|
||||||
|
count: 1,
|
||||||
|
radius, // layout hexagons radius 300
|
||||||
|
};
|
||||||
|
const polygon = createPolygon(p.radius, 6, 0);
|
||||||
|
const vertices: any = []; // a hexagon vertices
|
||||||
|
for (let v = 0; v < polygon.length; v++) {
|
||||||
|
vertices.push([origin[0] + polygon[v][0], origin[1] + polygon[v][1]]);
|
||||||
}
|
}
|
||||||
|
const linePath = d3.line();
|
||||||
|
linePath.curve(d3.curveLinearClosed);
|
||||||
|
return linePath(vertices) || "";
|
||||||
|
}
|
||||||
|
function createLayout() {
|
||||||
const dom: any = (chart.value && chart.value.getBoundingClientRect()) || {
|
const dom: any = (chart.value && chart.value.getBoundingClientRect()) || {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@@ -182,28 +272,6 @@ function createLayout() {
|
|||||||
count: 1,
|
count: 1,
|
||||||
radius, // layout hexagons radius 300
|
radius, // layout hexagons radius 300
|
||||||
};
|
};
|
||||||
const polygon = createPolygon(p.radius, 6, 0);
|
|
||||||
const origin = [0, 0];
|
|
||||||
const vertices: any = []; // a hexagon vertices
|
|
||||||
for (let v = 0; v < polygon.length; v++) {
|
|
||||||
vertices.push([origin[0] + polygon[v][0], origin[1] + polygon[v][1]]);
|
|
||||||
}
|
|
||||||
const linePath = d3.line();
|
|
||||||
linePath.curve(d3.curveLinearClosed);
|
|
||||||
const hexPolygon = graph.value.append("g");
|
|
||||||
hexPolygon
|
|
||||||
.append("path")
|
|
||||||
.attr("d", linePath(vertices))
|
|
||||||
.attr("stroke", "#D5DDF6")
|
|
||||||
.attr("stroke-width", 2)
|
|
||||||
.style("fill", "none");
|
|
||||||
hexPolygon
|
|
||||||
.append("text")
|
|
||||||
.attr("fill", "#000")
|
|
||||||
.attr("text-anchor", "middle")
|
|
||||||
.attr("x", 0)
|
|
||||||
.attr("y", p.radius - 15)
|
|
||||||
.text(() => selectorStore.currentPod.label);
|
|
||||||
const nodeArr = networkProfilingStore.nodes.filter(
|
const nodeArr = networkProfilingStore.nodes.filter(
|
||||||
(d: ProcessNode) => d.isReal || d.name === "UNKNOWN_LOCAL"
|
(d: ProcessNode) => d.isReal || d.name === "UNKNOWN_LOCAL"
|
||||||
);
|
);
|
||||||
@@ -278,67 +346,11 @@ function createLayout() {
|
|||||||
outNodes[v].x = pointArr[v][0];
|
outNodes[v].x = pointArr[v][0];
|
||||||
outNodes[v].y = pointArr[v][1];
|
outNodes[v].y = pointArr[v][1];
|
||||||
}
|
}
|
||||||
drawTopology([...nodeArr, ...outNodes]);
|
nodeList.value = [...nodeArr, ...outNodes];
|
||||||
}
|
const drag: any = d3.drag().on("drag", (d: ProcessNode) => {
|
||||||
|
moveNode(d);
|
||||||
function drawTopology(nodeArr: any[]) {
|
});
|
||||||
node.value = node.value.data(nodeArr, (d: ProcessNode) => d.id);
|
d3.selectAll(".node").call(drag);
|
||||||
node.value.exit().remove();
|
|
||||||
node.value = nodeElement(
|
|
||||||
d3,
|
|
||||||
node.value.enter(),
|
|
||||||
{
|
|
||||||
tipHtml: (data: ProcessNode) => {
|
|
||||||
return ` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tip.value
|
|
||||||
).merge(node.value);
|
|
||||||
// line element
|
|
||||||
const obj = {} as any;
|
|
||||||
const calls = networkProfilingStore.calls.reduce((prev: any[], next: any) => {
|
|
||||||
if (obj[next.targetId + next.sourceId]) {
|
|
||||||
next.lowerArc = true;
|
|
||||||
}
|
|
||||||
obj[next.sourceId + next.targetId] = true;
|
|
||||||
prev.push(next);
|
|
||||||
return prev;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
link.value = link.value.data(calls, (d: Call) => d.id);
|
|
||||||
link.value.exit().remove();
|
|
||||||
link.value = linkElement(link.value.enter()).merge(link.value);
|
|
||||||
anchor.value = anchor.value.data(calls, (d: Call) => d.id);
|
|
||||||
anchor.value.exit().remove();
|
|
||||||
anchor.value = anchorElement(
|
|
||||||
anchor.value.enter(),
|
|
||||||
{
|
|
||||||
handleLinkClick: handleLinkClick,
|
|
||||||
tipHtml: (data: Call) => {
|
|
||||||
const types = [...data.sourceComponents, ...data.targetComponents];
|
|
||||||
let l = "TCP";
|
|
||||||
if (types.includes("https")) {
|
|
||||||
l = "HTTPS";
|
|
||||||
}
|
|
||||||
if (types.includes("http")) {
|
|
||||||
l = "HTTP";
|
|
||||||
}
|
|
||||||
if (types.includes("tls")) {
|
|
||||||
l = "TLS";
|
|
||||||
}
|
|
||||||
const html = `<div><span class="grey">${t(
|
|
||||||
"detectPoint"
|
|
||||||
)}: </span>${data.detectPoints.join(" | ")}</div>
|
|
||||||
<div><span class="grey">Type: </span>${l}</div>`;
|
|
||||||
return html;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tip.value
|
|
||||||
).merge(anchor.value);
|
|
||||||
// arrow marker
|
|
||||||
arrow.value = arrow.value.data(calls, (d: Call) => d.id);
|
|
||||||
arrow.value.exit().remove();
|
|
||||||
arrow.value = arrowMarker(arrow.value.enter()).merge(arrow.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function shuffleArray(array: number[][]) {
|
function shuffleArray(array: number[][]) {
|
||||||
@@ -347,7 +359,6 @@ function shuffleArray(array: number[][]) {
|
|||||||
[array[i], array[j]] = [array[j], array[i]];
|
[array[i], array[j]] = [array[j], array[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLinkClick(event: any, d: Call) {
|
function handleLinkClick(event: any, d: Call) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
networkProfilingStore.setNode(null);
|
networkProfilingStore.setNode(null);
|
||||||
@@ -423,13 +434,100 @@ function resize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function freshNodes() {
|
async function freshNodes() {
|
||||||
svg.value.selectAll(".svg-graph").remove();
|
|
||||||
if (!networkProfilingStore.nodes.length) {
|
if (!networkProfilingStore.nodes.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
drawGraph();
|
drawGraph();
|
||||||
createLayout();
|
createLayout();
|
||||||
}
|
}
|
||||||
|
function startMoveNode(event: MouseEvent, d: ProcessNode) {
|
||||||
|
event.stopPropagation();
|
||||||
|
currentNode.value = d;
|
||||||
|
}
|
||||||
|
function stopMoveNode(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
currentNode.value = null;
|
||||||
|
}
|
||||||
|
function moveNode(d: ProcessNode) {
|
||||||
|
if (!currentNode.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inNode =
|
||||||
|
currentNode.value.isReal || currentNode.value.name === "UNKNOWN_LOCAL";
|
||||||
|
const diff = inNode ? -20 : 20;
|
||||||
|
const inside = posInHex(d.x || 0, d.y || 0, diff);
|
||||||
|
if (inNode) {
|
||||||
|
if (!inside) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (inside) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodeList.value = nodeList.value.map((node: ProcessNode) => {
|
||||||
|
if (currentNode.value && node.id === currentNode.value.id) {
|
||||||
|
node.x = d.x;
|
||||||
|
node.y = d.y;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function posInHex(posX: number, posY: number, diff: number) {
|
||||||
|
const halfSideLen = (radius + diff) / 2;
|
||||||
|
const mathSqrt3 = Math.sqrt(3);
|
||||||
|
const dx = Math.abs(origin[0] - posX);
|
||||||
|
const dy = Math.abs(origin[1] - posY);
|
||||||
|
|
||||||
|
if (dx < halfSideLen) {
|
||||||
|
return dy <= halfSideLen * mathSqrt3;
|
||||||
|
} else {
|
||||||
|
const maxY = -mathSqrt3 * (dx - halfSideLen) + halfSideLen * mathSqrt3;
|
||||||
|
return dy < maxY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNodeTip(d: ProcessNode, event: MouseEvent) {
|
||||||
|
const tipHtml = ` <div class="mb-5"><span class="grey">name: </span>${d.name}</div>`;
|
||||||
|
|
||||||
|
tooltip.value
|
||||||
|
.style("top", event.offsetY + "px")
|
||||||
|
.style("left", event.offsetX + "px")
|
||||||
|
.style("visibility", "visible")
|
||||||
|
.html(tipHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideNodeTip() {
|
||||||
|
tooltip.value.style("visibility", "hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLinkTip(link: Call, event: MouseEvent) {
|
||||||
|
const types = [...link.sourceComponents, ...link.targetComponents];
|
||||||
|
let l = "TCP";
|
||||||
|
if (types.includes("https")) {
|
||||||
|
l = "HTTPS";
|
||||||
|
}
|
||||||
|
if (types.includes("http")) {
|
||||||
|
l = "HTTP";
|
||||||
|
}
|
||||||
|
if (types.includes("tls")) {
|
||||||
|
l = "TLS";
|
||||||
|
}
|
||||||
|
const tipHtml = `<div><span class="grey">${t(
|
||||||
|
"detectPoint"
|
||||||
|
)}: </span>${link.detectPoints.join(" | ")}</div>
|
||||||
|
<div><span class="grey">Type: </span>${l}</div>`;
|
||||||
|
|
||||||
|
tooltip.value
|
||||||
|
.style("top", event.offsetY + "px")
|
||||||
|
.style("left", event.offsetX + "px")
|
||||||
|
.style("visibility", "visible")
|
||||||
|
.html(tipHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLinkTip() {
|
||||||
|
tooltip.value.style("visibility", "hidden");
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => networkProfilingStore.nodes,
|
() => networkProfilingStore.nodes,
|
||||||
@@ -500,4 +598,13 @@ watch(
|
|||||||
.query {
|
.query {
|
||||||
margin-left: 510px;
|
margin-left: 510px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tooltip {
|
||||||
|
position: absolute;
|
||||||
|
visibility: hidden;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -17,24 +17,13 @@ limitations under the License. -->
|
|||||||
<div class="profile-task-wrapper flex-v">
|
<div class="profile-task-wrapper flex-v">
|
||||||
<div class="profile-t-tool">
|
<div class="profile-t-tool">
|
||||||
<span>{{ t("taskList") }}</span>
|
<span>{{ t("taskList") }}</span>
|
||||||
<span v-if="inProcess" class="new-task cp" @click="createTask">
|
<span class="new-task cp" @click="createTask">
|
||||||
<Icon
|
<Icon
|
||||||
:style="{ color: '#ccc' }"
|
:style="{ color: inProcess ? '#ccc' : '#000' }"
|
||||||
iconName="library_add"
|
iconName="library_add"
|
||||||
size="middle"
|
size="middle"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<el-popconfirm
|
|
||||||
title="Are you sure to create a task?"
|
|
||||||
@confirm="createTask"
|
|
||||||
v-else
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<span class="new-task cp">
|
|
||||||
<Icon iconName="library_add" size="middle" />
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-t-wrapper">
|
<div class="profile-t-wrapper">
|
||||||
<div
|
<div
|
||||||
@@ -87,6 +76,15 @@ limitations under the License. -->
|
|||||||
>
|
>
|
||||||
<TaskDetails :details="networkProfilingStore.selectedNetworkTask" />
|
<TaskDetails :details="networkProfilingStore.selectedNetworkTask" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog
|
||||||
|
v-model="newTask"
|
||||||
|
:title="t('taskTitle')"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
fullscreen
|
||||||
|
@closed="newTask = false"
|
||||||
|
>
|
||||||
|
<NewTask @create="saveNewTask" />
|
||||||
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
@@ -99,13 +97,15 @@ import TaskDetails from "../../components/TaskDetails.vue";
|
|||||||
import dateFormatStep, { dateFormat } from "@/utils/dateFormat";
|
import dateFormatStep, { dateFormat } from "@/utils/dateFormat";
|
||||||
import getLocalTime from "@/utils/localtime";
|
import getLocalTime from "@/utils/localtime";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import NewTask from "./NewTask.vue";
|
||||||
|
|
||||||
|
/*global Nullable */
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const selectorStore = useSelectorStore();
|
const selectorStore = useSelectorStore();
|
||||||
const networkProfilingStore = useNetworkProfilingStore();
|
const networkProfilingStore = useNetworkProfilingStore();
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
const viewDetail = ref<boolean>(false);
|
const viewDetail = ref<boolean>(false);
|
||||||
/*global Nullable */
|
const newTask = ref<boolean>(false);
|
||||||
const intervalFn = ref<Nullable<any>>(null);
|
const intervalFn = ref<Nullable<any>>(null);
|
||||||
const intervalKeepAlive = ref<Nullable<any>>(null);
|
const intervalKeepAlive = ref<Nullable<any>>(null);
|
||||||
const inProcess = ref<boolean>(false);
|
const inProcess = ref<boolean>(false);
|
||||||
@@ -163,28 +163,35 @@ async function getTopology() {
|
|||||||
}
|
}
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
async function createTask() {
|
function createTask() {
|
||||||
if (inProcess.value) {
|
if (inProcess.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const serviceId =
|
newTask.value = true;
|
||||||
(selectorStore.currentService && selectorStore.currentService.id) || "";
|
}
|
||||||
const serviceInstanceId =
|
async function saveNewTask(
|
||||||
|
params: {
|
||||||
|
uriRegex: string;
|
||||||
|
when4xx: string;
|
||||||
|
when5xx: string;
|
||||||
|
minDuration: number;
|
||||||
|
}[]
|
||||||
|
) {
|
||||||
|
const instanceId =
|
||||||
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
|
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
|
||||||
if (!serviceId) {
|
if (!instanceId) {
|
||||||
return;
|
return ElMessage.error("No Instance ID");
|
||||||
}
|
}
|
||||||
if (!serviceInstanceId) {
|
const res = await networkProfilingStore.createNetworkTask(instanceId, params);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const res = await networkProfilingStore.createNetworkTask({
|
|
||||||
serviceId,
|
|
||||||
serviceInstanceId,
|
|
||||||
});
|
|
||||||
if (res.errors) {
|
if (res.errors) {
|
||||||
ElMessage.error(res.errors);
|
ElMessage.error(res.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!res.data.createEBPFNetworkProfiling.status) {
|
||||||
|
ElMessage.error(res.data.createEBPFNetworkProfiling.errorReason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newTask.value = false;
|
||||||
await fetchTasks();
|
await fetchTasks();
|
||||||
}
|
}
|
||||||
function enableInterval() {
|
function enableInterval() {
|
||||||
@@ -239,6 +246,7 @@ async function fetchTasks() {
|
|||||||
watch(
|
watch(
|
||||||
() => selectorStore.currentPod,
|
() => selectorStore.currentPod,
|
||||||
() => {
|
() => {
|
||||||
|
inProcess.value = false;
|
||||||
fetchTasks();
|
fetchTasks();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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 const ProfileMode: any[] = [
|
||||||
|
{ label: "Include Children", value: "include" },
|
||||||
|
{ label: "Exclude Children", value: "exclude" },
|
||||||
|
];
|
||||||
|
export const NewTaskField = {
|
||||||
|
service: { key: "", label: "None" },
|
||||||
|
monitorTime: { key: "0", label: "monitor now" },
|
||||||
|
monitorDuration: { key: 5, label: "5 min" },
|
||||||
|
minThreshold: 0,
|
||||||
|
dumpPeriod: { key: 10, label: "10ms" },
|
||||||
|
endpointName: "",
|
||||||
|
maxSamplingCount: { key: 5, label: "5" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InitTaskField = {
|
||||||
|
Whenxx: [
|
||||||
|
{ value: "1", label: "True" },
|
||||||
|
{ value: "0", label: "False" },
|
||||||
|
],
|
||||||
|
};
|
@@ -119,8 +119,8 @@ function updateTimeRange() {
|
|||||||
if (!children || !children.length) {
|
if (!children || !children.length) {
|
||||||
timeRange.value = [
|
timeRange.value = [
|
||||||
{
|
{
|
||||||
start: this.currentSpan.startTime,
|
start: startTime,
|
||||||
end: this.currentSpan.endTime,
|
end: endTime,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return;
|
return;
|
||||||
|
@@ -90,6 +90,7 @@ import {
|
|||||||
reactive,
|
reactive,
|
||||||
watch,
|
watch,
|
||||||
computed,
|
computed,
|
||||||
|
nextTick,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
@@ -111,9 +112,9 @@ import { Service } from "@/types/selector";
|
|||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
import { MetricConfigOpt } from "@/types/dashboard";
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
import { aggregation } from "@/hooks/useProcessor";
|
import { aggregation } from "@/hooks/useMetricsProcessor";
|
||||||
import icons from "@/assets/img/icons";
|
import icons from "@/assets/img/icons";
|
||||||
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
|
import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
|
||||||
|
|
||||||
/*global Nullable, defineProps */
|
/*global Nullable, defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -149,6 +150,14 @@ const graphConfig = computed(() => props.config.graph || {});
|
|||||||
const depth = ref<number>(graphConfig.value.depth || 2);
|
const depth = ref<number>(graphConfig.value.depth || 2);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
const dom = document.querySelector(".topology")?.getBoundingClientRect() || {
|
||||||
|
height: 40,
|
||||||
|
width: 0,
|
||||||
|
};
|
||||||
|
height.value = dom.height - 40;
|
||||||
|
width.value = dom.width;
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const json = await selectorStore.fetchServices(dashboardStore.layerId);
|
const json = await selectorStore.fetchServices(dashboardStore.layerId);
|
||||||
if (json.errors) {
|
if (json.errors) {
|
||||||
@@ -157,18 +166,13 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
const resp = await getTopology();
|
const resp = await getTopology();
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
||||||
if (resp && resp.errors) {
|
if (resp && resp.errors) {
|
||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
}
|
}
|
||||||
topologyStore.getLinkClientMetrics(settings.value.linkClientMetrics || []);
|
topologyStore.getLinkClientMetrics(settings.value.linkClientMetrics || []);
|
||||||
topologyStore.getLinkServerMetrics(settings.value.linkServerMetrics || []);
|
topologyStore.getLinkServerMetrics(settings.value.linkServerMetrics || []);
|
||||||
topologyStore.queryNodeMetrics(settings.value.nodeMetrics || []);
|
topologyStore.queryNodeMetrics(settings.value.nodeMetrics || []);
|
||||||
const dom = document.querySelector(".topology")?.getBoundingClientRect() || {
|
|
||||||
height: 40,
|
|
||||||
width: 0,
|
|
||||||
};
|
|
||||||
height.value = dom.height - 40;
|
|
||||||
width.value = dom.width;
|
|
||||||
window.addEventListener("resize", resize);
|
window.addEventListener("resize", resize);
|
||||||
svg.value = d3.select(chart.value).append("svg").attr("class", "topo-svg");
|
svg.value = d3.select(chart.value).append("svg").attr("class", "topo-svg");
|
||||||
await initLegendMetrics();
|
await initLegendMetrics();
|
||||||
@@ -733,4 +737,8 @@ watch(
|
|||||||
stroke-dashoffset: 0;
|
stroke-dashoffset: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-loading-spinner {
|
||||||
|
top: 30%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -21,7 +21,7 @@ import { computed, PropType } from "vue";
|
|||||||
import { useTopologyStore } from "@/store/modules/topology";
|
import { useTopologyStore } from "@/store/modules/topology";
|
||||||
import { Node, Call } from "@/types/topology";
|
import { Node, Call } from "@/types/topology";
|
||||||
import { MetricConfigOpt } from "@/types/dashboard";
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
import { aggregation } from "@/hooks/useProcessor";
|
import { aggregation } from "@/hooks/useMetricsProcessor";
|
||||||
|
|
||||||
/*global defineEmits, defineProps */
|
/*global defineEmits, defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@@ -248,7 +248,7 @@ import { useTopologyStore } from "@/store/modules/topology";
|
|||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { MetricCatalog, ScopeType, MetricConditions } from "../../../data";
|
import { MetricCatalog, ScopeType, MetricConditions } from "../../../data";
|
||||||
import { Option } from "@/types/app";
|
import { Option } from "@/types/app";
|
||||||
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
|
import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
|
||||||
import { Node } from "@/types/topology";
|
import { Node } from "@/types/topology";
|
||||||
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
|
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
|
||||||
import { EntityType, LegendOpt, MetricsType } from "../../../data";
|
import { EntityType, LegendOpt, MetricsType } from "../../../data";
|
||||||
|
@@ -17,11 +17,6 @@ limitations under the License. -->
|
|||||||
v-if="traceStore.currentTrace.endpointNames"
|
v-if="traceStore.currentTrace.endpointNames"
|
||||||
>
|
>
|
||||||
<h5 class="mb-5 mt-0">
|
<h5 class="mb-5 mt-0">
|
||||||
<Icon
|
|
||||||
icon="clear"
|
|
||||||
v-if="traceStore.currentTrace.isError"
|
|
||||||
class="red mr-5 sm"
|
|
||||||
/>
|
|
||||||
<span class="vm">{{ traceStore.currentTrace.endpointNames[0] }}</span>
|
<span class="vm">{{ traceStore.currentTrace.endpointNames[0] }}</span>
|
||||||
<div class="trace-log-btn">
|
<div class="trace-log-btn">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -34,7 +29,7 @@ limitations under the License. -->
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</h5>
|
</h5>
|
||||||
<div class="mb-5 blue sm">
|
<div class="mb-5 blue">
|
||||||
<Selector
|
<Selector
|
||||||
size="small"
|
size="small"
|
||||||
:value="
|
:value="
|
||||||
@@ -46,12 +41,7 @@ limitations under the License. -->
|
|||||||
@change="changeTraceId"
|
@change="changeTraceId"
|
||||||
class="trace-detail-ids"
|
class="trace-detail-ids"
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon class="cp ml-5" iconName="copy" @click="handleClick" />
|
||||||
size="sm"
|
|
||||||
class="icon grey link-hover cp ml-5"
|
|
||||||
iconName="review-list"
|
|
||||||
@click="handleClick"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-h item">
|
<div class="flex-h item">
|
||||||
<div>
|
<div>
|
||||||
|
@@ -95,17 +95,15 @@ limitations under the License. -->
|
|||||||
import { ref, reactive, watch, onUnmounted } from "vue";
|
import { ref, reactive, watch, onUnmounted } from "vue";
|
||||||
import type { PropType } from "vue";
|
import type { PropType } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { Option } from "@/types/app";
|
import { Option, DurationTime } from "@/types/app";
|
||||||
import { Status } from "../../data";
|
|
||||||
import { useTraceStore } from "@/store/modules/trace";
|
import { useTraceStore } from "@/store/modules/trace";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import ConditionTags from "@/views/components/ConditionTags.vue";
|
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { EntityType } from "../../data";
|
import { EntityType, QueryOrders, Status } from "../../data";
|
||||||
import { LayoutConfig } from "@/types/dashboard";
|
import { LayoutConfig } from "@/types/dashboard";
|
||||||
import { DurationTime } from "@/types/app";
|
|
||||||
|
|
||||||
/*global defineProps, Recordable */
|
/*global defineProps, Recordable */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -115,33 +113,35 @@ const props = defineProps({
|
|||||||
default: () => ({ graph: {} }),
|
default: () => ({ graph: {} }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const traceId = ref<string>(
|
const filters = reactive<Recordable>(props.data.filters || {});
|
||||||
(props.data.filters && props.data.filters.traceId) || ""
|
const traceId = ref<string>(filters.traceId || "");
|
||||||
);
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
const selectorStore = useSelectorStore();
|
const selectorStore = useSelectorStore();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const traceStore = useTraceStore();
|
const traceStore = useTraceStore();
|
||||||
const duration = ref<DurationTime>(
|
const duration = ref<DurationTime>(filters.duration || appStore.durationTime);
|
||||||
(props.data.filters && props.data.filters.duration) || appStore.durationTime
|
|
||||||
);
|
|
||||||
const minTraceDuration = ref<number>();
|
const minTraceDuration = ref<number>();
|
||||||
const maxTraceDuration = ref<number>();
|
const maxTraceDuration = ref<number>();
|
||||||
const tagsList = ref<string[]>([]);
|
const tagsList = ref<string[]>([]);
|
||||||
const tagsMap = ref<Option[]>([]);
|
const tagsMap = ref<Option[]>([]);
|
||||||
const state = reactive<Recordable>({
|
const state = reactive<Recordable>({
|
||||||
status: { label: "All", value: "ALL" },
|
status: filters.status === "ERROR" ? Status[2] : Status[0],
|
||||||
instance: { value: "0", label: "All" },
|
instance: { value: "0", label: "All" },
|
||||||
endpoint: { value: "0", label: "All" },
|
endpoint: { value: "0", label: "All" },
|
||||||
service: { value: "", label: "" },
|
service: { value: "", label: "" },
|
||||||
});
|
});
|
||||||
|
if (filters.queryOrder) {
|
||||||
|
traceStore.setTraceCondition({
|
||||||
|
queryOrder: filters.queryOrder,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (props.needQuery) {
|
if (props.needQuery) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
|
duration.value = filters.duration || appStore.durationTime;
|
||||||
if (dashboardStore.entity === EntityType[1].value) {
|
if (dashboardStore.entity === EntityType[1].value) {
|
||||||
await getServices();
|
await getServices();
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ async function getServices() {
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.service = traceStore.services[0];
|
state.service = getCurrentNode(traceStore.services) || traceStore.services[0];
|
||||||
getEndpoints(state.service.id);
|
getEndpoints(state.service.id);
|
||||||
getInstances(state.service.id);
|
getInstances(state.service.id);
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,8 @@ async function getEndpoints(id?: string, keyword?: string) {
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.endpoint = traceStore.endpoints[0];
|
state.endpoint =
|
||||||
|
getCurrentNode(traceStore.endpoints) || traceStore.endpoints[0];
|
||||||
}
|
}
|
||||||
async function getInstances(id?: string) {
|
async function getInstances(id?: string) {
|
||||||
const resp = await traceStore.getInstances(id);
|
const resp = await traceStore.getInstances(id);
|
||||||
@@ -183,9 +184,39 @@ async function getInstances(id?: string) {
|
|||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.instance = traceStore.instances[0];
|
state.instance =
|
||||||
|
getCurrentNode(traceStore.instances) || traceStore.instances[0];
|
||||||
}
|
}
|
||||||
function searchTraces() {
|
function getCurrentNode(arr: { id: string }[]) {
|
||||||
|
let item;
|
||||||
|
if (!props.data.filters) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
if (props.data.filters.id) {
|
||||||
|
item = arr.find((d: { id: string }) => d.id === props.data.filters?.id);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
function setCondition() {
|
||||||
|
let param: any = {
|
||||||
|
traceState: state.status.value || "ALL",
|
||||||
|
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
||||||
|
queryOrder: traceStore.conditions.queryOrder || QueryOrders[1].value,
|
||||||
|
queryDuration: duration.value,
|
||||||
|
minTraceDuration: Number(minTraceDuration.value),
|
||||||
|
maxTraceDuration: Number(maxTraceDuration.value),
|
||||||
|
traceId: traceId.value || undefined,
|
||||||
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
|
};
|
||||||
|
if (props.data.filters && props.data.filters.id) {
|
||||||
|
param = {
|
||||||
|
...param,
|
||||||
|
serviceId: selectorStore.currentService.id,
|
||||||
|
endpointId: state.endpoint.id || undefined,
|
||||||
|
serviceInstanceId: state.instance.id || undefined,
|
||||||
|
};
|
||||||
|
return param;
|
||||||
|
}
|
||||||
let endpoint = "",
|
let endpoint = "",
|
||||||
instance = "";
|
instance = "";
|
||||||
if (dashboardStore.entity === EntityType[2].value) {
|
if (dashboardStore.entity === EntityType[2].value) {
|
||||||
@@ -194,21 +225,18 @@ function searchTraces() {
|
|||||||
if (dashboardStore.entity === EntityType[3].value) {
|
if (dashboardStore.entity === EntityType[3].value) {
|
||||||
instance = selectorStore.currentPod.id;
|
instance = selectorStore.currentPod.id;
|
||||||
}
|
}
|
||||||
traceStore.setTraceCondition({
|
param = {
|
||||||
|
...param,
|
||||||
serviceId: selectorStore.currentService
|
serviceId: selectorStore.currentService
|
||||||
? selectorStore.currentService.id
|
? selectorStore.currentService.id
|
||||||
: state.service.id,
|
: state.service.id,
|
||||||
traceId: traceId.value || undefined,
|
|
||||||
endpointId: endpoint || state.endpoint.id || undefined,
|
endpointId: endpoint || state.endpoint.id || undefined,
|
||||||
serviceInstanceId: instance || state.instance.id || undefined,
|
serviceInstanceId: instance || state.instance.id || undefined,
|
||||||
traceState: state.status.value || "ALL",
|
};
|
||||||
queryDuration: duration.value,
|
return param;
|
||||||
minTraceDuration: Number(minTraceDuration.value),
|
}
|
||||||
maxTraceDuration: Number(maxTraceDuration.value),
|
function searchTraces() {
|
||||||
queryOrder: traceStore.conditions.queryOrder || "BY_DURATION",
|
traceStore.setTraceCondition(setCondition());
|
||||||
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
|
||||||
paging: { pageNum: 1, pageSize: 20 },
|
|
||||||
});
|
|
||||||
queryTraces();
|
queryTraces();
|
||||||
}
|
}
|
||||||
async function queryTraces() {
|
async function queryTraces() {
|
||||||
@@ -263,10 +291,13 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// Event widget associate with trace widget
|
||||||
watch(
|
watch(
|
||||||
() => props.data.filters,
|
() => props.data.filters,
|
||||||
(newJson, oldJson) => {
|
(newJson, oldJson) => {
|
||||||
if (props.data.filters) {
|
if (!props.data.filters) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (JSON.stringify(newJson) === JSON.stringify(oldJson)) {
|
if (JSON.stringify(newJson) === JSON.stringify(oldJson)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -274,7 +305,6 @@ watch(
|
|||||||
duration.value = props.data.filters.duration || appStore.durationTime;
|
duration.value = props.data.filters.duration || appStore.durationTime;
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
290
src/views/dashboard/related/trace/Header.vue
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
<!-- 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. -->
|
||||||
|
<template>
|
||||||
|
<div v-if="filters.id" class="conditions flex-h">
|
||||||
|
<div class="label grey">TraceId:</div>
|
||||||
|
<el-input size="small" v-model="traceId" class="trace-id" />
|
||||||
|
</div>
|
||||||
|
<div class="conditions flex-h" v-else>
|
||||||
|
<el-radio-group v-model="conditions" @change="changeCondition" size="small">
|
||||||
|
<el-radio-button
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
:label="item.label"
|
||||||
|
:key="item.label + index"
|
||||||
|
border
|
||||||
|
>
|
||||||
|
{{ t(item.label) }}
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<Selector
|
||||||
|
v-if="conditions === 'latency' && filters.latency.length > 1"
|
||||||
|
:value="filters.latency[0].value"
|
||||||
|
:options="filters.latency"
|
||||||
|
placeholder="Select a option"
|
||||||
|
@change="changeLatency"
|
||||||
|
class="ml-10"
|
||||||
|
/>
|
||||||
|
<el-popover trigger="hover" width="250" placement="bottom" effect="light">
|
||||||
|
<template #reference>
|
||||||
|
<div class="cp conditions-popup">
|
||||||
|
<Icon iconName="conditions" size="middle" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div class="title">{{ t("queryConditions") }}</div>
|
||||||
|
<div v-for="key in Object.keys(FiltersKeys)" :key="key">
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
[
|
||||||
|
FiltersKeys.minTraceDuration,
|
||||||
|
FiltersKeys.maxTraceDuration,
|
||||||
|
].includes(key) && !isNaN(traceStore.conditions[FiltersKeys[key]])
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ t(key) }}: {{ traceStore.conditions[FiltersKeys[key]] }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="key !== 'duration'">
|
||||||
|
{{ t(key) }}: {{ traceStore.conditions[FiltersKeys[key]] }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<el-popover trigger="hover" width="250" placement="bottom" effect="light">
|
||||||
|
<template #reference>
|
||||||
|
<div class="cp metric-value">
|
||||||
|
<Icon iconName="info_outline" size="middle" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div class="title">{{ t("metricValues") }}</div>
|
||||||
|
<div v-for="metric in filters.metricValue" :key="metric.value">
|
||||||
|
{{ metric.label }}: {{ metric.data }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div class="flex-h">
|
||||||
|
<ConditionTags :type="'TRACE'" @update="updateTags" />
|
||||||
|
<div class="search-btn">
|
||||||
|
<el-button size="small" type="primary" @click="queryTraces">
|
||||||
|
{{ t("search") }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, onUnmounted } from "vue";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { Option, DurationTime } from "@/types/app";
|
||||||
|
import { useTraceStore } from "@/store/modules/trace";
|
||||||
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
|
import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { EntityType, QueryOrders, Status } from "../../data";
|
||||||
|
import { LayoutConfig } from "@/types/dashboard";
|
||||||
|
|
||||||
|
const FiltersKeys: { [key: string]: string } = {
|
||||||
|
status: "traceState",
|
||||||
|
queryOrder: "queryOrder",
|
||||||
|
duration: "queryDuration",
|
||||||
|
minTraceDuration: "minTraceDuration",
|
||||||
|
maxTraceDuration: "maxTraceDuration",
|
||||||
|
};
|
||||||
|
/*global defineProps, Recordable */
|
||||||
|
const props = defineProps({
|
||||||
|
needQuery: { type: Boolean, default: true },
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<LayoutConfig>,
|
||||||
|
default: () => ({ graph: {} }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const filters = reactive<Recordable>(props.data.filters || {});
|
||||||
|
const appStore = useAppStoreWithOut();
|
||||||
|
const selectorStore = useSelectorStore();
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const traceStore = useTraceStore();
|
||||||
|
const tagsList = ref<string[]>([]);
|
||||||
|
const tagsMap = ref<Option[]>([]);
|
||||||
|
const traceId = ref<string>(filters.refId || "");
|
||||||
|
const duration = ref<DurationTime>(filters.duration || appStore.durationTime);
|
||||||
|
const state = reactive<Recordable>({
|
||||||
|
instance: "",
|
||||||
|
endpoint: "",
|
||||||
|
service: "",
|
||||||
|
});
|
||||||
|
const conditions = ref<string>("");
|
||||||
|
const items = ref<{ label: string; value: string }[]>([]);
|
||||||
|
const currentLatency = ref<number[]>(
|
||||||
|
filters.latency ? filters.latency[0].data : []
|
||||||
|
);
|
||||||
|
init();
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
for (const d of Object.keys(filters)) {
|
||||||
|
if (
|
||||||
|
(d === "queryOrder" &&
|
||||||
|
filters[d] &&
|
||||||
|
filters[d] === QueryOrders[1].value) ||
|
||||||
|
(d === "status" && filters[d] && filters[d] !== Status[0].value) ||
|
||||||
|
(filters[d] && d === "latency")
|
||||||
|
) {
|
||||||
|
items.value.push({ label: d, value: FiltersKeys[d] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conditions.value = (items.value[0] && items.value[0].label) || "";
|
||||||
|
if (!filters.id) {
|
||||||
|
state.service = selectorStore.currentService.id;
|
||||||
|
if (dashboardStore.entity === EntityType[2].value) {
|
||||||
|
state.endpoint = selectorStore.currentPod.id;
|
||||||
|
}
|
||||||
|
if (dashboardStore.entity === EntityType[3].value) {
|
||||||
|
state.instance = selectorStore.currentPod.id;
|
||||||
|
}
|
||||||
|
await queryTraces();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dashboardStore.entity === EntityType[1].value) {
|
||||||
|
await getService();
|
||||||
|
}
|
||||||
|
if (dashboardStore.entity === EntityType[0].value) {
|
||||||
|
state.service = selectorStore.currentService.id;
|
||||||
|
await getInstance();
|
||||||
|
if (!state.instance) {
|
||||||
|
await getEndpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await queryTraces();
|
||||||
|
}
|
||||||
|
function changeCondition() {
|
||||||
|
if (conditions.value === "latency") {
|
||||||
|
currentLatency.value = filters.latency ? filters.latency[0].data : [];
|
||||||
|
}
|
||||||
|
queryTraces();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeLatency(options: any[]) {
|
||||||
|
currentLatency.value = options[0].data;
|
||||||
|
queryTraces();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getService() {
|
||||||
|
const resp = await traceStore.getService(filters.id);
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.service = (resp.data.service && resp.data.service) || "";
|
||||||
|
}
|
||||||
|
async function getEndpoint() {
|
||||||
|
const resp = await traceStore.getEndpoint(filters.id);
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.endpoint = (resp.data.endpoint && resp.data.endpoint.id) || "";
|
||||||
|
}
|
||||||
|
async function getInstance() {
|
||||||
|
const resp = await traceStore.getInstance(filters.id);
|
||||||
|
if (resp.errors) {
|
||||||
|
ElMessage.error(resp.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.instance = (resp.data.instance && resp.data.instance.id) || "";
|
||||||
|
}
|
||||||
|
function setCondition() {
|
||||||
|
let params: any = {
|
||||||
|
traceState: Status[0].value,
|
||||||
|
queryOrder: QueryOrders[0].value,
|
||||||
|
queryDuration: duration.value,
|
||||||
|
minTraceDuration: currentLatency.value[0],
|
||||||
|
maxTraceDuration: currentLatency.value[1],
|
||||||
|
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
||||||
|
paging: { pageNum: 1, pageSize: 20 },
|
||||||
|
serviceId: state.service || undefined,
|
||||||
|
endpointId: state.endpoint || undefined,
|
||||||
|
serviceInstanceId: state.instance || undefined,
|
||||||
|
traceId: traceId.value || undefined,
|
||||||
|
};
|
||||||
|
for (const k of items.value) {
|
||||||
|
if (
|
||||||
|
k.label === conditions.value &&
|
||||||
|
FiltersKeys[k.label] &&
|
||||||
|
filters[k.label]
|
||||||
|
) {
|
||||||
|
params[k.value] = filters[k.label];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isNaN(params.minTraceDuration)) {
|
||||||
|
params.queryOrder = QueryOrders[1].value;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
async function queryTraces() {
|
||||||
|
traceStore.setTraceCondition(setCondition());
|
||||||
|
const res = await traceStore.getTraces();
|
||||||
|
if (res && res.errors) {
|
||||||
|
ElMessage.error(res.errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateTags(data: { tagsMap: Array<Option>; tagsList: string[] }) {
|
||||||
|
tagsList.value = data.tagsList;
|
||||||
|
tagsMap.value = data.tagsMap;
|
||||||
|
}
|
||||||
|
onUnmounted(() => {
|
||||||
|
traceStore.resetState();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.row {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value {
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-popup {
|
||||||
|
padding-left: 10px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-id {
|
||||||
|
width: 300px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
</style>
|