feat: implement dashboard layouts (#7)

This commit is contained in:
吴晟 Wu Sheng 2021-12-27 16:14:17 +08:00 committed by GitHub
commit 6ee2e0dd9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1224 additions and 246 deletions

31
dist/LICENSE vendored
View File

@ -7,15 +7,22 @@ vuex 4.0.2: https://github.com/vuejs/vuex MIT
dayjs 1.10.7: https://github.com/iamkun/dayjs MIT dayjs 1.10.7: https://github.com/iamkun/dayjs MIT
lodash 4.17.21: https://github.com/lodash/lodash MIT lodash 4.17.21: https://github.com/lodash/lodash MIT
vue-i18n 9.1.9: https://github.com/kazupon/vue-i18n MIT vue-i18n 9.1.9: https://github.com/kazupon/vue-i18n MIT
pinia 2.0.5: github.com/posva/pinia MIT pinia 2.0.5: https://github.com/posva/pinia MIT
normalize-wheel-es 1.1.1: github.com/sxzz/normalize-wheel-es MIT normalize-wheel-es 1.1.1: https://github.com/sxzz/normalize-wheel-es MIT
async-validator 4.0.7: github.com/yiminghe/async-validator MIT async-validator 4.0.7: https://github.com/yiminghe/async-validator MIT
axios 0.24.0: github.com/axios/axios MIT axios 0.24.0: https://github.com/axios/axios MIT
memoize-one 6.0.0: github.com/alexreardon/memoize-one MIT memoize-one 6.0.0: https://github.com/alexreardon/memoize-one MIT
popperjs/core 2.10.2: github.com/popperjs/popper-core MIT popperjs/core 2.10.2: https://github.com/popperjs/popper-core MIT
three 0.131.3: github.com/mrdoob/three.js MIT three 0.131.3: https://github.com/mrdoob/three.js MIT
three-orbit-controls 82.1.0: github.com/mattdesl/three-orbit-controls MIT three-orbit-controls 82.1.0: https://github.com/mattdesl/three-orbit-controls MIT
vue-demi 0.12.1: github.com/antfu/vue-demi MIT vue-demi 0.12.1: https://github.com/antfu/vue-demi MIT
vueuse/core 6.8.0: github.com/vueuse/vueuse MIT vueuse/core 6.8.0: https://github.com/vueuse/vueuse MIT
vue/devtools-api 6.0.0-beta.20.1: github.com/vuejs/vue-devtools MIT vue/devtools-api 6.0.0-beta.20.1: https://github.com/vuejs/vue-devtools MIT
element-plus 1.2.0-beta.3: github.com/element-plus/element-plus MIT element-plus 1.2.0-beta.3: https://github.com/element-plus/element-plus MIT
vue-types 4.1.1: https://github.com/dwightjack/vue-types MIT
is-plain-object 5.0.0: https://github.com/jonschlinkert/is-plain-object MIT
vue-grid-layout 3.0.0-beta1: https://github.com/jbaysolutions/vue-grid-layout MIT
interactjs 1.10.11: https://github.com/taye/interact.js MIT
mitt 1.1.2: https://github.com/developit/mitt MIT
element-resize-detector 1.2.4: https://github.com/wnr/element-resize-detector MIT
batch-processor 1.0.0: https://github.com/wnr/batch-processor MIT

21
dist/licenses/LICENSE-batch-processor vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

21
dist/licenses/LICENSE-interactjs vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

21
dist/licenses/LICENSE-is-plain-object vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

21
dist/licenses/LICENSE-mitt vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

21
dist/licenses/LICENSE-vue-grid-layout vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

21
dist/licenses/LICENSE-vue-types vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

259
package-lock.json generated
View File

@ -1321,6 +1321,132 @@
"@hapi/hoek": "^8.3.0" "@hapi/hoek": "^8.3.0"
} }
}, },
"@interactjs/actions": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/actions/-/actions-1.10.11.tgz",
"integrity": "sha512-P39zeefr4hkmKx+5nZ+mrH1s0l2YJ3gIHrthXmE81n6MlMa42m0WtHcTms4C5JTTNBP2EEDY+KGgGxSnmJKvUw==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/auto-scroll": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/auto-scroll/-/auto-scroll-1.10.11.tgz",
"integrity": "sha512-feHNjhi0EMNLV2nQcEgjYPz2mI54aeSW2RiaoNtFLyBvtXKp0b4DmluwDv6DvuXmUpDwD5g/Hk1gGM2rgl7iqQ==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/auto-start": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/auto-start/-/auto-start-1.10.11.tgz",
"integrity": "sha512-cIg5CcalCPtC6AiGq6j/0hKUtL2MweEpvw12FuB19sz2Q9Dye0J4GliHKhOYvtumNinnvfVAZ4FZMqZEuX7YZA==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/core": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/core/-/core-1.10.11.tgz",
"integrity": "sha512-aJ50ccVeszpJt7wPH7Yfqm7f1aG1SA94qd90P0NaESh5/QUXn4CESO6igobo4DFHQ5z+1Rfdl8aphP4JxlH4gw=="
},
"@interactjs/dev-tools": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/dev-tools/-/dev-tools-1.10.11.tgz",
"integrity": "sha512-BP2FNfMbF7zLuOAUGMkDhCo1e1B0fnqyb9ih/Y8yAIJuoLrZxP/9htbsS1vZOIVZ4UgtrId4cYOwfcAZBMQtmw==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/inertia": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/inertia/-/inertia-1.10.11.tgz",
"integrity": "sha512-h+sknCzRqBSyHy4ctPNsq56mxkAMMdwHWD6en7rDEw899gdGKYaXVDVdv1jMfiwNRw0eRFBNoCiol8r3a/a3Jw==",
"requires": {
"@interactjs/interact": "1.10.11",
"@interactjs/offset": "1.10.11"
}
},
"@interactjs/interact": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/interact/-/interact-1.10.11.tgz",
"integrity": "sha512-0iZJ9l547JuBA/lKxK4ARGYVmMqRSsAdA8gXL1zWe51qEIQq8PyWmMipoi8JbDaL7exC2THKwkXu5uq5ndT+iA==",
"requires": {
"@interactjs/core": "1.10.11",
"@interactjs/types": "1.10.11",
"@interactjs/utils": "1.10.11"
}
},
"@interactjs/interactjs": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/interactjs/-/interactjs-1.10.11.tgz",
"integrity": "sha512-cGOxf6rp3Y8/sk88LhIT0XDn4gCiCzAnUG5Kkj9SAqiUO6BK/9+Wbp1IBkNaPgl/8uG8gNHh/dXBrlBBNcqJAg==",
"requires": {
"@interactjs/actions": "1.10.11",
"@interactjs/auto-scroll": "1.10.11",
"@interactjs/auto-start": "1.10.11",
"@interactjs/core": "1.10.11",
"@interactjs/dev-tools": "1.10.11",
"@interactjs/inertia": "1.10.11",
"@interactjs/interact": "1.10.11",
"@interactjs/modifiers": "1.10.11",
"@interactjs/offset": "1.10.11",
"@interactjs/pointer-events": "1.10.11",
"@interactjs/reflow": "1.10.11",
"@interactjs/utils": "1.10.11"
}
},
"@interactjs/modifiers": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/modifiers/-/modifiers-1.10.11.tgz",
"integrity": "sha512-ltqX1RSqeAIikixlQBlyEUdclT5+rbfIGi3sIdLLYaIZQnltYkWqL9MHKx/w5b+hV+Mc0p5MLUFWJbTdkSCZ9g==",
"requires": {
"@interactjs/interact": "1.10.11",
"@interactjs/snappers": "1.10.11"
}
},
"@interactjs/offset": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/offset/-/offset-1.10.11.tgz",
"integrity": "sha512-mBT7eIfy5ivofECiv+VwtEwwIMLV54fT9ujSMWJPduxdSYIHepUWgEf/3zjJknFh6jQc7pqz9dtjvVvyzRCLlQ==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/pointer-events": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/pointer-events/-/pointer-events-1.10.11.tgz",
"integrity": "sha512-yBT8JJVMZ+MgBay5l1WAHnL8ch/mZsRfaFahti+QFYeQyRloDtsWmEMDSYI/Onyy9+hS3gN/ge77ArGciZZ0Ow==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/reflow": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/reflow/-/reflow-1.10.11.tgz",
"integrity": "sha512-NSCtcCkjImOYSbxzzv2kFqR9t49J8KlhEr9UoePc7GyLbNXsiv3WQ3n0ehZd7CgZXQDiVXnP2UnmIOv5Zd4HQg==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/snappers": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/snappers/-/snappers-1.10.11.tgz",
"integrity": "sha512-yYtOMUZ7aFUZ1IYheq9Tj5hZ4J1r5dnaXhLF44WsI/awQ5L0DjZf07GPWof0B+7rZHEVudxyQNbPfFmb+1K94Q==",
"requires": {
"@interactjs/interact": "1.10.11"
}
},
"@interactjs/types": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.11.tgz",
"integrity": "sha512-YRsVFWjL8Gkkvlx3qnjeaxW4fnibSJ9791g8BA7Pv5ANByI64WmtR1vU7A2rXcrOn8XvyCEfY0ss1s8NhZP+MA=="
},
"@interactjs/utils": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@interactjs/utils/-/utils-1.10.11.tgz",
"integrity": "sha512-410ZoxKF+r1roeSelL+WHXfdryUMg5iykC1XwQ3l6XqNw43IMACzyvTH6k6Pwxj7w7x42nce0Qdn1GQ3Y8xyCw=="
},
"@intervolga/optimize-cssnano-plugin": { "@intervolga/optimize-cssnano-plugin": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", "resolved": "https://registry.npmjs.org/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz",
@ -4915,6 +5041,11 @@
"integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
"dev": true "dev": true
}, },
"batch-processor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz",
"integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg="
},
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -7632,6 +7763,14 @@
"normalize-wheel-es": "^1.1.0" "normalize-wheel-es": "^1.1.0"
} }
}, },
"element-resize-detector": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz",
"integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==",
"requires": {
"batch-processor": "1.0.0"
}
},
"elliptic": { "elliptic": {
"version": "6.5.4", "version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
@ -14726,6 +14865,90 @@
} }
} }
}, },
"postcss-html": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.3.0.tgz",
"integrity": "sha512-ewbwd7OGW4dLsErtvZH9HpVMEcXnlhYSzKsr7MepGlOT8imHTIZ/+pdfEruLS+hTYapLTQAWDnoQcJpsYU4uRw==",
"dev": true,
"requires": {
"htmlparser2": "^7.1.2",
"postcss": "^8.4.0",
"postcss-safe-parser": "^6.0.0"
},
"dependencies": {
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"dependencies": {
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
}
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"dev": true
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dev": true,
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true
},
"htmlparser2": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
"integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.2",
"domutils": "^2.8.0",
"entities": "^3.0.1"
}
},
"postcss": {
"version": "8.4.5",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz",
"integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==",
"dev": true,
"requires": {
"nanoid": "^3.1.30",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.1"
}
},
"source-map-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz",
"integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==",
"dev": true
}
}
},
"postcss-load-config": { "postcss-load-config": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz",
@ -19522,6 +19745,27 @@
} }
} }
}, },
"vue-grid-layout": {
"version": "3.0.0-beta1",
"resolved": "https://registry.npmjs.org/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz",
"integrity": "sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==",
"requires": {
"@interactjs/actions": "^1.10.2",
"@interactjs/auto-start": "^1.10.2",
"@interactjs/dev-tools": "^1.10.2",
"@interactjs/interactjs": "^1.10.2",
"@interactjs/modifiers": "^1.10.2",
"element-resize-detector": "^1.2.1",
"mitt": "^2.1.0"
},
"dependencies": {
"mitt": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
}
}
},
"vue-hot-reload-api": { "vue-hot-reload-api": {
"version": "2.3.4", "version": "2.3.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@ -19614,6 +19858,21 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true "dev": true
}, },
"vue-types": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/vue-types/-/vue-types-4.1.1.tgz",
"integrity": "sha512-Jq2GZ/w6rExJbLA/h7nHBFLciu+YNekgox0DB64wN1snZ4IIJMq+qnqp1/vE4fc7vEjZcP5KGhLzkkSjIHLRzw==",
"requires": {
"is-plain-object": "5.0.0"
},
"dependencies": {
"is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
}
}
},
"vuex": { "vuex": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",

View File

@ -16,8 +16,10 @@
"three": "^0.131.3", "three": "^0.131.3",
"three-orbit-controls": "^82.1.0", "three-orbit-controls": "^82.1.0",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.1.9", "vue-i18n": "^9.1.9",
"vue-router": "^4.0.0-0", "vue-router": "^4.0.0-0",
"vue-types": "^4.1.1",
"vuex": "^4.0.0-0" "vuex": "^4.0.0-0"
}, },
"devDependencies": { "devDependencies": {
@ -43,6 +45,7 @@
"husky": "^7.0.4", "husky": "^7.0.4",
"lint-staged": "^12.1.3", "lint-staged": "^12.1.3",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"postcss-html": "^1.3.0",
"postcss-scss": "^4.0.2", "postcss-scss": "^4.0.2",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"sass-loader": "^10.2.0", "sass-loader": "^10.2.0",
@ -109,7 +112,7 @@
"*.vue": [ "*.vue": [
"eslint --fix", "eslint --fix",
"prettier --write", "prettier --write",
"stylelint --fix" "stylelint --fix --custom-syntax postcss-html"
], ],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [ "{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
"prettier --write--parser json" "prettier --write--parser json"

View File

@ -0,0 +1,18 @@
<!-- 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 version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>clearclose</title>
<path d="M18.984 6.422l-5.578 5.578 5.578 5.578-1.406 1.406-5.578-5.578-5.578 5.578-1.406-1.406 5.578-5.578-5.578-5.578 1.406-1.406 5.578 5.578 5.578-5.578z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,18 @@
<!-- 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 version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>createmode_editedit</title>
<path d="M20.719 7.031l-1.828 1.828-3.75-3.75 1.828-1.828q0.281-0.281 0.703-0.281t0.703 0.281l2.344 2.344q0.281 0.281 0.281 0.703t-0.281 0.703zM3 17.25l11.063-11.063 3.75 3.75-11.063 11.063h-3.75v-3.75z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,18 @@
<!-- 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 version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>folder_open</title>
<path d="M20.016 18v-9.984h-16.031v9.984h16.031zM20.016 6q0.797 0 1.383 0.609t0.586 1.406v9.984q0 0.797-0.586 1.406t-1.383 0.609h-16.031q-0.797 0-1.383-0.609t-0.586-1.406v-12q0-0.797 0.586-1.406t1.383-0.609h6l2.016 2.016h8.016z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

18
src/assets/icons/save.svg Normal file
View File

@ -0,0 +1,18 @@
<!-- 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 version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>save</title>
<path d="M15 9v-3.984h-9.984v3.984h9.984zM12 18.984q1.219 0 2.109-0.891t0.891-2.109-0.891-2.109-2.109-0.891-2.109 0.891-0.891 2.109 0.891 2.109 2.109 0.891zM17.016 3l3.984 3.984v12q0 0.797-0.609 1.406t-1.406 0.609h-13.969q-0.844 0-1.43-0.586t-0.586-1.43v-13.969q0-0.844 0.586-1.43t1.43-0.586h12z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,18 @@
<!-- 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 version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>save_alt</title>
<path d="M12.984 12.656l2.625-2.578 1.406 1.406-5.016 5.016-5.016-5.016 1.406-1.406 2.625 2.578v-9.656h1.969v9.656zM18.984 12h2.016v6.984q0 0.797-0.609 1.406t-1.406 0.609h-13.969q-0.797 0-1.406-0.609t-0.609-1.406v-6.984h2.016v6.984h13.969v-6.984z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,95 @@
<!-- 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>
<el-select
:size="size"
v-model="selected"
:placeholder="placeholder"
@change="changeSelected"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</template>
<script lang="ts" setup>
import { defineProps, ref, defineEmits } from "vue";
import type { PropType } from "vue";
import { ElSelect, ElOption } from "element-plus";
interface Option {
label: string;
value: string;
}
const emit = defineEmits(["change"]);
const props = defineProps({
options: {
type: Array as PropType<Option[]>,
default: () => [],
},
value: { type: String, default: "" },
size: { type: String, default: "small" },
placeholder: { type: String, default: "Select a option" },
});
const selected = ref<string>(props.value);
function changeSelected() {
const optionSele = props.options.filter(
(d: Option) => d.value === selected.value
)[0];
emit("change", optionSele);
}
</script>
<style lang="scss" scope>
.icon {
width: 16px;
height: 16px;
vertical-align: middle;
fill: currentColor;
&.sm {
width: 14px;
height: 14px;
}
&.middle {
width: 18px;
height: 18px;
}
&.lg {
width: 24px;
height: 24px;
}
&.loading {
animation: loading 1.5s linear infinite;
}
&.logo {
height: 30px;
width: 110px;
}
&.xl {
height: 30px;
width: 30px;
}
}
</style>

View File

@ -16,13 +16,21 @@
*/ */
import Icon from "./Icon.vue"; import Icon from "./Icon.vue";
import TimePicker from "./TimePicker.vue"; import TimePicker from "./TimePicker.vue";
import Selector from "./Selector.vue";
import type { App } from "vue"; import type { App } from "vue";
import VueGridLayout from "vue-grid-layout";
const components: { [key: string]: any } = { Icon, TimePicker }; const components: { [key: string]: any } = {
Icon,
TimePicker,
VueGridLayout,
Selector,
};
const componentsName: string[] = Object.keys(components); const componentsName: string[] = Object.keys(components);
export default { export default {
install: (vue: App): void => { install: (vue: App): void => {
vue.use(components["VueGridLayout"]);
componentsName.forEach((i) => { componentsName.forEach((i) => {
vue.component(i, components[i]); vue.component(i, components[i]);
}); });

View File

@ -24,6 +24,7 @@ limitations under the License. -->
<script lang="ts" setup></script> <script lang="ts" setup></script>
<style lang="scss" scoped> <style lang="scss" scoped>
.app-main { .app-main {
height: calc(100% - 128px); height: calc(100% - 40px);
background: #f7f9fa;
} }
</style> </style>

View File

@ -17,6 +17,7 @@
const msg = { const msg = {
generalService: "General Service", generalService: "General Service",
services: "Services", services: "Services",
service: "Service",
traces: "Traces", traces: "Traces",
metrics: "Metrics", metrics: "Metrics",
serviceMesh: "Service Mesh", serviceMesh: "Service Mesh",
@ -43,8 +44,14 @@ const msg = {
eventList: "Event List", eventList: "Event List",
databasePanel: "Database Panel", databasePanel: "Database Panel",
meshServicePanel: "Service Panel", meshServicePanel: "Service Panel",
newDashboard: "New a dashboard", newDashboard: "Create a new dashboard",
dashboardEdit: "Edit the dashboard", dashboardEdit: "Edit the dashboard",
edit: "Edit",
delete: "Delete",
layer: "Layer",
endpoint: "Endpoint",
instance: "Instance",
create: "Create",
hourTip: "Select Hour", hourTip: "Select Hour",
minuteTip: "Select Minute", minuteTip: "Select Minute",
secondTip: "Select Second", secondTip: "Select Second",
@ -71,9 +78,6 @@ const msg = {
reload: "Reload", reload: "Reload",
usermode: "User Mode", usermode: "User Mode",
editmode: "Edit Mode", editmode: "Edit Mode",
currentService: "Current Service",
currentEndpoint: "Current Endpoint",
currentInstance: "Current Instance",
currentVersion: "Current Version", currentVersion: "Current Version",
currentPage: "Current Page", currentPage: "Current Page",
version: "Version", version: "Version",
@ -109,8 +113,6 @@ const msg = {
all: "All", all: "All",
success: "Success", success: "Success",
error: "Error", error: "Error",
service: "Service",
instance: "Instance",
endpoints: "Endpoints", endpoints: "Endpoints",
cache: "Cache", cache: "Cache",
global: "Global", global: "Global",
@ -190,7 +192,6 @@ const msg = {
noneParentService: "No Parent Service", noneParentService: "No Parent Service",
serviceGroup: "Service Group", serviceGroup: "Service Group",
endpointFilter: "Endpoint Filter", endpointFilter: "Endpoint Filter",
editConfig: "Edit Config",
databaseView: "Database", databaseView: "Database",
browserView: "Browser", browserView: "Browser",
metricsView: "NOC - Network Operation Center", metricsView: "NOC - Network Operation Center",
@ -257,6 +258,7 @@ const msg = {
scope: "Scope", scope: "Scope",
destService: "Destination Service", destService: "Destination Service",
destServiceInstance: "Destination Service Instance", destServiceInstance: "Destination Service Instance",
destEndpoint: "Destination Endpoint",
eventSource: "Event Source", eventSource: "Event Source",
modalTitle: "Inspection", modalTitle: "Inspection",
selectRedirectPage: "Do you want to inspect Traces or Logs of %s service?", selectRedirectPage: "Do you want to inspect Traces or Logs of %s service?",

View File

@ -45,6 +45,11 @@ const msg = {
meshServicePanel: "服务面板", meshServicePanel: "服务面板",
newDashboard: "新增仪表盘", newDashboard: "新增仪表盘",
dashboardEdit: "编辑仪表盘", dashboardEdit: "编辑仪表盘",
edit: "编辑",
delete: "删除",
layer: "层",
endpoint: "端点",
create: "新建",
hourTip: "选择小时", hourTip: "选择小时",
minuteTip: "选择分钟", minuteTip: "选择分钟",
secondTip: "选择秒数", secondTip: "选择秒数",
@ -70,9 +75,6 @@ const msg = {
reload: "刷新", reload: "刷新",
usermode: "用户模式", usermode: "用户模式",
editmode: "编辑模式", editmode: "编辑模式",
currentService: "当前服务",
currentEndpoint: "当前端点",
currentInstance: "当前实例",
currentVersion: "当前版本", currentVersion: "当前版本",
currentPage: "当前页面", currentPage: "当前页面",
version: "版本", version: "版本",
@ -190,7 +192,6 @@ const msg = {
noneParentService: "不设置父服务", noneParentService: "不设置父服务",
serviceGroup: "服务组", serviceGroup: "服务组",
endpointFilter: "端点过滤器", endpointFilter: "端点过滤器",
editConfig: "编辑",
databaseView: "数据库视图", databaseView: "数据库视图",
browserView: "浏览器视图", browserView: "浏览器视图",
metricsView: "大屏视图", metricsView: "大屏视图",
@ -255,6 +256,7 @@ const msg = {
scope: "范围", scope: "范围",
destService: "终点服务", destService: "终点服务",
destServiceInstance: "终点实例", destServiceInstance: "终点实例",
destEndpoint: "终点端点",
eventSource: "事件资源", eventSource: "事件资源",
modalTitle: "查看", modalTitle: "查看",
selectRedirectPage: "查看 %s 服务的追踪或日志?", selectRedirectPage: "查看 %s 服务的追踪或日志?",

View File

@ -48,29 +48,9 @@ export const routesDashboard: Array<RouteRecordRaw> = [
}, },
}, },
{ {
path: "/dashboard/edit/service/:serviceId", path: "/dashboard/edit/:layerId/:entityId/:dashboardId",
component: () => import("@/views/dashboard/Edit.vue"), component: () => import("@/views/dashboard/Edit.vue"),
name: "serviceEdit", name: "Edit",
meta: {
title: "dashboardEdit",
exact: false,
notShow: true,
},
},
{
path: "/dashboard/edit/endpoint/:serviceId/:endpointId",
component: () => import("@/views/dashboard/Edit.vue"),
name: "endpointEdit",
meta: {
title: "dashboardEdit",
exact: false,
notShow: true,
},
},
{
path: "/dashboard/edit/instance/:serviceId/:instanceId",
component: () => import("@/views/dashboard/Edit.vue"),
name: "instanceEdit",
meta: { meta: {
title: "dashboardEdit", title: "dashboardEdit",
exact: false, exact: false,

View File

@ -27,7 +27,7 @@ interface AppState {
utcHour: number; utcHour: number;
utcMin: number; utcMin: number;
eventStack: (() => unknown)[]; eventStack: (() => unknown)[];
timer: ReturnType<typeof setInterval> | null; timer: Nullable<any>;
} }
export const appStore = defineStore({ export const appStore = defineStore({

View File

@ -0,0 +1,61 @@
/**
* 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 { defineStore } from "pinia";
import { store } from "@/store";
import { LayoutConfig } from "@/types/dashboard";
interface DashboardState {
showConfig: boolean;
layout: LayoutConfig[];
}
export const dashboardStore = defineStore({
id: "dashboard",
state: (): DashboardState => ({
layout: [],
showConfig: false,
}),
actions: {
setLayout(data: LayoutConfig[]) {
this.layout = data;
},
addWidget() {
const newWidget: LayoutConfig = {
x: 0,
y: 0,
w: 24,
h: 12,
i: String(this.layout.length),
};
this.layout = this.layout.map((d: LayoutConfig) => {
d.y = d.y + newWidget.h;
return d;
});
this.layout.push(newWidget);
},
removeWidget(item: LayoutConfig) {
this.layout = this.layout.filter((d: LayoutConfig) => d.i !== item.i);
},
setConfigPanel(show: boolean) {
this.showConfig = show;
},
},
});
export function useDashboardStore(): any {
return dashboardStore(store);
}

View File

@ -29,9 +29,8 @@ export const selectorStore = defineStore({
state: (): SelectorState => ({ state: (): SelectorState => ({
services: [], services: [],
}), }),
getters: {},
actions: { actions: {
async fetchLayers() { async fetchLayers(): Promise<AxiosResponse> {
const res: AxiosResponse = await graph.query("queryLayers").params({}); const res: AxiosResponse = await graph.query("queryLayers").params({});
return res; return res;

39
src/types/dashboard.ts Normal file
View File

@ -0,0 +1,39 @@
import { string } from "vue-types";
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface LayoutConfig {
x: number;
y: number;
w: number;
h: number;
i: string;
widget?: WidgetConfig;
graph?: GraphConfig;
}
export interface WidgetConfig {
title: string;
Metrics: string[];
unit: string;
tips: string;
sortOrder: string;
}
export interface GraphConfig {
type: string;
}

31
src/types/vue-grid-layout.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
/**
* 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.
*/
declare module "vue-grid-layout" {
import Vue from "vue";
export class GridLayout extends Vue {}
export class GridItem extends Vue {}
export interface GridItemData {
x: number;
y: number;
w: number;
h: number;
i: string;
}
}

27
src/utils/uuid.ts Normal file
View File

@ -0,0 +1,27 @@
/**
* 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 function uuid(): string {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
/* tslint:disable */
const r = (Math.random() * 16) | 0;
/* tslint:disable */
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}

View File

@ -12,4 +12,117 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 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>dashboard edit</template> <template>
<div class="dashboard-tool">
<el-tooltip class="item" effect="dark" content="Add Widget" placement="top">
<span class="icon-btn" @click="dashboardStore.addWidget">
<Icon size="sm" iconName="playlist_add" />
</span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="Settings" placement="top">
<span class="icon-btn" @click="dashboardStore.setConfigPanel(true)">
<Icon size="sm" iconName="settings" />
</span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="Import" placement="top">
<span class="icon-btn">
<Icon size="sm" iconName="folder_open" />
</span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="Export" placement="top">
<span class="icon-btn">
<Icon size="sm" iconName="save_alt" />
</span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="Apply" placement="top">
<span class="icon-btn">
<Icon size="sm" iconName="save" />
</span>
</el-tooltip>
</div>
<div class="ds-main">
<GridLayout />
<el-dialog
v-model="dashboardStore.showConfig"
title="Configurations"
width="95%"
@closed="dashboardStore.setConfigPanel(false)"
>
<div class="configuration">xxxx</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import GridLayout from "./panel/Layout.vue";
import { LayoutConfig } from "@/types/dashboard";
import { useDashboardStore } from "@/store/modules/dashboard";
import { ElDialog, ElTooltip } from "element-plus";
const dashboardStore = useDashboardStore();
// fetch layout data from serve side
const layout: LayoutConfig[] = [
{ x: 0, y: 0, w: 4, h: 12, i: "0" },
{ x: 4, y: 0, w: 4, h: 12, i: "1" },
{ x: 8, y: 0, w: 4, h: 15, i: "2" },
{ x: 12, y: 0, w: 4, h: 9, i: "3" },
{ x: 16, y: 0, w: 4, h: 9, i: "4" },
{ x: 20, y: 0, w: 4, h: 9, i: "5" },
{ x: 0, y: 12, w: 4, h: 15, i: "7" },
{ x: 4, y: 12, w: 4, h: 15, i: "8" },
{ x: 8, y: 15, w: 4, h: 12, i: "9" },
{ x: 12, y: 9, w: 4, h: 12, i: "10" },
{ x: 16, y: 9, w: 4, h: 12, i: "11" },
{ x: 20, y: 9, w: 4, h: 15, i: "12" },
{ x: 0, y: 27, w: 4, h: 12, i: "14" },
{ x: 4, y: 27, w: 4, h: 12, i: "15" },
{ x: 8, y: 27, w: 4, h: 15, i: "16" },
];
dashboardStore.setLayout(layout);
</script>
<style lang="scss" scoped>
.dashboard-tool {
text-align: right;
padding: 5px 10px;
background: rgb(240, 242, 245);
border-bottom: 1px solid #dfe4e8;
}
.ds-main {
height: calc(100% - 40px);
}
.layout {
height: 100%;
flex-grow: 2;
overflow: hidden;
}
.grids {
height: 100%;
overflow-y: auto;
}
.ds-config {
width: 340px;
background-color: #fff;
box-shadow: 2px 0 2px 0 #ccc;
text-align: center;
border-left: 1px solid #eee;
}
.icon-btn {
display: inline-block;
padding: 0 5px;
text-align: center;
border: 1px solid #ccc;
border-radius: 3px;
margin-left: 8px;
cursor: pointer;
background-color: #eee;
color: #666;
}
.configuration {
height: 600px;
}
</style>

View File

@ -40,17 +40,17 @@ limitations under the License. -->
<el-table-column label="Operations"> <el-table-column label="Operations">
<template #default="scope"> <template #default="scope">
<el-button size="mini" @click="handleEdit(scope.$index, scope.row)"> <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">
View {{ t("view") }}
</el-button> </el-button>
<el-button size="mini" @click="handleEdit(scope.$index, scope.row)"> <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">
Edit {{ t("edit") }}
</el-button> </el-button>
<el-button <el-button
size="mini" size="mini"
type="danger" type="danger"
@click="handleDelete(scope.$index, scope.row)" @click="handleDelete(scope.$index, scope.row)"
> >
Delete {{ t("delete") }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>

View File

@ -14,195 +14,97 @@ See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div class="new-dashboard"> <div class="new-dashboard">
<h4>Create a new dashboard</h4> <div class="title">{{ t("newDashboard") }}</div>
<div class="item"> <div class="item">
<div class="label">Name</div> <div class="label">{{ t("name") }}</div>
<el-input <el-input
size="small" size="small"
v-model="state.name" v-model="states.name"
placeholder="Please input name" placeholder="Please input name"
/> />
</div> </div>
<div class="item"> <div class="item">
<div class="label">Layer</div> <div class="label">{{ t("layer") }}</div>
<el-select <Selector
:value="states.layer"
:options="Options"
size="small" size="small"
v-model="state.layer"
placeholder="Select a layer" placeholder="Select a layer"
@change="changeLayer"
class="selectors" class="selectors"
> />
<el-option
v-for="item in Options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div> </div>
<div class="item"> <div class="item">
<div class="label">Entity</div> <div class="label">{{ t("entityType") }}</div>
<el-select <Selector
:value="states.entity"
:options="EntityType"
size="small" size="small"
v-model="state.entity"
placeholder="Select a entity" placeholder="Select a entity"
@change="changeEntity"
class="selectors" class="selectors"
> />
<el-option
v-for="item in EntityType"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div class="item" v-show="state.entity === EntityType[0].value">
<div class="label">Service</div>
<el-select
size="small"
v-model="state.service"
placeholder="Select a service"
class="selectors"
>
<el-option
v-for="item in Options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div class="item" v-show="state.entity === EntityType[2].value">
<div class="label">Service / Endpoint</div>
<el-cascader
v-model="state.serviceEndpoint"
:options="SelectOpt"
:props="props"
size="small"
@change="handleChange"
:style="{ width: '600px' }"
></el-cascader>
</div>
<div class="item" v-show="state.entity === EntityType[3].value">
<div class="label">Service / Instance</div>
<el-cascader
v-model="state.serviceInstance"
:options="SelectOpt"
:props="props"
size="small"
@change="handleChange"
:style="{ width: '600px' }"
></el-cascader>
</div>
<div class="item" v-show="state.entity === EntityType[4].value">
<div class="label">Destination Service</div>
<el-select
size="small"
v-model="state.destService"
placeholder="Select"
class="selectors"
>
<el-option
v-for="item in Options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div class="item" v-show="state.entity === EntityType[5].value">
<span class="label">Destination Service / Endpoint</span>
<el-cascader
v-model="state.destServiceEndpoint"
:options="SelectOpt"
:props="props"
@change="handleChange"
:style="{ width: '600px' }"
></el-cascader>
</div>
<div class="item" v-show="state.entity === EntityType[6].value">
<span class="label">Destination Service / Instance</span>
<el-cascader
v-model="state.destServiceInstance"
:options="SelectOpt"
:props="props"
@change="handleChange"
:style="{ width: '600px' }"
></el-cascader>
</div> </div>
<div class="btn"> <div class="btn">
<el-button class="create" size="small" type="primary" @click="onCreate"> <el-button class="create" size="small" type="primary" @click="onCreate">
Create {{ t("create") }}
</el-button> </el-button>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive } from "vue"; import { reactive } from "vue";
import { useI18n } from "vue-i18n";
import router from "@/router"; import router from "@/router";
import { import { ElInput, ElButton } from "element-plus";
ElSelect,
ElOption,
ElCascader,
ElInput,
ElButton,
} from "element-plus";
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import { EntityType, SelectOpt, Options } from "./data"; import { EntityType, Options } from "./data";
import uuid from "@/utils/uuid";
const { t } = useI18n();
const selectorStore = useSelectorStore(); const selectorStore = useSelectorStore();
const props = { const states = reactive({
expandTrigger: "hover",
};
const state = reactive({
name: "", name: "",
layer: "", layer: Options[0].value,
entity: EntityType[0].value, entity: EntityType[0].value,
service: "",
serviceEndpoint: "",
serviceInstance: "",
destService: "",
destServiceEndpoint: "",
destServiceInstance: "",
}); });
const handleChange = (value: any) => {
console.log(value);
};
const onCreate = () => { const onCreate = () => {
let path = `/dashboard/edit/${state.entity}/`; const id = uuid();
switch (state.entity) { const path = `/dashboard/edit/${states.layer}/${states.entity}/${id}`;
case EntityType[0].value:
path += state.service;
break;
case EntityType[2].value:
path += `${state.service}/${state.serviceEndpoint}`;
break;
case EntityType[3].value:
path += `${state.service}/${state.serviceInstance}`;
break;
}
router.push(path); router.push(path);
}; };
selectorStore.fetchServices("general"); selectorStore.fetchServices("general");
function changeLayer(opt: { label: string; value: string }) {
states.layer = opt.value;
}
function changeEntity(opt: { label: string; value: string }) {
states.entity = opt.value;
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.title {
font-size: 18px;
font-weight: bold;
padding-top: 20px;
}
.new-dashboard { .new-dashboard {
margin: 0 auto; margin: 0 auto;
} }
.item { .item {
margin-top: 20px; margin-top: 20px;
} }
.new-dashboard, .new-dashboard,
.selectors, .selectors {
.el-cascader-menu {
width: 600px; width: 600px;
} }
.create { .create {
width: 600px; width: 600px;
} }
.btn { .btn {
margin-top: 40px; margin-top: 40px;
} }

View File

@ -19,69 +19,37 @@ export const EntityType = [
{ value: "all", label: "All" }, { value: "all", label: "All" },
{ value: "endpoint", label: "Service Endpoint" }, { value: "endpoint", label: "Service Endpoint" },
{ value: "serviceInstance", label: "Service Instance" }, { value: "serviceInstance", label: "Service Instance" },
{ value: "serviceRelation", label: "Service Relation" }, { value: "serviceRelationClient", label: "Service Relation(client)" },
{ value: "serviceInstanceRelation", label: "Service Instance Relation" }, { value: "serviceRelationServer", label: "Service Relation(server)" },
{ value: "endpointRelation", label: "Endpoint Relation" },
];
export const SelectOpt = [
{ {
value: "guide", value: "serviceInstanceRelationClient",
label: "Guide", label: "Service Instance Relation(client)",
children: [
{
value: "disciplines",
label: "Disciplines",
children: [
{
value: "consistency",
label: "Consistency",
},
{
value: "feedback",
label: "Feedback",
},
{
value: "efficiency",
label: "Efficiency",
},
],
},
{
value: "navigation",
label: "Navigation",
children: [
{
value: "side nav",
label: "Side Navigation",
},
{
value: "top nav",
label: "Top Navigation",
},
],
},
],
}, },
{
value: "serviceInstanceRelationServer",
label: "Service Instance Relation(server)",
},
{ value: "endpointRelation", label: "Endpoint Relation" },
]; ];
export const Options = [ export const Options = [
{ {
value: "Option1", value: "layer1",
label: "Option1", label: "layer1",
}, },
{ {
value: "Option2", value: "layer2",
label: "Option2", label: "layer2",
}, },
{ {
value: "Option3", value: "layer3",
label: "Option3", label: "layer3",
}, },
{ {
value: "Option4", value: "layer4",
label: "Option4", label: "layer4",
}, },
{ {
value: "Option5", value: "layer5",
label: "Option5", label: "layer5",
}, },
]; ];

View File

@ -0,0 +1,58 @@
<!-- 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>
<grid-layout
v-model:layout="dashboardStore.layout"
:col-num="24"
:row-height="10"
:is-draggable="true"
:is-resizable="true"
@layout-updated="layoutUpdatedEvent"
>
<grid-item
v-for="item in dashboardStore.layout"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:key="item.i"
>
<Widget :item="item" />
</grid-item>
</grid-layout>
</template>
<script lang="ts" setup>
import { useDashboardStore } from "@/store/modules/dashboard";
import { LayoutConfig } from "@/types/dashboard";
import Widget from "./Widget.vue";
const dashboardStore = useDashboardStore();
function layoutUpdatedEvent(newLayout: LayoutConfig) {
dashboardStore.setLayout(newLayout);
}
</script>
<style lang="scss" scoped>
.vue-grid-layout {
background: #f7f9fa;
height: auto !important;
}
.vue-grid-item:not(.vue-grid-placeholder) {
background: #fff;
box-shadow: 0px 1px 4px 0px #00000029;
border-radius: 5px;
}
</style>

View File

@ -0,0 +1,71 @@
<!-- 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="widget">
<div class="header flex-h">
<div>title</div>
<div class="operations">
<Icon
class="mr-5"
size="sm"
iconName="createmode_editedit"
@click="setConfig"
/>
<Icon size="sm" iconName="clearclose" @click="removeWidget" />
</div>
</div>
<div class="body">chart</div>
</div>
</template>
<script lang="ts" setup>
import { defineProps } from "vue";
import type { PropType } from "vue";
import { LayoutConfig } from "@/types/dashboard";
import { useDashboardStore } from "@/store/modules/dashboard";
const props = defineProps({
item: { type: Object as PropType<LayoutConfig> },
});
const dashboardStore = useDashboardStore();
function removeWidget() {
dashboardStore.removeWidget(props.item);
}
function setConfig() {
dashboardStore.setConfigPanel(true);
}
</script>
<style lang="scss" scoped>
.widget {
font-size: 12px;
// position: relative;
}
.header {
height: 30px;
padding: 5px;
width: 100%;
border-bottom: 1px solid #eee;
justify-content: space-between;
}
.operations {
color: #aaa;
cursor: pointer;
}
.body {
padding: 5px;
}
</style>

View File

@ -34,6 +34,7 @@
"webpack-env", "webpack-env",
"jest" "jest"
], ],
"typeRoots": ["./node_modules/@types/", "./types"],
"paths": { "paths": {
"@/*": [ "@/*": [
"src/*" "src/*"

114
type.d.ts vendored Normal file
View File

@ -0,0 +1,114 @@
/**
* 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 type {
ComponentRenderProxy,
VNode,
VNodeChild,
ComponentPublicInstance,
FunctionalComponent,
PropType as VuePropType,
} from "vue";
declare module "three";
declare module "three-orbit-controls";
declare module "element-plus";
declare global {
const __APP_INFO__: {
pkg: {
name: string;
version: string;
dependencies: Recordable<string>;
devDependencies: Recordable<string>;
};
lastBuildTime: string;
};
// vue
declare type PropType<T> = VuePropType<T>;
declare type VueNode = VNodeChild | JSX.Element;
export type Writable<T> = {
-readonly [P in keyof T]: T[P];
};
declare type Nullable<T> = T | null;
declare type NonNullable<T> = T extends null | undefined ? never : T;
declare type Recordable<T = any> = Record<string, T>;
declare type ReadonlyRecordable<T = any> = {
readonly [key: string]: T;
};
declare type Indexable<T = any> = {
[key: string]: T;
};
declare type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
declare type TimeoutHandle = ReturnType<typeof setTimeout>;
declare type IntervalHandle = ReturnType<typeof setInterval>;
declare interface ChangeEvent extends Event {
target: HTMLInputElement;
}
declare interface WheelEvent {
path?: EventTarget[];
}
interface ImportMetaEnv extends ViteEnv {
__: unknown;
}
declare interface ViteEnv {
VITE_PORT: number;
VITE_USE_MOCK: boolean;
VITE_USE_PWA: boolean;
VITE_PUBLIC_PATH: string;
VITE_PROXY: [string, string][];
VITE_GLOB_APP_TITLE: string;
VITE_GLOB_APP_SHORT_NAME: string;
VITE_USE_CDN: boolean;
VITE_DROP_CONSOLE: boolean;
VITE_BUILD_COMPRESS: "gzip" | "brotli" | "none";
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
VITE_LEGACY: boolean;
VITE_USE_IMAGEMIN: boolean;
VITE_GENERATE_UI: string;
}
declare function parseInt(s: string | number, radix?: number): number;
declare function parseFloat(string: string | number): number;
namespace JSX {
type Element = VNode;
type ElementClass = ComponentRenderProxy;
interface ElementAttributesProperty {
$props: any;
}
interface IntrinsicElements {
[elem: string]: any;
}
interface IntrinsicAttributes {
[elem: string]: any;
}
}
}
declare module "vue" {
export type JSXComponent<Props = any> =
| { new (): ComponentPublicInstance<Props> }
| FunctionalComponent<Props>;
}