Compare commits
79 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6c4991bc56 | ||
![]() |
77c1694383 | ||
![]() |
7fe3257c32 | ||
![]() |
8c9c339417 | ||
![]() |
62a82590c9 | ||
![]() |
22db68646c | ||
![]() |
7738695601 | ||
![]() |
9b1a5f7a74 | ||
![]() |
54997794b4 | ||
![]() |
372aee2eb6 | ||
![]() |
d662a0fb54 | ||
![]() |
b293f4ddb5 | ||
![]() |
279ec60915 | ||
![]() |
05688f0888 | ||
![]() |
15cd839480 | ||
![]() |
dc22f8da6e | ||
![]() |
ec67b4148c | ||
![]() |
986fe8b90e | ||
![]() |
fe28fae27d | ||
![]() |
359197a1c5 | ||
![]() |
903cf8e9bd | ||
![]() |
921c961dc1 | ||
![]() |
d129c75c8c | ||
![]() |
a4a2c4fefc | ||
![]() |
7dde4820de | ||
![]() |
7257858921 | ||
![]() |
ce585d6e08 | ||
![]() |
3f178f89f8 | ||
![]() |
ceae10cbfa | ||
![]() |
49a5481fb0 | ||
![]() |
8bb45bb453 | ||
![]() |
8077043c7e | ||
![]() |
0e15c023cc | ||
![]() |
55e4828adc | ||
![]() |
68eea52e88 | ||
![]() |
5973632f0f | ||
![]() |
1d189e82a7 | ||
![]() |
2491ab7176 | ||
![]() |
449dccdf36 | ||
![]() |
8031c1b463 | ||
![]() |
1c905aeb06 | ||
![]() |
4e64b9a4b1 | ||
![]() |
1be572a95f | ||
![]() |
5cc913a332 | ||
![]() |
220525a2d9 | ||
![]() |
72060f8227 | ||
![]() |
b247ed1c24 | ||
![]() |
b2707e0e62 | ||
![]() |
a1066f09e4 | ||
![]() |
efed817f73 | ||
![]() |
4613149759 | ||
![]() |
1877776720 | ||
![]() |
2b88266d67 | ||
![]() |
17b627a5d9 | ||
![]() |
5b0a68fe18 | ||
![]() |
d93ed2c5d3 | ||
![]() |
c73322a504 | ||
![]() |
4486684183 | ||
![]() |
1768a1641c | ||
![]() |
224053c0f4 | ||
![]() |
ca38366a60 | ||
![]() |
45f2985549 | ||
![]() |
3ef790dc07 | ||
![]() |
70df7605cb | ||
![]() |
4fc451f370 | ||
![]() |
163de5e5cf | ||
![]() |
8785817efe | ||
![]() |
db793e6c05 | ||
![]() |
d11ceab59d | ||
![]() |
1278454148 | ||
![]() |
7768f6ef16 | ||
![]() |
210b9ba491 | ||
![]() |
969580b770 | ||
![]() |
44dcb1e7f6 | ||
![]() |
1e0c253488 | ||
![]() |
141a288542 | ||
![]() |
154372615e | ||
![]() |
253f5c9261 | ||
![]() |
aa11a681ce |
33
.eslintignore
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
*.sh
|
||||
node_modules
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
/public
|
||||
/docs
|
||||
.husky
|
||||
.local
|
||||
/bin
|
||||
Dockerfile
|
53
.eslintrc.cjs
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript",
|
||||
"@vue/eslint-config-prettier",
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ["cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}"],
|
||||
extends: ["plugin:cypress/recommended"],
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"vue/script-setup-uses-vars": "error",
|
||||
"@typescript-eslint/ban-ts-ignore'": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-this-alias": "off",
|
||||
"vue/multi-word-component-names": "off",
|
||||
},
|
||||
};
|
3
.github/workflows/nodejs.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
@@ -49,6 +49,7 @@ jobs:
|
||||
npm ci
|
||||
npm run lint
|
||||
npm run build --if-present
|
||||
npm run check-components-types
|
||||
npm run test:unit
|
||||
env:
|
||||
CI: true
|
||||
|
34
.gitignore
vendored
@@ -16,24 +16,40 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
/node
|
||||
|
||||
/tests/e2e/videos/
|
||||
/tests/e2e/screenshots/
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
|
27
.husky/commit-msg
Executable 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.
|
||||
#
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
# shellcheck source=./_/husky.sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
27
.husky/common.sh
Normal 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.
|
||||
#
|
||||
|
||||
#!/bin/sh
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
29
.husky/pre-commit
Executable file
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
. "$(dirname "$0")/common.sh"
|
||||
|
||||
[ -n "$CI" ] && exit 0
|
||||
|
||||
PATH="/usr/local/bin:$PATH"
|
||||
|
||||
# Format and submit code according to lintstagedrc configuration
|
||||
npm run lint:lint-staged
|
22
.stylelintignore
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
/dist/*
|
||||
/public/*
|
||||
public/*
|
20
README.md
@@ -1,5 +1,4 @@
|
||||
Apache SkyWalking Booster UI
|
||||
===============
|
||||
# Apache SkyWalking Booster UI
|
||||
|
||||
<img src="http://skywalking.apache.org/assets/logo.svg" alt="Sky Walking logo" height="90px" align="right" />
|
||||
|
||||
@@ -10,11 +9,12 @@ Apache SkyWalking Booster UI
|
||||
This UI starts from SkyWalking OAP v9 core.
|
||||
|
||||
## Release
|
||||
|
||||
This repo wouldn't release separately. All source codes have been included in the main repo release. The tags match the [main repo](https://github.com/apache/skywalking) tags.
|
||||
|
||||
## Development
|
||||
|
||||
The app was built with [Vue3.x + Typescript](https://github.com/vuejs/vue).
|
||||
The app was built with [Vue3.x + Typescript](https://github.com/vuejs/vue).
|
||||
|
||||
### Prepare
|
||||
|
||||
@@ -28,19 +28,21 @@ npm install
|
||||
### Build
|
||||
|
||||
**All following builds are for dev.**
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run serve
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The default UI address is `http://localhost:8080`.
|
||||
|
||||
|
||||
# Contact Us
|
||||
* Submit an [issue](https://github.com/apache/skywalking/issues) if you face some issues. Submit a [discussion](https://github.com/apache/skywalking/discussions) if you want to propose new feature or have any question.
|
||||
* Mailing list: **dev@skywalking.apache.org**. Mail to `dev-subscribe@skywalking.apache.org`, follow the reply to subscribe the mailing list.
|
||||
* Join Slack. Send `Request to join SkyWalking slack` mail to the mail list(`dev@skywalking.apache.org`), we will invite you in.
|
||||
* QQ Group: 392443393, 901167865
|
||||
|
||||
- Submit an [issue](https://github.com/apache/skywalking/issues) if you face some issues. Submit a [discussion](https://github.com/apache/skywalking/discussions) if you want to propose new feature or have any question.
|
||||
- Mailing list: **dev@skywalking.apache.org**. Mail to `dev-subscribe@skywalking.apache.org`, follow the reply to subscribe the mailing list.
|
||||
- Join Slack. Send `Request to join SkyWalking slack` mail to the mail list(`dev@skywalking.apache.org`), we will invite you in.
|
||||
- QQ Group: 392443393, 901167865
|
||||
|
||||
# License
|
||||
|
||||
[Apache 2.0 License.](/LICENSE)
|
||||
|
50
commitlint.config.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
ignores: [(commit) => commit.includes("init")],
|
||||
extends: ["@commitlint/config-conventional"],
|
||||
rules: {
|
||||
"body-leading-blank": [2, "always"],
|
||||
"footer-leading-blank": [1, "always"],
|
||||
"header-max-length": [2, "always", 108],
|
||||
"subject-empty": [2, "never"],
|
||||
"type-empty": [2, "never"],
|
||||
"subject-case": [0],
|
||||
"type-enum": [
|
||||
2,
|
||||
"always",
|
||||
[
|
||||
"feat",
|
||||
"fix",
|
||||
"perf",
|
||||
"style",
|
||||
"docs",
|
||||
"test",
|
||||
"refactor",
|
||||
"build",
|
||||
"ci",
|
||||
"chore",
|
||||
"revert",
|
||||
"wip",
|
||||
"workflow",
|
||||
"types",
|
||||
"release",
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
13
src/assets/icons/index.ts → cypress.config.ts
Executable file → Normal file
@@ -14,7 +14,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const requireAll = (requireContext: Recordable) =>
|
||||
requireContext.keys().map(requireContext);
|
||||
const req = require.context("./", true, /\.svg$/);
|
||||
requireAll(req);
|
||||
|
||||
import { defineConfig } from "cypress";
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
specPattern: "cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}",
|
||||
baseUrl: "http://localhost:4173",
|
||||
},
|
||||
});
|
@@ -14,10 +14,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
describe("My First Test", () => {
|
||||
it("renders props.msg when passed", () => {
|
||||
const msg = "new message";
|
||||
console.log(msg);
|
||||
it("visits the app root url", () => {
|
||||
cy.visit("/");
|
||||
cy.contains("h1", "You did it!");
|
||||
});
|
||||
});
|
26
cypress/e2e/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["./**/*", "../support/**/*"],
|
||||
"compilerOptions": {
|
||||
"isolatedModules": false,
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"]
|
||||
}
|
||||
}
|
@@ -14,13 +14,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
module.exports = {
|
||||
plugins: ["cypress"],
|
||||
env: {
|
||||
mocha: true,
|
||||
"cypress/globals": true,
|
||||
},
|
||||
rules: {
|
||||
strict: "off",
|
||||
},
|
||||
};
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
@@ -14,8 +14,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// This example commands.ts shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
@@ -26,16 +27,29 @@
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
//
|
||||
// declare global {
|
||||
// namespace Cypress {
|
||||
// interface Chainable {
|
||||
// login(email: string, password: string): Chainable<void>
|
||||
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
export {};
|
@@ -14,6 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
26
env.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_SW_PROXY_TARGET: string;
|
||||
readonly VITE_DROP_CONSOLE: boolean;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
@@ -15,17 +15,13 @@ limitations under the License. -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Apache SkyWalking</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
51242
package-lock.json
generated
165
package.json
@@ -1,13 +1,21 @@
|
||||
{
|
||||
"name": "skywalking-booster-ui",
|
||||
"version": "9.3.0",
|
||||
"version": "9.4.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"test:e2e": "vue-cli-service test:e2e",
|
||||
"lint": "vue-cli-service lint"
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "vitest --environment jsdom --root src/",
|
||||
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"lint:lint-staged": "lint-staged",
|
||||
"prepare": "husky install",
|
||||
"check-components-types": "if (! git diff --quiet -U0 ./src/types); then echo 'type files are not updated correctly'; git diff -U0 ./src/types; exit 1; fi"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
@@ -15,134 +23,83 @@
|
||||
"d3-flame-graph": "^4.1.3",
|
||||
"d3-tip": "^0.9.1",
|
||||
"echarts": "^5.2.2",
|
||||
"element-plus": "^2.0.2",
|
||||
"element-plus": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"monaco-editor": "^0.27.0",
|
||||
"pinia": "^2.0.5",
|
||||
"monaco-editor": "^0.34.1",
|
||||
"pinia": "^2.0.28",
|
||||
"vis-timeline": "^7.5.1",
|
||||
"vue": "^3.0.0",
|
||||
"vue": "^3.2.45",
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-types": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.3.0",
|
||||
"@commitlint/config-conventional": "^17.3.0",
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/d3": "^7.1.0",
|
||||
"@types/d3-tip": "^3.5.5",
|
||||
"@types/echarts": "^4.9.12",
|
||||
"@types/jest": "^24.0.19",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/lodash": "^4.14.179",
|
||||
"@types/node": "^18.11.12",
|
||||
"@types/three": "^0.131.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-e2e-cypress": "~5.0.8",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||
"@vue/cli-plugin-unit-jest": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^7.0.0",
|
||||
"@vue/test-utils": "^2.0.0-0",
|
||||
"babel-jest": "^24.9.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^7.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.1.3",
|
||||
"monaco-editor-webpack-plugin": "^4.1.2",
|
||||
"node-sass": "^6.0.1",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"@vue/test-utils": "^2.2.6",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"@vueuse/core": "^9.6.0",
|
||||
"cypress": "^12.0.2",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"husky": "^8.0.2",
|
||||
"jsdom": "^20.0.3",
|
||||
"lint-staged": "^13.2.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"node-sass": "^8.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss-html": "^1.3.0",
|
||||
"postcss-scss": "^4.0.2",
|
||||
"prettier": "^2.2.1",
|
||||
"sass-loader": "^10.2.0",
|
||||
"stylelint": "^14.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.56.1",
|
||||
"start-server-and-test": "^1.15.2",
|
||||
"stylelint": "^15.6.0",
|
||||
"stylelint-config-html": "^1.0.0",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-standard": "^24.0.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"typescript": "~4.4.4",
|
||||
"stylelint-config-standard": "^33.0.0",
|
||||
"stylelint-order": "^6.0.3",
|
||||
"typescript": "~4.7.4",
|
||||
"unplugin-auto-import": "^0.7.0",
|
||||
"unplugin-vue-components": "^0.19.2",
|
||||
"vue-jest": "^5.0.0-0"
|
||||
"vite": "^4.0.5",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vitest": "^0.25.6",
|
||||
"vue-tsc": "^1.0.12"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/typescript/recommended",
|
||||
"@vue/prettier",
|
||||
"@vue/prettier/@typescript-eslint"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"vue/script-setup-uses-vars": "error",
|
||||
"@typescript-eslint/ban-ts-ignore'": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-this-alias": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/__tests__/*.{j,t}s?(x)",
|
||||
"**/tests/unit/**/*.spec.{j,t}s?(x)"
|
||||
],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"vue.config.js"
|
||||
],
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
],
|
||||
"jest": {
|
||||
"preset": "@vue/cli-plugin-unit-jest/presets/typescript-and-babel",
|
||||
"transform": {
|
||||
"^.+\\.vue$": "vue-jest"
|
||||
}
|
||||
},
|
||||
"gitHooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write"
|
||||
"*.{js,jsx,ts,tsx,vue}": [
|
||||
"eslint . --ext .vue,.js,.jsx,.ts,.tsx --fix --ignore-path .gitignore",
|
||||
"prettier --write \"src/**/*.{js,tsx,css,less,scss,vue,html,md}\"",
|
||||
"stylelint --cache --fix \"**/*.{vue}\" --cache --cache-location node_modules/.cache/stylelint/"
|
||||
],
|
||||
"*.vue": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"stylelint --fix --custom-syntax postcss-html"
|
||||
"*.{scss,less}": [
|
||||
"prettier --write \"src/**/*.{js,tsx,css,less,scss,vue,html,md}\"",
|
||||
"stylelint --cache --fix \"**/*.{less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/"
|
||||
],
|
||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
|
||||
"prettier --write--parser json"
|
||||
],
|
||||
"package.json": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.{scss,less,styl}": [
|
||||
"stylelint --fix",
|
||||
"package.json": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.md": [
|
||||
|
@@ -14,8 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
},
|
||||
autoprefixer: {},
|
||||
};
|
||||
|
26
prettier.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
printWidth: 120,
|
||||
semi: true,
|
||||
vueIndentScriptAndStyle: true,
|
||||
trailingComma: "all",
|
||||
proseWrap: "never",
|
||||
htmlWhitespaceSensitivity: "strict",
|
||||
endOfLine: "auto",
|
||||
};
|
23
src/App.vue
@@ -15,11 +15,22 @@ limitations under the License. -->
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useRoute } from "vue-router";
|
||||
const route = useRoute();
|
||||
|
||||
setTimeout(() => {
|
||||
if (route.name === "ViewWidget") {
|
||||
(document.querySelector("#app") as any).style.minWidth = "120px";
|
||||
} else {
|
||||
(document.querySelector("#app") as any).style.minWidth = "1024px";
|
||||
}
|
||||
}, 500);
|
||||
</script>
|
||||
<style>
|
||||
#app {
|
||||
color: #2c3e50;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
min-width: 1024px;
|
||||
}
|
||||
#app {
|
||||
color: #2c3e50;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
15
src/assets/icons/add_iframe.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 class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M856.32 428.064a32 32 0 0 0-32 32v163.328H372.48c-0.896 0-1.664 0.448-2.56 0.512v-177.696h244.48a32 32 0 1 0 0-64H130.56c-0.896 0-1.664 0.448-2.56 0.512V231.68h488.16a32 32 0 1 0 0-64H96a32 32 0 0 0-32 32v701.824a32 32 0 0 0 32 32h760.32a32 32 0 0 0 32-32V460.064a32 32 0 0 0-32-32zM128 445.728c0.896 0.064 1.664 0.512 2.56 0.512h175.36v423.264H128V445.728z m241.92 423.776v-182.624c0.896 0.064 1.664 0.512 2.56 0.512h451.84v182.08h-454.4zM960 174.656h-61.376V113.28a32 32 0 1 0-64 0v61.344H752.64a32 32 0 1 0 0 64h81.984v81.984a32 32 0 1 0 64 0V238.656H960a32 32 0 1 0 0-64z" fill="#2c2c2c"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
@@ -12,6 +12,6 @@ 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">
|
||||
<path d="M15 15.984h6v3q0 0.797-0.609 1.406t-1.406 0.609h-13.969q-0.797 0-1.406-0.609t-0.609-1.406v-3h6q0 1.219 0.891 2.109t2.109 0.891 2.109-0.891 0.891-2.109zM18.984 9v-3.984h-13.969v3.984h3.984q0 1.219 0.891 2.109t2.109 0.891 2.109-0.891 0.891-2.109h3.984zM18.984 3q0.797 0 1.406 0.609t0.609 1.406v6.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.984q0-0.797 0.609-1.406t1.406-0.609h13.969z"></path>
|
||||
<svg t="1684376918107" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7954">
|
||||
<path d="M243.921917 780.038686l357.445972 0L601.367889 422.592714 243.921917 422.592714 243.921917 780.038686zM288.679283 467.350081l268.036639 0 0 268.035616L288.679283 735.385696 288.679283 467.350081zM779.993149 65.25112 243.921917 65.25112c-98.640578 0-178.6314 79.990822-178.6314 178.716334L65.290517 780.038686c0 98.640578 79.990822 178.710194 178.6314 178.710194l536.071232 0c98.725512 0 178.716334-80.069617 178.716334-178.710194L958.709483 243.967454C958.709483 145.242965 878.717637 65.25112 779.993149 65.25112zM869.404528 735.385696c0 73.992201-60.07319 133.972271-134.084834 133.972271L288.679283 869.357967c-74.096579 0-134.063345-59.98007-134.063345-133.972271L154.615938 288.61328c0-73.984015 59.966767-134.057205 134.063345-134.057205l446.639386 0c74.011644 0 134.084834 60.07319 134.084834 134.057205L869.403504 735.385696zM511.957533 243.967454l268.035616 0 0 89.319282 0 268.035616-89.326445 0-44.645826 0 0-44.673455 89.298815 0L735.319693 288.61328l-268.014126 0 0 89.326445-44.652989 0 0-133.971247L511.957533 243.968477z" p-id="7955"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -12,6 +12,6 @@ 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">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M18.984 18q1.219 0 2.109-0.891t0.891-2.109-0.891-2.109-2.109-0.891h-1.5v-0.516q0-2.297-1.594-3.891t-3.891-1.594q-1.875 0-3.328 1.125t-1.969 2.859h-0.703q-1.641 0-2.813 1.195t-1.172 2.836 1.172 2.813 2.813 1.172h12.984zM19.359 10.031q1.922 0.141 3.281 1.57t1.359 3.398q0 2.063-1.477 3.539t-3.539 1.477h-12.984q-2.484 0-4.242-1.758t-1.758-4.242q0-2.203 1.57-3.961t3.773-1.992q0.984-1.828 2.766-2.953t3.891-1.125q2.531 0 4.711 1.781t2.648 4.266z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
17
src/assets/icons/continuous_profiling.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- 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="1684390612367" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12207">
|
||||
<path d="M202.66008 885.33784A10.66632 10.66632 0 0 0 213.3264 874.67152v-85.33056a42.66528 42.66528 0 0 0-42.66528-42.66528H85.33056a42.66528 42.66528 0 0 0-42.66528 42.66528v85.33056a10.66632 10.66632 0 0 0 10.66632 10.66632zM458.65176 885.33784a10.66632 10.66632 0 0 0 10.66632-10.66632v-298.65696a42.66528 42.66528 0 0 0-42.66528-42.665281H341.32224a42.66528 42.66528 0 0 0-42.66528 42.665281v298.65696a10.66632 10.66632 0 0 0 10.66632 10.66632zM714.643441 885.33784a10.66632 10.66632 0 0 0 10.66632-10.66632v-213.3264a42.66528 42.66528 0 0 0-42.66528-42.66528h-85.33056a42.66528 42.66528 0 0 0-42.66528 42.66528v213.3264a10.66632 10.66632 0 0 0 10.66632 10.66632zM970.635121 885.33784a10.66632 10.66632 0 0 0 10.66632-10.66632v-511.983361a42.66528 42.66528 0 0 0-42.66528-42.66528h-85.33056a42.66528 42.66528 0 0 0-42.66528 42.66528v511.983361a10.66632 10.66632 0 0 0 10.66632 10.66632z" p-id="12208"></path><path d="M149.32848 576.01456a85.33056 85.33056 0 0 0 85.33056-85.330561 84.51992 84.51992 0 0 0-4.266528-25.599168l135.120942-112.636339a83.410622 83.410622 0 0 0 104.273945-19.626029l106.6632 35.582844A85.33056 85.33056 0 0 0 746.642401 362.688159a83.79461 83.79461 0 0 0-9.85568-38.910735l140.240776-163.621349A85.074568 85.074568 0 1 0 831.972961 85.363839a83.709279 83.709279 0 0 0 3.967871 24.361875L688.190967 282.136111a82.429321 82.429321 0 0 0-91.346364 25.300511l-106.663201-35.540179A85.117234 85.117234 0 1 0 324.256128 302.956767L189.263182 415.763768A84.263928 84.263928 0 0 0 149.32848 405.353439a85.33056 85.33056 0 0 0 0 170.661121zM981.301441 938.66944H42.66528a43.347925 43.347925 0 0 0-42.66528 42.66528 42.66528 42.66528 0 0 0 42.66528 42.66528h938.636161a42.66528 42.66528 0 0 0 42.66528-42.66528 43.305259 43.305259 0 0 0-42.66528-42.66528z" p-id="12209"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
15
src/assets/icons/cross.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="1680101648371" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15649" width="48" height="48"><path d="M832 272c0-62.4-51-112.9-113.6-112-60.7 0.9-110 50.6-110.4 111.3-0.3 52.6 35.6 96.8 84.2 109.2 14 3.6 23.8 16 24.1 30.4 0.5 27.3-4.4 57.4-22.3 82.5-28.7 40.3-80.7 54.9-126.6 67.8-29 8.1-50.1 10.2-68.7 12-26.4 2.6-51.4 5.1-82.6 23-6.6 3.8-13.1 8-19.2 12.6-5.3 4-12.8 0.2-12.8-6.4V241.3c0-12.2 6.8-23.5 17.7-28.9 37.1-18.4 62.6-56.8 62.3-101.1-0.5-62.8-53.2-113.4-116-111.2C288.1 2.1 240 51.4 240 112c0 44 25.4 82.1 62.3 100.4 10.9 5.4 17.7 16.5 17.7 28.6v541.7c0 12.2-6.8 23.5-17.7 28.9-37.1 18.4-62.6 56.8-62.3 101.1 0.4 62.8 53.1 113.3 115.9 111.2C416 1021.9 464 972.5 464 912c0-44-25.4-82.1-62.3-100.4-10.9-5.4-17.7-16.5-17.7-28.6v-19.2c0-42 19.9-81.8 54.3-105.9 3.1-2.2 6.4-4.3 9.7-6.2 19.3-11.1 33.5-12.5 57-14.8 20.2-2 45.3-4.5 79.7-14.1 50.5-14.2 119.6-33.5 161.4-92.3 24-33.7 35.4-75 34.1-123-0.2-6.9-0.7-13.8-1.4-20.9-1.1-10.7 3.5-21 11.8-27.8 25.3-20.4 41.4-51.7 41.4-86.8zM304 112c0-26.5 21.5-48 48-48s48 21.5 48 48-21.5 48-48 48-48-21.5-48-48z m96 800c0 26.5-21.5 48-48 48s-48-21.5-48-48 21.5-48 48-48 48 21.5 48 48z m320-592c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48z" p-id="15650"></path></svg>
|
After Width: | Height: | Size: 2.0 KiB |
17
src/assets/icons/edit.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- 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="1684722897341" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2408">
|
||||
<path d="M853.333333 501.333333c-17.066667 0-32 14.933333-32 32v320c0 6.4-4.266667 10.666667-10.666666 10.666667H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V213.333333c0-6.4 4.266667-10.666667 10.666667-10.666666h320c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32H170.666667c-40.533333 0-74.666667 34.133333-74.666667 74.666666v640c0 40.533333 34.133333 74.666667 74.666667 74.666667h640c40.533333 0 74.666667-34.133333 74.666666-74.666667V533.333333c0-17.066667-14.933333-32-32-32z" fill="#666666" p-id="2409"></path><path d="M405.333333 484.266667l-32 125.866666c-2.133333 10.666667 0 23.466667 8.533334 29.866667 6.4 6.4 14.933333 8.533333 23.466666 8.533333h8.533334l125.866666-32c6.4-2.133333 10.666667-4.266667 14.933334-8.533333l300.8-300.8c38.4-38.4 38.4-102.4 0-140.8-38.4-38.4-102.4-38.4-140.8 0L413.866667 469.333333c-4.266667 4.266667-6.4 8.533333-8.533334 14.933334z m59.733334 23.466666L761.6 213.333333c12.8-12.8 36.266667-12.8 49.066667 0 12.8 12.8 12.8 36.266667 0 49.066667L516.266667 558.933333l-66.133334 17.066667 14.933334-68.266667z" p-id="2410"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
15
src/assets/icons/entry.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="1680083488716" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1096" width="48" height="48"><path d="M853.333333 512a42.666667 42.666667 0 0 0-42.666666-42.666667h-323.84l98.133333-97.706666a42.666667 42.666667 0 1 0-60.586667-60.586667l-170.666666 170.666667a42.666667 42.666667 0 0 0-8.96 14.08 42.666667 42.666667 0 0 0 0 32.426666 42.666667 42.666667 0 0 0 8.96 14.08l170.666666 170.666667a42.666667 42.666667 0 0 0 60.586667 0 42.666667 42.666667 0 0 0 0-60.586667L486.826667 554.666667H810.666667a42.666667 42.666667 0 0 0 42.666666-42.666667zM725.333333 85.333333H298.666667a128 128 0 0 0-128 128v597.333334a128 128 0 0 0 128 128h426.666666a128 128 0 0 0 128-128v-128a42.666667 42.666667 0 0 0-85.333333 0v128a42.666667 42.666667 0 0 1-42.666667 42.666666H298.666667a42.666667 42.666667 0 0 1-42.666667-42.666666V213.333333a42.666667 42.666667 0 0 1 42.666667-42.666666h426.666666a42.666667 42.666667 0 0 1 42.666667 42.666666v128a42.666667 42.666667 0 0 0 85.333333 0V213.333333a128 128 0 0 0-128-128z" p-id="1097"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
15
src/assets/icons/exit.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="1680104481890" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7991" width="16" height="16"><path d="M918.4 489.6l-160-160c-12.8-12.8-32-12.8-44.8 0-12.8 12.8-12.8 32 0 44.8l105.6 105.6L512 480c-19.2 0-32 12.8-32 32s12.8 32 32 32l307.2 0-105.6 105.6c-12.8 12.8-12.8 32 0 44.8 6.4 6.4 12.8 9.6 22.4 9.6 9.6 0 16-3.2 22.4-9.6l160-163.2c0 0 0-3.2 3.2-3.2C931.2 518.4 931.2 499.2 918.4 489.6zM832 736c-19.2 0-32 12.8-32 32l0 64c0 19.2-12.8 32-32 32L224 864c-19.2 0-32-12.8-32-32L192 192c0-19.2 12.8-32 32-32l544 0c19.2 0 32 12.8 32 32l0 64c0 19.2 12.8 32 32 32s32-12.8 32-32L864 192c0-54.4-41.6-96-96-96L224 96C169.6 96 128 137.6 128 192l0 640c0 54.4 41.6 96 96 96l544 0c54.4 0 96-41.6 96-96l0-64C864 748.8 851.2 736 832 736z" p-id="7992"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
15
src/assets/icons/functions.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="1670381479745" class="icon" viewBox="0 0 1280 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2930"><path d="M577.450256 639.9892c0-104.678234 33.919428-206.436516 96.018379-289.895108 10.339826-13.879766 8.89985-33.079442-4.299927-44.279253l-49.379167-41.959292c-13.999764-11.899799-35.659398-10.179828-46.759211 4.459925C492.171695 374.833675 447.99244 505.551469 447.99244 639.9892c0 134.457731 44.179254 265.175525 125.03789 371.673728 11.119812 14.639753 32.759447 16.359724 46.759211 4.459925l49.379167-41.979292c13.179778-11.219811 14.619753-30.399487 4.299927-44.279252-62.118952-83.418592-96.01838-185.196875-96.018379-289.875109zM447.99244 31.99946c0-17.679702-14.319758-31.99946-31.99946-31.99946h-95.99838C205.116539 0 111.99811 93.118429 111.99811 207.99649v127.99784H31.99946c-17.679702 0-31.99946 14.319758-31.99946 31.99946v95.99838c0 17.679702 14.319758 31.99946 31.99946 31.99946h79.99865v255.99568c0 26.399555-21.599636 47.99919-47.99919 47.99919H31.99946c-17.679702 0-31.99946 14.319758-31.99946 31.99946v95.99838c0 17.679702 14.319758 31.99946 31.99946 31.99946h31.99946c114.878061 0 207.99649-93.118429 207.99649-207.99649V495.99163h79.99865c17.679702 0 31.99946-14.319758 31.99946-31.99946v-95.99838c0-17.679702-14.319758-31.99946-31.99946-31.99946h-79.99865v-127.99784c0-26.399555 21.599636-47.99919 47.99919-47.99919h95.99838c17.679702 0 31.99946-14.319758 31.99946-31.99946V31.99946z m706.94807 236.316012c-11.119812-14.639753-32.759447-16.359724-46.759211-4.459925l-49.379166 41.959292c-13.179778 11.219811-14.619753 30.399487-4.299928 44.279253 62.098952 83.418592 96.01838 185.216874 96.01838 289.895108 0 104.678234-33.919428 206.456516-96.01838 289.895108-10.339826 13.879766-8.89985 33.079442 4.299928 44.279253l49.379166 41.979292c13.999764 11.899799 35.659398 10.179828 46.759211-4.459925C1235.799146 905.124726 1279.9784 774.426932 1279.9784 639.9892c0-134.457731-44.179254-265.175525-125.03789-371.673728z m-108.338171 463.792174L954.483893 639.9892l92.118446-92.118445c12.499789-12.499789 12.499789-32.759447 0-45.259237l-45.239237-45.239236c-12.499789-12.499789-32.759447-12.499789-45.259236 0L863.98542 549.490727l-92.118445-92.118445c-12.499789-12.499789-32.759447-12.499789-45.259237 0l-45.239236 45.239236c-12.499789 12.499789-12.499789 32.759447 0 45.259237L773.486947 639.9892l-92.118445 92.118446c-12.499789 12.499789-12.499789 32.759447 0 45.259236l45.239236 45.239237c12.499789 12.499789 32.759447 12.499789 45.259237 0L863.98542 730.487673l92.118446 92.118446c12.499789 12.499789 32.759447 12.499789 45.259236 0l45.239237-45.239237c12.499789-12.499789 12.499789-32.759447 0-45.259236z" p-id="2931"></path></svg>
|
After Width: | Height: | Size: 3.4 KiB |
16
src/assets/icons/mq.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 class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M750.08 657.92c-16.384 0-30.208 2.56-44.032 8.192l-44.032-52.224c33.28-38.4 55.296-90.624 55.296-145.92s-19.456-101.888-52.224-140.288l27.648-24.576c16.384 8.192 33.28 13.824 52.224 13.824 63.488 0 115.712-52.224 115.712-115.712s-52.224-115.712-115.712-115.712c-63.488 0-115.712 52.224-115.712 115.712 0 13.824 2.56 30.208 8.192 41.472l-35.84 30.208c-34.304-19.968-73.216-30.208-112.64-30.208-44.032 0-85.504 13.824-121.344 32.768l-41.472-52.224c0-5.632 2.56-13.824 2.56-19.456 0-40.96-32.768-74.24-73.728-74.24h-0.512c-40.96 0-74.24 32.768-74.24 73.728v0.512c0 40.96 32.768 74.24 73.728 74.24H262.656L306.688 332.8c-27.648 38.4-46.592 85.504-46.592 134.656 0 35.84 8.192 71.68 24.576 101.888l-35.84 30.208c-8.192-5.632-19.456-5.632-30.208-5.632-49.664 0-90.624 41.472-90.624 90.624 0 49.664 41.472 90.624 90.624 90.624s90.624-41.472 90.624-90.624c0-8.192 0-16.384-2.56-24.576l30.208-27.648c41.472 35.84 93.696 57.856 154.112 57.856 33.28 0 63.488-5.632 90.624-19.456l46.592 57.856c-11.264 19.456-19.456 44.032-19.456 68.608 0 77.312 63.488 140.288 143.36 140.288s143.36-63.488 143.36-140.288c0.512-75.776-65.536-139.264-145.408-139.264z m-261.632-11.264c-99.328 0-181.76-79.872-181.76-178.688C306.688 368.64 389.12 289.28 488.448 289.28s181.76 79.872 181.76 178.688c0 99.328-82.432 178.688-181.76 178.688zM430.592 465.408c0.512 18.432-13.824 33.28-32.256 33.792-18.432 0.512-33.28-13.824-33.792-32.256v-1.536c0-18.432 14.848-32.768 33.28-32.768 18.432-0.512 32.768 14.336 32.768 32.768z m88.064 0c0 18.432-14.848 33.28-32.768 33.28-18.432 0-33.28-14.848-33.28-32.768 0-18.432 14.848-32.768 33.28-32.768s32.768 13.824 32.768 32.256z m91.136 0c0 18.432-14.848 33.28-32.768 33.28s-33.28-14.848-33.28-32.768c0-18.432 14.848-32.768 33.28-32.768 17.92-1.024 32.768 13.824 32.768 32.256z"></path></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@@ -12,6 +12,6 @@ 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">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12 15.516q1.453 0 2.484-1.031t1.031-2.484-1.031-2.484-2.484-1.031-2.484 1.031-1.031 2.484 1.031 2.484 2.484 1.031zM19.453 12.984l2.109 1.641q0.328 0.234 0.094 0.656l-2.016 3.469q-0.188 0.328-0.609 0.188l-2.484-0.984q-0.984 0.703-1.688 0.984l-0.375 2.625q-0.094 0.422-0.469 0.422h-4.031q-0.375 0-0.469-0.422l-0.375-2.625q-0.891-0.375-1.688-0.984l-2.484 0.984q-0.422 0.141-0.609-0.188l-2.016-3.469q-0.234-0.422 0.094-0.656l2.109-1.641q-0.047-0.328-0.047-0.984t0.047-0.984l-2.109-1.641q-0.328-0.234-0.094-0.656l2.016-3.469q0.188-0.328 0.609-0.188l2.484 0.984q0.984-0.703 1.688-0.984l0.375-2.625q0.094-0.422 0.469-0.422h4.031q0.375 0 0.469 0.422l0.375 2.625q0.891 0.375 1.688 0.984l2.484-0.984q0.422-0.141 0.609 0.188l2.016 3.469q0.234 0.422-0.094 0.656l-2.109 1.641q0.047 0.328 0.047 0.984t-0.047 0.984z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
17
src/assets/icons/task_timeline.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- 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="1685973573331" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2708">
|
||||
<path d="M138 573.8V450.2c48.2-12.1 84-55.8 84-107.7 0-51.9-35.8-95.6-84-107.7V68.1c0-14.9-12.1-27-27-27s-27 12.1-27 27v166.7C35.8 246.9 0 290.6 0 342.5c0 51.9 35.8 95.6 84 107.7v123.7c-48.2 12-84 55.7-84 107.6s35.8 95.6 84 107.7v166.7c0 14.9 12.1 27 27 27s27-12.1 27-27V789.2c48.2-12.1 84-55.8 84-107.7s-35.8-95.6-84-107.7zM60 342.5c0-28.1 22.9-51 51-51s51 22.9 51 51-22.9 51-51 51-51-22.9-51-51z m51 390c-28.1 0-51-22.9-51-51s22.9-51 51-51 51 22.9 51 51-22.9 51-51 51zM942 283H352c-16.6 0-30-13.4-30-30s13.4-30 30-30h590c16.6 0 30 13.4 30 30s-13.4 30-30 30zM771.4 457H347.6c-14.2 0-25.6-11.5-25.6-25.6v-8.7c0-14.2 11.5-25.6 25.6-25.6h423.7c14.2 0 25.6 11.5 25.6 25.6v8.7c0.1 14.1-11.4 25.6-25.5 25.6z" p-id="2709"></path><path d="M942 625H352c-16.6 0-30-13.4-30-30s13.4-30 30-30h590c16.6 0 30 13.4 30 30s-13.4 30-30 30zM771.4 799H347.6c-14.2 0-25.6-11.5-25.6-25.6v-8.7c0-14.2 11.5-25.6 25.6-25.6h423.7c14.2 0 25.6 11.5 25.6 25.6v8.7c0.1 14.1-11.4 25.6-25.5 25.6z" p-id="2710"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
@@ -14,8 +14,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const requireComponent = require.context("./technologies", false, /\.png$/);
|
||||
const requireTool = require.context("./tools", false, /\.png$/);
|
||||
const requireComponent = import.meta.glob("./technologies/*.png", { eager: true });
|
||||
const requireTool = import.meta.glob("./tools/*.png", { eager: true });
|
||||
const result: { [key: string]: string } = {};
|
||||
const t: { [key: string]: string } = {};
|
||||
|
||||
@@ -24,25 +24,19 @@ function capitalizeFirstLetter(str: string) {
|
||||
}
|
||||
function validateFileName(str: string): string | undefined {
|
||||
if (/^\S+\.png$/.test(str)) {
|
||||
return str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) =>
|
||||
capitalizeFirstLetter($1)
|
||||
);
|
||||
return str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1));
|
||||
}
|
||||
}
|
||||
[...requireComponent.keys()].forEach((filePath: string) => {
|
||||
const componentConfig = requireComponent(filePath);
|
||||
|
||||
Object.keys(requireComponent).forEach((filePath: string) => {
|
||||
const fileName = validateFileName(filePath);
|
||||
if (fileName) {
|
||||
result[fileName] = componentConfig;
|
||||
result[fileName] = (requireComponent as Indexable)[filePath].default;
|
||||
}
|
||||
});
|
||||
[...requireTool.keys()].forEach((filePath: string) => {
|
||||
const componentConfig = requireTool(filePath);
|
||||
|
||||
Object.keys(requireTool).forEach((filePath: string) => {
|
||||
const fileName = validateFileName(filePath);
|
||||
if (fileName) {
|
||||
t[fileName] = componentConfig;
|
||||
t[fileName] = (requireTool as Indexable)[filePath].default;
|
||||
}
|
||||
});
|
||||
|
||||
|
BIN
src/assets/img/technologies/FASTAPI.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/img/technologies/GRIZZLY.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/technologies/HTTPX.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
src/assets/img/technologies/JERSEY.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/img/technologies/MICROMETER.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
src/assets/img/technologies/WEBSOCKETS.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/tools/ENTRY.png
Normal file
After Width: | Height: | Size: 262 B |
BIN
src/assets/img/tools/EXIT.png
Normal file
After Width: | Height: | Size: 269 B |
BIN
src/assets/img/tools/STREAM.png
Normal file
After Width: | Height: | Size: 373 B |
@@ -15,15 +15,19 @@ limitations under the License. -->
|
||||
<template>
|
||||
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
|
||||
<div v-if="!available" class="no-data">No Data</div>
|
||||
<div class="menus" v-show="visMenus" ref="menus">
|
||||
<div
|
||||
class="menus"
|
||||
v-show="visMenus"
|
||||
:style="{
|
||||
top: menuPos.y + 'px',
|
||||
left: menuPos.x + 'px',
|
||||
}"
|
||||
@mouseenter="hideTooltips"
|
||||
>
|
||||
<div class="tools" @click="associateMetrics" v-if="associate.length">
|
||||
{{ t("associateMetrics") }}
|
||||
</div>
|
||||
<div
|
||||
class="tools"
|
||||
@click="viewTrace"
|
||||
v-if="relatedTrace && relatedTrace.enableRelate"
|
||||
>
|
||||
<div class="tools" @click="viewTrace" v-if="relatedTrace && relatedTrace.enableRelate">
|
||||
{{ t("viewTrace") }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,224 +44,230 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
watch,
|
||||
ref,
|
||||
Ref,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
unref,
|
||||
computed,
|
||||
} 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 { addResizeListener, removeResizeListener } from "@/utils/event";
|
||||
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||
import associateProcessor from "@/hooks/useAssociateProcessor";
|
||||
import { watch, ref, onMounted, onBeforeUnmount, unref, computed, reactive } from "vue";
|
||||
import type { PropType, Ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { EventParams } from "@/types/app";
|
||||
import type { Filters, RelatedTrace } from "@/types/dashboard";
|
||||
import { useECharts } from "@/hooks/useEcharts";
|
||||
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
||||
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||
import associateProcessor from "@/hooks/useAssociateProcessor";
|
||||
|
||||
/*global Nullable, defineProps, defineEmits*/
|
||||
const emits = defineEmits(["select"]);
|
||||
const { t } = useI18n();
|
||||
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const menus = ref<Nullable<HTMLDivElement>>(null);
|
||||
const visMenus = ref<boolean>(false);
|
||||
const { setOptions, resize, getInstance } = useECharts(
|
||||
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({
|
||||
height: { type: String, default: "100%" },
|
||||
width: { type: String, default: "100%" },
|
||||
option: {
|
||||
type: Object as PropType<{ [key: string]: any }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<Filters>,
|
||||
},
|
||||
relatedTrace: {
|
||||
type: Object as PropType<RelatedTrace>,
|
||||
},
|
||||
associate: {
|
||||
type: Array as PropType<{ widgetId: string }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const available = computed(
|
||||
() =>
|
||||
(Array.isArray(props.option.series) &&
|
||||
props.option.series[0] &&
|
||||
props.option.series[0].data) ||
|
||||
(Array.isArray(props.option.series.data) && props.option.series.data[0])
|
||||
);
|
||||
onMounted(async () => {
|
||||
await setOptions(props.option);
|
||||
chartRef.value && addResizeListener(unref(chartRef), resize);
|
||||
instanceEvent();
|
||||
});
|
||||
/*global Nullable, defineProps, defineEmits, Indexable*/
|
||||
const emits = defineEmits(["select"]);
|
||||
const { t } = useI18n();
|
||||
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const visMenus = ref<boolean>(false);
|
||||
const { setOptions, resize, getInstance } = useECharts(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 menuPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
|
||||
const props = defineProps({
|
||||
height: { type: String, default: "100%" },
|
||||
width: { type: String, default: "100%" },
|
||||
option: {
|
||||
type: Object as PropType<Indexable>,
|
||||
default: () => ({}),
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<Filters>,
|
||||
},
|
||||
relatedTrace: {
|
||||
type: Object as PropType<RelatedTrace>,
|
||||
},
|
||||
associate: {
|
||||
type: Array as PropType<{ widgetId: string }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const available = computed(
|
||||
() =>
|
||||
(Array.isArray(props.option.series) && props.option.series[0] && props.option.series[0].data) ||
|
||||
(Array.isArray(props.option.series.data) && props.option.series.data[0]),
|
||||
);
|
||||
onMounted(async () => {
|
||||
await setOptions(props.option);
|
||||
chartRef.value && addResizeListener(unref(chartRef), resize);
|
||||
instanceEvent();
|
||||
});
|
||||
|
||||
function instanceEvent() {
|
||||
setTimeout(() => {
|
||||
function instanceEvent() {
|
||||
setTimeout(() => {
|
||||
const instance = getInstance();
|
||||
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
instance.on("click", (params: EventParams) => {
|
||||
currentParams.value = params;
|
||||
if (props.option.series.type === "sankey") {
|
||||
emits("select", currentParams.value);
|
||||
return;
|
||||
}
|
||||
instance.dispatchAction({
|
||||
type: "hideTip",
|
||||
});
|
||||
visMenus.value = true;
|
||||
if (!chartRef.value) {
|
||||
return;
|
||||
}
|
||||
const w = chartRef.value.getBoundingClientRect().width || 0;
|
||||
const h = chartRef.value.getBoundingClientRect().height || 0;
|
||||
if (w - params.event.offsetX > 120) {
|
||||
menuPos.x = params.event.offsetX;
|
||||
} else {
|
||||
menuPos.x = params.event.offsetX - 120;
|
||||
}
|
||||
if (h - params.event.offsetY < 50) {
|
||||
menuPos.y = params.event.offsetY - 40;
|
||||
} else {
|
||||
menuPos.y = params.event.offsetY;
|
||||
}
|
||||
});
|
||||
if (props.option.series.type === "sankey") {
|
||||
return;
|
||||
}
|
||||
instance.on("mouseover", () => {
|
||||
visMenus.value = false;
|
||||
});
|
||||
instance.on("mouseout", () => {
|
||||
instance.dispatchAction({
|
||||
type: "hideTip",
|
||||
});
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
visMenus.value = false;
|
||||
instance.dispatchAction({
|
||||
type: "hideTip",
|
||||
});
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
},
|
||||
true,
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function associateMetrics() {
|
||||
emits("select", currentParams.value);
|
||||
updateOptions(currentParams.value || undefined);
|
||||
}
|
||||
|
||||
function updateOptions(params?: EventParams) {
|
||||
const instance = getInstance();
|
||||
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
instance.on("click", (params: EventParams) => {
|
||||
currentParams.value = params;
|
||||
if (!menus.value || !chartRef.value) {
|
||||
if (!props.filters) {
|
||||
return;
|
||||
}
|
||||
if (props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
const options = eventAssociate();
|
||||
setOptions(options || props.option);
|
||||
} else {
|
||||
instance.dispatchAction({
|
||||
type: "showTip",
|
||||
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||
seriesIndex: params ? params.seriesIndex : 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function viewTrace() {
|
||||
const item = associateProcessor(props).traceFilters(currentParams.value);
|
||||
traceOptions.value = {
|
||||
...traceOptions.value,
|
||||
filters: item,
|
||||
};
|
||||
showTrace.value = true;
|
||||
visMenus.value = true;
|
||||
}
|
||||
|
||||
function hideTooltips() {
|
||||
const instance = getInstance();
|
||||
instance.dispatchAction({
|
||||
type: "hideTip",
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(newVal, oldVal) => {
|
||||
if (!available.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 (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
|
||||
return;
|
||||
}
|
||||
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";
|
||||
let options;
|
||||
if (props.filters && props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
options = eventAssociate();
|
||||
}
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
visMenus.value = false;
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
setOptions(options || props.option);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.filters,
|
||||
() => {
|
||||
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();
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
if (!props.filters) {
|
||||
return;
|
||||
}
|
||||
if (props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
const options = eventAssociate();
|
||||
setOptions(options || props.option);
|
||||
} else {
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||
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 viewTrace() {
|
||||
const item = associateProcessor(props).traceFilters(currentParams.value);
|
||||
traceOptions.value = {
|
||||
...traceOptions.value,
|
||||
filters: item,
|
||||
};
|
||||
showTrace.value = true;
|
||||
visMenus.value = true;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(newVal, oldVal) => {
|
||||
if (!available.value) {
|
||||
return;
|
||||
}
|
||||
if (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
|
||||
return;
|
||||
}
|
||||
let options;
|
||||
if (props.filters && props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
options = eventAssociate();
|
||||
}
|
||||
setOptions(options || props.option);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.filters,
|
||||
() => {
|
||||
updateOptions();
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeResizeListener(unref(chartRef), resize);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
removeResizeListener(unref(chartRef), resize);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.no-data {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-box-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.chart {
|
||||
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;
|
||||
.no-data {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-box-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.chart {
|
||||
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>
|
||||
|
@@ -24,64 +24,62 @@ limitations under the License. -->
|
||||
loading,
|
||||
}"
|
||||
>
|
||||
<use :xlink:href="`#${iconName}`"></use>
|
||||
<use :href="`#${iconName}`"></use>
|
||||
</svg>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import "@/assets/icons/index";
|
||||
|
||||
/*global defineProps */
|
||||
defineProps({
|
||||
iconName: { type: String, default: "" },
|
||||
size: { type: String, default: "sm" },
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
/*global defineProps */
|
||||
defineProps({
|
||||
iconName: { type: String, default: "" },
|
||||
size: { type: String, default: "sm" },
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
fill: currentColor;
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
fill: currentColor;
|
||||
|
||||
&.sm {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
&.sm {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
&.middle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
&.lg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
&.lg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
animation: loading 1.5s linear infinite;
|
||||
}
|
||||
&.loading {
|
||||
animation: loading 1.5s linear infinite;
|
||||
}
|
||||
|
||||
&.logo {
|
||||
height: 30px;
|
||||
width: 110px;
|
||||
}
|
||||
&.logo {
|
||||
height: 30px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
&.xl {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
@keyframes loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
&.xl {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
@keyframes loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -20,31 +20,31 @@ limitations under the License. -->
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { ref } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
interface Option {
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
}
|
||||
/*global defineProps, defineEmits */
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<
|
||||
{
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
}[]
|
||||
>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: "",
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
});
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<Option[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: "",
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
});
|
||||
const selected = ref<string>(props.value);
|
||||
|
||||
const selected = ref<string>(props.value);
|
||||
|
||||
function checked(opt: unknown) {
|
||||
emit("change", opt);
|
||||
}
|
||||
function checked(opt: unknown) {
|
||||
emit("change", opt);
|
||||
}
|
||||
</script>
|
||||
|
@@ -19,9 +19,7 @@ limitations under the License. -->
|
||||
{{ selected.label }}
|
||||
</span>
|
||||
<span class="no-data" v-else>Please select a option</span>
|
||||
<span class="remove-icon" @click="removeSelected" v-if="clearable">
|
||||
×
|
||||
</span>
|
||||
<span class="remove-icon" @click="removeSelected" v-if="clearable"> × </span>
|
||||
</div>
|
||||
<div class="opt-wrapper" v-show="visible">
|
||||
<div
|
||||
@@ -37,141 +35,141 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Option } from "@/types/app";
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import type { Option } from "@/types/app";
|
||||
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<Option[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: () => "",
|
||||
},
|
||||
clearable: { type: Boolean, default: false },
|
||||
});
|
||||
const visible = ref<boolean>(false);
|
||||
const opt = props.options.find((d: Option) => props.value === d.value);
|
||||
const selected = ref<Option>(opt || { label: "", value: "" });
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<Option[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: () => "",
|
||||
},
|
||||
clearable: { type: Boolean, default: false },
|
||||
});
|
||||
const visible = ref<boolean>(false);
|
||||
const opt = props.options.find((d: Option) => props.value === d.value);
|
||||
const selected = ref<Option>(opt || { label: "", value: "" });
|
||||
|
||||
function handleSelect(i: Option) {
|
||||
selected.value = i;
|
||||
emit("change", i.value);
|
||||
}
|
||||
function removeSelected() {
|
||||
selected.value = { label: "", value: "" };
|
||||
emit("change", "");
|
||||
}
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
const opt = props.options.find((d: Option) => data === d.value);
|
||||
selected.value = opt || { label: "", value: "" };
|
||||
function handleSelect(i: Option) {
|
||||
selected.value = i;
|
||||
emit("change", i.value);
|
||||
}
|
||||
);
|
||||
document.body.addEventListener("click", handleClick, false);
|
||||
function removeSelected() {
|
||||
selected.value = { label: "", value: "" };
|
||||
emit("change", "");
|
||||
}
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
const opt = props.options.find((d: Option) => data === d.value);
|
||||
selected.value = opt || { label: "", value: "" };
|
||||
},
|
||||
);
|
||||
document.body.addEventListener("click", handleClick, false);
|
||||
|
||||
function handleClick() {
|
||||
visible.value = false;
|
||||
}
|
||||
function setPopper(event: any) {
|
||||
event.stopPropagation();
|
||||
visible.value = !visible.value;
|
||||
}
|
||||
function handleClick() {
|
||||
visible.value = false;
|
||||
}
|
||||
function setPopper(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
visible.value = !visible.value;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.bar-select {
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
|
||||
.selected {
|
||||
padding: 0 3px;
|
||||
.bar-select {
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
margin: 3px;
|
||||
color: #409eff;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #e8e8e8;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
|
||||
.no-data {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.bar-i {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 2px 10px;
|
||||
overflow: auto;
|
||||
color: #606266;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.remove-icon {
|
||||
display: block;
|
||||
.selected {
|
||||
padding: 0 3px;
|
||||
border-radius: 3px;
|
||||
margin: 3px;
|
||||
color: #409eff;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #e8e8e8;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
.no-data {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.opt-wrapper {
|
||||
color: #606266;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
border: 1px solid #ddd;
|
||||
width: 100%;
|
||||
border-radius: 0 0 3px 3px;
|
||||
border-right-width: 1px !important;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 12px;
|
||||
opacity: 0.6;
|
||||
.bar-i {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 2px 10px;
|
||||
overflow: auto;
|
||||
color: #606266;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
.remove-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opt {
|
||||
padding: 7px 15px;
|
||||
|
||||
&.select-disabled {
|
||||
color: #409eff;
|
||||
cursor: not-allowed;
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
.opt-wrapper {
|
||||
color: #606266;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
border: 1px solid #ddd;
|
||||
width: 100%;
|
||||
border-radius: 0 0 3px 3px;
|
||||
border-right-width: 1px !important;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 12px;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opt {
|
||||
padding: 7px 15px;
|
||||
|
||||
&.select-disabled {
|
||||
color: #409eff;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -32,68 +32,72 @@ limitations under the License. -->
|
||||
:key="item.value || ''"
|
||||
:label="item.label || ''"
|
||||
:value="item.value || ''"
|
||||
:disabled="item.disabled || false"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
interface Option {
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
}
|
||||
// interface Option {
|
||||
// label: string | number;
|
||||
// value: string | number;
|
||||
// }
|
||||
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change", "query"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<(Option & { disabled?: boolean })[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: [Array, String, Number, undefined] as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
placeholder: {
|
||||
type: [String, undefined] as PropType<string>,
|
||||
default: "Select a option",
|
||||
},
|
||||
borderRadius: { type: Number, default: 3 },
|
||||
multiple: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
isRemote: { type: Boolean, default: false },
|
||||
filterable: { type: Boolean, default: true },
|
||||
});
|
||||
/*global defineProps, defineEmits, Indexable*/
|
||||
const emit = defineEmits(["change", "query"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<
|
||||
({
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
} & { disabled?: boolean })[]
|
||||
>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: [Array, String, Number, undefined] as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
placeholder: {
|
||||
type: [String, undefined] as PropType<string>,
|
||||
default: "Select a option",
|
||||
},
|
||||
borderRadius: { type: Number, default: 3 },
|
||||
multiple: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
isRemote: { type: Boolean, default: false },
|
||||
filterable: { type: Boolean, default: true },
|
||||
});
|
||||
|
||||
const selected = ref<string[] | string>(props.value);
|
||||
function changeSelected() {
|
||||
const options = props.options.filter((d: any) =>
|
||||
props.multiple
|
||||
? selected.value.includes(d.value)
|
||||
: selected.value === d.value
|
||||
const selected = ref<string[] | string>(props.value);
|
||||
function changeSelected() {
|
||||
const options = props.options.filter((d: Indexable) =>
|
||||
props.multiple ? selected.value.includes(d.value) : selected.value === d.value,
|
||||
);
|
||||
emit("change", options);
|
||||
}
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
if (props.isRemote) {
|
||||
emit("query", query);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
selected.value = data;
|
||||
},
|
||||
);
|
||||
emit("change", options);
|
||||
}
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
if (props.isRemote) {
|
||||
emit("query", query);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
selected.value = data;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.el-input__inner {
|
||||
border-radius: unset !important;
|
||||
}
|
||||
.el-input__inner {
|
||||
border-radius: unset !important;
|
||||
}
|
||||
</style>
|
||||
|
@@ -35,56 +35,28 @@ limitations under the License. -->
|
||||
<transition name="datepicker-anim">
|
||||
<div
|
||||
class="datepicker-popup"
|
||||
:class="[
|
||||
popupClass,
|
||||
{ 'datepicker-inline': type === 'inline' },
|
||||
position,
|
||||
]"
|
||||
:class="[popupClass, { 'datepicker-inline': type === 'inline' }, position]"
|
||||
tabindex="-1"
|
||||
v-if="show || type === 'inline'"
|
||||
>
|
||||
<template v-if="range">
|
||||
<div class="datepicker-popup__sidebar">
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('quarter')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('quarter')">
|
||||
{{ local.quarterHourCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('half')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('half')">
|
||||
{{ local.halfHourCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('hour')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('hour')">
|
||||
{{ local.hourCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('day')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('day')">
|
||||
{{ local.dayCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('week')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('week')">
|
||||
{{ local.weekCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('month')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('month')">
|
||||
{{ local.monthCutTip }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -123,16 +95,10 @@ limitations under the License. -->
|
||||
/>
|
||||
</template>
|
||||
<div v-if="showButtons" class="datepicker__buttons">
|
||||
<button
|
||||
@click.prevent.stop="cancel"
|
||||
class="datepicker__button-cancel"
|
||||
>
|
||||
<button @click.prevent.stop="cancel" class="datepicker__button-cancel">
|
||||
{{ local.cancelTip }}
|
||||
</button>
|
||||
<button
|
||||
@click.prevent.stop="submit"
|
||||
class="datepicker__button-select"
|
||||
>
|
||||
<button @click.prevent.stop="submit" class="datepicker__button-select">
|
||||
{{ local.submitTip }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -142,438 +108,431 @@ limitations under the License. -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DateCalendar from "./DateCalendar.vue";
|
||||
import { useTimeoutFn } from "@/hooks/useTimeout";
|
||||
/*global defineProps, defineEmits */
|
||||
const datepicker = ref(null);
|
||||
const { t } = useI18n();
|
||||
const show = ref<boolean>(false);
|
||||
const dates = ref<Date | string[] | any>([]);
|
||||
const props = defineProps({
|
||||
position: { type: String, default: "bottom" },
|
||||
name: [String],
|
||||
inputClass: [String],
|
||||
popupClass: [String],
|
||||
value: [Date, Array, String],
|
||||
disabled: [Boolean],
|
||||
type: {
|
||||
type: String,
|
||||
default: "normal",
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: "~",
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: [String],
|
||||
disabledDate: {
|
||||
type: Function,
|
||||
default: () => false,
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: "YYYY-MM-DD",
|
||||
},
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dateRangeSelect: [Function],
|
||||
});
|
||||
const emit = defineEmits(["clear", "input", "confirm", "cancel"]);
|
||||
const local = computed(() => {
|
||||
return {
|
||||
dow: 1, // Monday is the first day of the week
|
||||
hourTip: t("hourTip"), // tip of select hour
|
||||
minuteTip: t("minuteTip"), // tip of select minute
|
||||
secondTip: t("secondTip"), // tip of select second
|
||||
yearSuffix: t("yearSuffix"), // format of head
|
||||
monthsHead: t("monthsHead").split("_"), // months of head
|
||||
months: t("months").split("_"), // months of panel
|
||||
weeks: t("weeks").split("_"), // weeks
|
||||
cancelTip: t("cancel"), // default text for cancel button
|
||||
submitTip: t("confirm"), // default text for submit button
|
||||
quarterHourCutTip: t("quarterHourCutTip"),
|
||||
halfHourCutTip: t("halfHourCutTip"),
|
||||
hourCutTip: t("hourCutTip"),
|
||||
dayCutTip: t("dayCutTip"),
|
||||
weekCutTip: t("weekCutTip"),
|
||||
monthCutTip: t("monthCutTip"),
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DateCalendar from "./DateCalendar.vue";
|
||||
import { useTimeoutFn } from "@/hooks/useTimeout";
|
||||
/*global defineProps, defineEmits*/
|
||||
const datepicker = ref(null);
|
||||
const { t } = useI18n();
|
||||
const show = ref<boolean>(false);
|
||||
const dates = ref<Date | string[] | any>([]);
|
||||
const props = defineProps({
|
||||
position: { type: String, default: "bottom" },
|
||||
name: [String],
|
||||
inputClass: [String],
|
||||
popupClass: [String],
|
||||
value: [Date, Array, String],
|
||||
disabled: [Boolean],
|
||||
type: {
|
||||
type: String,
|
||||
default: "normal",
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: "~",
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: [String],
|
||||
disabledDate: {
|
||||
type: Function,
|
||||
default: () => false,
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: "YYYY-MM-DD",
|
||||
},
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dateRangeSelect: [Function],
|
||||
});
|
||||
const emit = defineEmits(["clear", "input", "confirm", "cancel"]);
|
||||
const local = computed(() => {
|
||||
return {
|
||||
dow: 1, // Monday is the first day of the week
|
||||
hourTip: t("hourTip"), // tip of select hour
|
||||
minuteTip: t("minuteTip"), // tip of select minute
|
||||
secondTip: t("secondTip"), // tip of select second
|
||||
yearSuffix: t("yearSuffix"), // format of head
|
||||
monthsHead: t("monthsHead").split("_"), // months of head
|
||||
months: t("months").split("_"), // months of panel
|
||||
weeks: t("weeks").split("_"), // weeks
|
||||
cancelTip: t("cancel"), // default text for cancel button
|
||||
submitTip: t("confirm"), // default text for submit button
|
||||
quarterHourCutTip: t("quarterHourCutTip"),
|
||||
halfHourCutTip: t("halfHourCutTip"),
|
||||
hourCutTip: t("hourCutTip"),
|
||||
dayCutTip: t("dayCutTip"),
|
||||
weekCutTip: t("weekCutTip"),
|
||||
monthCutTip: t("monthCutTip"),
|
||||
};
|
||||
});
|
||||
const tf = (time: Date, format?: any): string => {
|
||||
const year = time.getFullYear();
|
||||
const month = time.getMonth();
|
||||
const day = time.getDate();
|
||||
const hours24 = time.getHours();
|
||||
const hours = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
const minutes = time.getMinutes();
|
||||
const seconds = time.getSeconds();
|
||||
const milliseconds = time.getMilliseconds();
|
||||
const dd = (t: number) => `0${t}`.slice(-2);
|
||||
const map: { [key: string]: string | number } = {
|
||||
YYYY: year,
|
||||
MM: dd(month + 1),
|
||||
MMM: local.value.months[month],
|
||||
MMMM: local.value.monthsHead[month],
|
||||
M: month + 1,
|
||||
DD: dd(day),
|
||||
D: day,
|
||||
HH: dd(hours24),
|
||||
H: hours24,
|
||||
hh: dd(hours),
|
||||
h: hours,
|
||||
mm: dd(minutes),
|
||||
m: minutes,
|
||||
ss: dd(seconds),
|
||||
s: seconds,
|
||||
S: milliseconds,
|
||||
};
|
||||
return (format || props.format).replace(/Y+|M+|D+|H+|h+|m+|s+|S+/g, (str: string) => map[str]);
|
||||
};
|
||||
});
|
||||
const tf = (time: Date, format?: any): string => {
|
||||
const year = time.getFullYear();
|
||||
const month = time.getMonth();
|
||||
const day = time.getDate();
|
||||
const hours24 = time.getHours();
|
||||
const hours = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
const minutes = time.getMinutes();
|
||||
const seconds = time.getSeconds();
|
||||
const milliseconds = time.getMilliseconds();
|
||||
const dd = (t: number) => `0${t}`.slice(-2);
|
||||
const map: { [key: string]: string | number } = {
|
||||
YYYY: year,
|
||||
MM: dd(month + 1),
|
||||
MMM: local.value.months[month],
|
||||
MMMM: local.value.monthsHead[month],
|
||||
M: month + 1,
|
||||
DD: dd(day),
|
||||
D: day,
|
||||
HH: dd(hours24),
|
||||
H: hours24,
|
||||
hh: dd(hours),
|
||||
h: hours,
|
||||
mm: dd(minutes),
|
||||
m: minutes,
|
||||
ss: dd(seconds),
|
||||
s: seconds,
|
||||
S: milliseconds,
|
||||
const range = computed(() => {
|
||||
return dates.value.length === 2;
|
||||
});
|
||||
const text = computed(() => {
|
||||
const val = props.value;
|
||||
const txt = dates.value.map((date: Date) => tf(date)).join(` ${props.rangeSeparator} `);
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1 ? txt : "";
|
||||
}
|
||||
return val ? txt : "";
|
||||
});
|
||||
const get = () => {
|
||||
return Array.isArray(props.value) ? dates.value : dates.value[0];
|
||||
};
|
||||
return (format || props.format).replace(
|
||||
/Y+|M+|D+|H+|h+|m+|s+|S+/g,
|
||||
(str: string) => map[str]
|
||||
const cls = () => {
|
||||
emit("clear");
|
||||
emit("input", range.value ? [] : "");
|
||||
};
|
||||
const vi = (val: any) => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1 ? val.map((item) => new Date(item)) : [new Date(), new Date()];
|
||||
}
|
||||
return val ? [new Date(val)] : [new Date()];
|
||||
};
|
||||
const ok = (leaveOpened: boolean) => {
|
||||
emit("input", get());
|
||||
!leaveOpened &&
|
||||
!props.showButtons &&
|
||||
useTimeoutFn(() => {
|
||||
show.value = range.value;
|
||||
}, 1);
|
||||
};
|
||||
const setDates = (d: Date, pos: string) => {
|
||||
if (pos === "right") {
|
||||
dates.value[1] = d;
|
||||
return;
|
||||
}
|
||||
dates.value[0] = d;
|
||||
};
|
||||
const dc = (e: MouseEvent) => {
|
||||
show.value = (datepicker.value as any).contains(e.target) && !props.disabled;
|
||||
};
|
||||
const quickPick = (type: string) => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
switch (type) {
|
||||
case "quarter":
|
||||
start.setTime(start.getTime() - 60 * 15 * 1000); //15 mins
|
||||
break;
|
||||
case "half":
|
||||
start.setTime(start.getTime() - 60 * 30 * 1000); //30 mins
|
||||
break;
|
||||
case "hour":
|
||||
start.setTime(start.getTime() - 3600 * 1000); //1 hour
|
||||
break;
|
||||
case "day":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24); //1 day
|
||||
break;
|
||||
case "week":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); //1 week
|
||||
break;
|
||||
case "month":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); //1 month
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dates.value = [start, end];
|
||||
emit("input", get());
|
||||
};
|
||||
const submit = () => {
|
||||
emit("confirm", get());
|
||||
show.value = false;
|
||||
};
|
||||
const cancel = () => {
|
||||
emit("cancel");
|
||||
show.value = false;
|
||||
};
|
||||
onMounted(() => {
|
||||
dates.value = vi(props.value);
|
||||
document.addEventListener("click", dc, true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", dc, true);
|
||||
});
|
||||
watch(
|
||||
() => props.value,
|
||||
(val: unknown) => {
|
||||
dates.value = vi(val);
|
||||
},
|
||||
);
|
||||
};
|
||||
const range = computed(() => {
|
||||
return dates.value.length === 2;
|
||||
});
|
||||
const text = computed(() => {
|
||||
const val = props.value;
|
||||
const txt = dates.value
|
||||
.map((date: Date) => tf(date))
|
||||
.join(` ${props.rangeSeparator} `);
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1 ? txt : "";
|
||||
}
|
||||
return val ? txt : "";
|
||||
});
|
||||
const get = () => {
|
||||
return Array.isArray(props.value) ? dates.value : dates.value[0];
|
||||
};
|
||||
const cls = () => {
|
||||
emit("clear");
|
||||
emit("input", range.value ? [] : "");
|
||||
};
|
||||
const vi = (val: any) => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1
|
||||
? val.map((item) => new Date(item))
|
||||
: [new Date(), new Date()];
|
||||
}
|
||||
return val ? [new Date(val)] : [new Date()];
|
||||
};
|
||||
const ok = (leaveOpened: boolean) => {
|
||||
emit("input", get());
|
||||
!leaveOpened &&
|
||||
!props.showButtons &&
|
||||
useTimeoutFn(() => {
|
||||
show.value = range.value;
|
||||
}, 1);
|
||||
};
|
||||
const setDates = (d: Date, pos: string) => {
|
||||
if (pos === "right") {
|
||||
dates.value[1] = d;
|
||||
return;
|
||||
}
|
||||
dates.value[0] = d;
|
||||
};
|
||||
const dc = (e: any) => {
|
||||
show.value = (datepicker.value as any).contains(e.target) && !props.disabled;
|
||||
};
|
||||
const quickPick = (type: string) => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
switch (type) {
|
||||
case "quarter":
|
||||
start.setTime(start.getTime() - 60 * 15 * 1000); //15 mins
|
||||
break;
|
||||
case "half":
|
||||
start.setTime(start.getTime() - 60 * 30 * 1000); //30 mins
|
||||
break;
|
||||
case "hour":
|
||||
start.setTime(start.getTime() - 3600 * 1000); //1 hour
|
||||
break;
|
||||
case "day":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24); //1 day
|
||||
break;
|
||||
case "week":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); //1 week
|
||||
break;
|
||||
case "month":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); //1 month
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dates.value = [start, end];
|
||||
emit("input", get());
|
||||
};
|
||||
const submit = () => {
|
||||
emit("confirm", get());
|
||||
show.value = false;
|
||||
};
|
||||
const cancel = () => {
|
||||
emit("cancel");
|
||||
show.value = false;
|
||||
};
|
||||
onMounted(() => {
|
||||
dates.value = vi(props.value);
|
||||
document.addEventListener("click", dc, true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", dc, true);
|
||||
});
|
||||
watch(
|
||||
() => props.value,
|
||||
(val: unknown) => {
|
||||
dates.value = vi(val);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes datepicker-anim-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
@keyframes datepicker-anim-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes datepicker-anim-out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
color: #515a6ecc;
|
||||
}
|
||||
|
||||
.datepicker-close {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-close:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
background: #ccc
|
||||
url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3IDciIHdpZHRoPSI3IiBoZWlnaHQ9IjciPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01LjU4LDVsMi44LTIuODFBLjQxLjQxLDAsMSwwLDcuOCwxLjZMNSw0LjQxLDIuMiwxLjZhLjQxLjQxLDAsMCwwLS41OC41OGgwTDQuNDIsNSwxLjYyLDcuOGEuNDEuNDEsMCwwLDAsLjU4LjU4TDUsNS41OCw3LjgsOC4zOWEuNDEuNDEsMCwwLDAsLjU4LS41OGgwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuNSAtMS40OCkiIHN0eWxlPSJmaWxsOiNmZmYiLz48L3N2Zz4NCg==")
|
||||
no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.datepicker__clearable:hover:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.datepicker__clearable:hover .datepicker-close {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.datepicker-close:hover:before {
|
||||
background-color: #afafaf;
|
||||
}
|
||||
|
||||
.datepicker > input {
|
||||
color: inherit;
|
||||
// transition: all 200ms ease;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
background: none;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
font-family: "Monaco";
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
// .datepicker > input.focus {
|
||||
// border-color: #3f97e3;
|
||||
// -webkit-box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// }
|
||||
|
||||
.datepicker > input:disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: #ebebe4;
|
||||
border-color: #e5e5e5;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.datepicker-popup {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
transition: all 200ms ease;
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
margin-top: 2px;
|
||||
outline: 0;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
z-index: 999;
|
||||
|
||||
&.top {
|
||||
bottom: 30px;
|
||||
right: 0;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
top: 30px;
|
||||
right: 0;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.left {
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: -80px;
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
border-right: solid 1px #eaeaea;
|
||||
}
|
||||
|
||||
&__shortcut {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
line-height: 34px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: #3f97e3;
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-left: 100px;
|
||||
padding-left: 5px;
|
||||
@keyframes datepicker-anim-out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-inline {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
}
|
||||
.datepicker {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker-range {
|
||||
min-width: 238px;
|
||||
}
|
||||
.datepicker-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
color: #515a6ecc;
|
||||
}
|
||||
|
||||
.datepicker-range .datepicker-popup {
|
||||
width: 520px;
|
||||
}
|
||||
.datepicker-close {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-bottom {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
.datepicker-close:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
background: #ccc
|
||||
url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3IDciIHdpZHRoPSI3IiBoZWlnaHQ9IjciPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01LjU4LDVsMi44LTIuODFBLjQxLjQxLDAsMSwwLDcuOCwxLjZMNSw0LjQxLDIuMiwxLjZhLjQxLjQxLDAsMCwwLS41OC41OGgwTDQuNDIsNSwxLjYyLDcuOGEuNDEuNDEsMCwwLDAsLjU4LjU4TDUsNS41OCw3LjgsOC4zOWEuNDEuNDEsMCwwLDAsLjU4LS41OGgwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuNSAtMS40OCkiIHN0eWxlPSJmaWxsOiNmZmYiLz48L3N2Zz4NCg==")
|
||||
no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.datepicker-btn {
|
||||
padding: 5px 10px;
|
||||
background: #3f97e3;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker__clearable:hover:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.datepicker-anim-enter-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-in 0.2s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.datepicker__clearable:hover .datepicker-close {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.datepicker-anim-leave-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-out 0.2s cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
.datepicker-close:hover:before {
|
||||
background-color: #afafaf;
|
||||
}
|
||||
|
||||
.datepicker__buttons {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
.datepicker > input {
|
||||
color: inherit;
|
||||
// transition: all 200ms ease;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
background: none;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
font-family: "Monaco";
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.datepicker__buttons button {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin: 10px 0 0 5px;
|
||||
padding: 5px 15px;
|
||||
color: #ffffff;
|
||||
}
|
||||
// .datepicker > input.focus {
|
||||
// border-color: #3f97e3;
|
||||
// -webkit-box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// }
|
||||
|
||||
.datepicker__buttons .datepicker__button-select {
|
||||
background: #3f97e3;
|
||||
}
|
||||
.datepicker > input:disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: #ebebe4;
|
||||
border-color: #e5e5e5;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.datepicker__buttons .datepicker__button-cancel {
|
||||
background: #666;
|
||||
}
|
||||
.datepicker-popup {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
transition: all 200ms ease;
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
margin-top: 2px;
|
||||
outline: 0;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
z-index: 999;
|
||||
|
||||
&.top {
|
||||
bottom: 30px;
|
||||
right: 0;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
top: 30px;
|
||||
right: 0;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.left {
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: -80px;
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
border-right: solid 1px #eaeaea;
|
||||
}
|
||||
|
||||
&__shortcut {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
line-height: 34px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: #3f97e3;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-left: 100px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-inline {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.datepicker-range {
|
||||
min-width: 238px;
|
||||
}
|
||||
|
||||
.datepicker-range .datepicker-popup {
|
||||
width: 520px;
|
||||
}
|
||||
|
||||
.datepicker-bottom {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.datepicker-btn {
|
||||
padding: 5px 10px;
|
||||
background: #3f97e3;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-anim-enter-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-in 0.2s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
|
||||
.datepicker-anim-leave-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-out 0.2s cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
|
||||
.datepicker__buttons {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.datepicker__buttons button {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin: 10px 0 0 5px;
|
||||
padding: 5px 15px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.datepicker__buttons .datepicker__button-select {
|
||||
background: #3f97e3;
|
||||
}
|
||||
|
||||
.datepicker__buttons .datepicker__button-cancel {
|
||||
background: #666;
|
||||
}
|
||||
</style>
|
||||
|
@@ -14,11 +14,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// https://docs.cypress.io/api/introduction/api.html
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
// import { mount } from '@vue/test-utils'
|
||||
// import HelloWorld from '../HelloWorld.vue'
|
||||
|
||||
// describe('HelloWorld', () => {
|
||||
// it('renders properly', () => {
|
||||
// const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
|
||||
// expect(wrapper.text()).toContain('Hello Vitest')
|
||||
// })
|
||||
// })
|
||||
describe("My First Test", () => {
|
||||
it("Visits the app root url", () => {
|
||||
cy.visit("/");
|
||||
cy.contains("h1", "Welcome to Your Vue.js + TypeScript App");
|
||||
it("renders props.msg when passed", () => {
|
||||
const msg = "new message";
|
||||
console.log(msg);
|
||||
});
|
||||
});
|
@@ -23,7 +23,7 @@ import Radio from "./Radio.vue";
|
||||
import SelectSingle from "./SelectSingle.vue";
|
||||
import VueGridLayout from "vue-grid-layout";
|
||||
|
||||
const components: { [key: string]: any } = {
|
||||
const components: Indexable = {
|
||||
Icon,
|
||||
TimePicker,
|
||||
VueGridLayout,
|
||||
|
@@ -14,22 +14,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import axios from "axios";
|
||||
import { cancelToken } from "@/utils/cancelToken";
|
||||
|
||||
async function query(param: {
|
||||
queryStr: string;
|
||||
conditions: { [key: string]: unknown };
|
||||
}) {
|
||||
async function query(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
|
||||
const res: AxiosResponse = await axios.post(
|
||||
"/graphql",
|
||||
{ query: param.queryStr, variables: { ...param.conditions } },
|
||||
{ cancelToken: cancelToken() }
|
||||
{ cancelToken: cancelToken() },
|
||||
);
|
||||
if (res.data.errors) {
|
||||
res.data.errors = res.data.errors
|
||||
.map((e: { message: string }) => e.message)
|
||||
.join(" ");
|
||||
res.data.errors = res.data.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@@ -16,8 +16,7 @@
|
||||
*/
|
||||
|
||||
export const Alarm = {
|
||||
variable:
|
||||
"$keyword: String, $scope: Scope, $duration:Duration!, $tags:[AlarmTag], $paging: Pagination!",
|
||||
variable: "$keyword: String, $scope: Scope, $duration:Duration!, $tags:[AlarmTag], $paging: Pagination!",
|
||||
query: `
|
||||
getAlarm(keyword: $keyword, scope: $scope, duration: $duration, paging: $paging, tags: $tags) {
|
||||
items: msgs {
|
||||
|
@@ -34,20 +34,36 @@ export const createEBPFTask = {
|
||||
};
|
||||
export const queryEBPFTasks = {
|
||||
variable:
|
||||
"$serviceId: ID, $serviceInstanceId: ID, $targets: [EBPFProfilingTargetType!]",
|
||||
"$serviceId: ID, $serviceInstanceId: ID, $targets: [EBPFProfilingTargetType!], $triggerType: EBPFProfilingTriggerType",
|
||||
query: `
|
||||
queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId, serviceInstanceId: $serviceInstanceId, targets: $targets) {
|
||||
queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId, serviceInstanceId: $serviceInstanceId, targets: $targets, triggerType: $triggerType) {
|
||||
taskId
|
||||
serviceName
|
||||
serviceId
|
||||
serviceInstanceId
|
||||
serviceInstanceName
|
||||
processLabels
|
||||
processName
|
||||
processId
|
||||
taskStartTime
|
||||
triggerType
|
||||
fixedTriggerDuration
|
||||
targetType
|
||||
createTime
|
||||
continuousProfilingCauses {
|
||||
type
|
||||
singleValue {
|
||||
threshold
|
||||
current
|
||||
}
|
||||
uri {
|
||||
uriRegex
|
||||
uriPath
|
||||
threshold
|
||||
current
|
||||
}
|
||||
message
|
||||
}
|
||||
}`,
|
||||
};
|
||||
export const queryEBPFSchedules = {
|
||||
@@ -112,3 +128,26 @@ export const keepNetworkProfiling = {
|
||||
errorReason
|
||||
}`,
|
||||
};
|
||||
|
||||
export const monitoringInstances = {
|
||||
variable: "$serviceId: ID!, $target: ContinuousProfilingTargetType!",
|
||||
query: `
|
||||
instances: queryContinuousProfilingMonitoringInstances(serviceId: $serviceId, target: $target) {
|
||||
id
|
||||
name
|
||||
attributes {
|
||||
name
|
||||
value
|
||||
}
|
||||
triggeredCount
|
||||
lastTriggerTimestamp
|
||||
processes {
|
||||
id
|
||||
name
|
||||
detectType
|
||||
labels
|
||||
lastTriggerTimestamp
|
||||
triggeredCount
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
@@ -15,37 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const ProfileSegment = {
|
||||
variable: "$segmentId: String",
|
||||
query: `
|
||||
segment: getProfiledSegment(segmentId: $segmentId) {
|
||||
spans {
|
||||
spanId
|
||||
parentSpanId
|
||||
serviceCode
|
||||
startTime
|
||||
endTime
|
||||
endpointName
|
||||
type
|
||||
peer
|
||||
component
|
||||
isError
|
||||
layer
|
||||
tags {
|
||||
key value
|
||||
}
|
||||
logs {
|
||||
time
|
||||
data {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const CreateProfileTask = {
|
||||
variable: "$creationRequest: ProfileTaskCreationRequest",
|
||||
query: `
|
||||
@@ -79,23 +48,55 @@ export const GetProfileTaskList = {
|
||||
`,
|
||||
};
|
||||
export const GetProfileTaskSegmentList = {
|
||||
variable: "$taskID: String",
|
||||
variable: "$taskID: ID!",
|
||||
query: `
|
||||
segmentList: getProfileTaskSegmentList(taskID: $taskID) {
|
||||
segmentId
|
||||
segmentList: getProfileTaskSegments(taskID: $taskID) {
|
||||
traceId
|
||||
instanceId
|
||||
instanceName
|
||||
endpointNames
|
||||
start
|
||||
duration
|
||||
traceIds
|
||||
isError
|
||||
start
|
||||
spans {
|
||||
spanId
|
||||
parentSpanId
|
||||
segmentId
|
||||
refs {
|
||||
traceId
|
||||
parentSegmentId
|
||||
parentSpanId
|
||||
type
|
||||
}
|
||||
serviceCode
|
||||
serviceInstanceName
|
||||
startTime
|
||||
endTime
|
||||
endpointName
|
||||
type
|
||||
peer
|
||||
component
|
||||
isError
|
||||
layer
|
||||
tags {
|
||||
key value
|
||||
}
|
||||
logs {
|
||||
time
|
||||
data {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
profiled
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const GetProfileAnalyze = {
|
||||
variable: "$segmentId: String!, $timeRanges: [ProfileAnalyzeTimeRange!]!",
|
||||
variable: "$queries: [SegmentProfileAnalyzeQuery!]!",
|
||||
query: `
|
||||
analyze: getProfileAnalyze(segmentId: $segmentId, timeRanges: $timeRanges) {
|
||||
analyze: getSegmentsProfileAnalyze(queries: $queries) {
|
||||
tip
|
||||
trees {
|
||||
elements {
|
||||
@@ -122,3 +123,29 @@ export const GetProfileTaskLogs = {
|
||||
}
|
||||
`,
|
||||
};
|
||||
export const GetStrategyList = {
|
||||
variable: "$serviceId: ID!",
|
||||
query: `
|
||||
strategyList: queryContinuousProfilingServiceTargets(serviceId: $serviceId) {
|
||||
type
|
||||
checkItems {
|
||||
type
|
||||
threshold
|
||||
period
|
||||
count
|
||||
uriList
|
||||
uriRegex
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const EditStrategy = {
|
||||
variable: "$request: ContinuousProfilingPolicyCreation!",
|
||||
query: `
|
||||
strategy: setContinuousProfilingPolicy(request: $request) {
|
||||
errorReason
|
||||
status
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
@@ -53,8 +53,7 @@ export const EndpointTopology = {
|
||||
}`,
|
||||
};
|
||||
export const InstanceTopology = {
|
||||
variable:
|
||||
"$clientServiceId: ID!, $serverServiceId: ID!, $duration: Duration!",
|
||||
variable: "$clientServiceId: ID!, $serverServiceId: ID!, $duration: Duration!",
|
||||
query: `
|
||||
topology: getServiceInstanceTopology(clientServiceId: $clientServiceId,
|
||||
serverServiceId: $serverServiceId, duration: $duration) {
|
||||
|
@@ -14,7 +14,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import axios, { AxiosPromise, AxiosResponse } from "axios";
|
||||
import type { AxiosPromise, AxiosResponse } from "axios";
|
||||
import axios from "axios";
|
||||
import { cancelToken } from "@/utils/cancelToken";
|
||||
import * as app from "./query/app";
|
||||
import * as selector from "./query/selector";
|
||||
@@ -55,13 +56,11 @@ class Graphql {
|
||||
query: query[this.queryData],
|
||||
variables: variablesData,
|
||||
},
|
||||
{ cancelToken: cancelToken() }
|
||||
{ cancelToken: cancelToken() },
|
||||
)
|
||||
.then((res: AxiosResponse) => {
|
||||
if (res.data.errors) {
|
||||
res.data.errors = res.data.errors
|
||||
.map((e: { message: string }) => e.message)
|
||||
.join(" ");
|
||||
res.data.errors = res.data.errors.map((e: { message: string }) => e.message).join(" ");
|
||||
}
|
||||
return res;
|
||||
})
|
||||
|
@@ -23,6 +23,7 @@ import {
|
||||
analysisEBPFResult,
|
||||
createNetworkProfiling,
|
||||
keepNetworkProfiling,
|
||||
monitoringInstances,
|
||||
} from "../fragments/ebpf";
|
||||
|
||||
export const getCreateTaskData = `query queryCreateTaskData(${queryCreateTaskData.variable}) {${queryCreateTaskData.query}}`;
|
||||
@@ -38,3 +39,5 @@ export const getEBPFResult = `query analysisEBPFResult(${analysisEBPFResult.vari
|
||||
export const newNetworkProfiling = `mutation createNetworkProfiling(${createNetworkProfiling.variable}) {${createNetworkProfiling.query}}`;
|
||||
|
||||
export const aliveNetworkProfiling = `mutation keepNetworkProfiling(${keepNetworkProfiling.variable}) {${keepNetworkProfiling.query}}`;
|
||||
|
||||
export const getMonitoringInstances = `query continuousProfilingMonitoringInstances(${monitoringInstances.variable}) {${monitoringInstances.query}}`;
|
||||
|
@@ -16,16 +16,15 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
ProfileSegment,
|
||||
CreateProfileTask,
|
||||
GetProfileTaskList,
|
||||
GetProfileTaskSegmentList,
|
||||
GetProfileAnalyze,
|
||||
GetProfileTaskLogs,
|
||||
GetStrategyList,
|
||||
EditStrategy,
|
||||
} from "../fragments/profile";
|
||||
|
||||
export const queryProfileSegment = `query queryProfileSegment(${ProfileSegment.variable}) {${ProfileSegment.query}}`;
|
||||
|
||||
export const saveProfileTask = `mutation createProfileTask(${CreateProfileTask.variable}) {${CreateProfileTask.query}}`;
|
||||
|
||||
export const getProfileTaskList = `query getProfileTaskList(${GetProfileTaskList.variable}) {
|
||||
@@ -37,3 +36,7 @@ export const getProfileTaskSegmentList = `query getProfileTaskSegmentList(${GetP
|
||||
export const getProfileAnalyze = `query getProfileAnalyze(${GetProfileAnalyze.variable}) {${GetProfileAnalyze.query}}`;
|
||||
|
||||
export const getProfileTaskLogs = `query profileTaskLogs(${GetProfileTaskLogs.variable}) {${GetProfileTaskLogs.query}}`;
|
||||
|
||||
export const getStrategyList = `query getStrategyList(${GetStrategyList.variable}) {${GetStrategyList.query}}`;
|
||||
|
||||
export const editStrategy = `mutation editStrategy(${EditStrategy.variable}) {${EditStrategy.query}}`;
|
||||
|
@@ -14,12 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
InstanceTopology,
|
||||
EndpointTopology,
|
||||
ServicesTopology,
|
||||
ProcessTopology,
|
||||
} from "../fragments/topology";
|
||||
import { InstanceTopology, EndpointTopology, ServicesTopology, ProcessTopology } from "../fragments/topology";
|
||||
|
||||
export const getInstanceTopology = `query queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`;
|
||||
export const getEndpointTopology = `query queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`;
|
||||
|
@@ -15,12 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Traces,
|
||||
TraceSpans,
|
||||
TraceTagKeys,
|
||||
TraceTagValues,
|
||||
} from "../fragments/trace";
|
||||
import { Traces, TraceSpans, TraceTagKeys, TraceTagValues } from "../fragments/trace";
|
||||
|
||||
export const queryTraces = `query queryTraces(${Traces.variable}) {${Traces.query}}`;
|
||||
|
||||
|
@@ -22,6 +22,7 @@ export enum MetricQueryTypes {
|
||||
READHEATMAP = "readHeatMap",
|
||||
ReadSampledRecords = "readSampledRecords",
|
||||
ReadRecords = "readRecords",
|
||||
ReadNullableMetricsValue = "readNullableMetricsValue",
|
||||
}
|
||||
|
||||
export enum Calculations {
|
||||
@@ -30,7 +31,6 @@ export enum Calculations {
|
||||
ByteToMB = "byteToMB",
|
||||
ByteToGB = "byteToGB",
|
||||
Apdex = "apdex",
|
||||
Precision = "precision",
|
||||
ConvertSeconds = "convertSeconds",
|
||||
ConvertMilliseconds = "convertMilliseconds",
|
||||
MsToS = "msTos",
|
||||
@@ -67,14 +67,18 @@ screenMap.set(sizeEnum.LG, screenEnum.LG);
|
||||
screenMap.set(sizeEnum.XL, screenEnum.XL);
|
||||
screenMap.set(sizeEnum.XXL, screenEnum.XXL);
|
||||
|
||||
export const RespFields: any = {
|
||||
export const RespFields: Indexable = {
|
||||
readMetricsValues: `{
|
||||
label
|
||||
values {
|
||||
values {value}
|
||||
values {value isEmptyValue}
|
||||
}
|
||||
}`,
|
||||
readMetricsValue: "",
|
||||
readMetricsValue: ``,
|
||||
readNullableMetricsValue: `{
|
||||
value
|
||||
isEmptyValue
|
||||
}`,
|
||||
sortMetrics: `{
|
||||
name
|
||||
id
|
||||
@@ -84,7 +88,7 @@ export const RespFields: any = {
|
||||
readLabeledMetricsValues: `{
|
||||
label
|
||||
values {
|
||||
values {value}
|
||||
values {value isEmptyValue}
|
||||
}
|
||||
}`,
|
||||
readHeatMap: `{
|
||||
@@ -108,4 +112,21 @@ export const RespFields: any = {
|
||||
value
|
||||
refId
|
||||
}`,
|
||||
execExpression: `{
|
||||
type
|
||||
results {
|
||||
metric {
|
||||
labels {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
values {
|
||||
name: id
|
||||
value
|
||||
refId: traceID
|
||||
}
|
||||
}
|
||||
error
|
||||
}`,
|
||||
};
|
||||
|
@@ -17,9 +17,9 @@
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import dateFormatStep from "@/utils/dateFormat";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
import { EventParams } from "@/types/app";
|
||||
import type { EventParams } from "@/types/app";
|
||||
|
||||
export default function associateProcessor(props: any) {
|
||||
export default function associateProcessor(props: Indexable) {
|
||||
function eventAssociate() {
|
||||
if (!props.filters) {
|
||||
return;
|
||||
@@ -30,9 +30,7 @@ export default function associateProcessor(props: any) {
|
||||
if (!props.option.series[0]) {
|
||||
return;
|
||||
}
|
||||
const list = props.option.series[0].data.map(
|
||||
(d: (number | string)[]) => d[0]
|
||||
);
|
||||
const list = props.option.series[0].data.map((d: (number | string)[]) => d[0]);
|
||||
if (!list.includes(props.filters.duration.endTime)) {
|
||||
return;
|
||||
}
|
||||
@@ -77,16 +75,8 @@ export default function associateProcessor(props: any) {
|
||||
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
|
||||
),
|
||||
start: dateFormatStep(getLocalTime(appStore.utc, new Date(start)), step, true),
|
||||
end: dateFormatStep(getLocalTime(appStore.utc, new Date(end)), step, true),
|
||||
step,
|
||||
};
|
||||
}
|
||||
@@ -95,44 +85,36 @@ export default function associateProcessor(props: any) {
|
||||
const queryOrder = relatedTrace.queryOrder;
|
||||
const latency = relatedTrace.latency;
|
||||
const series = props.option.series || [];
|
||||
const item: any = {
|
||||
const item: Indexable = {
|
||||
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,
|
||||
};
|
||||
}
|
||||
);
|
||||
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],
|
||||
};
|
||||
}
|
||||
);
|
||||
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 };
|
||||
}
|
||||
|
@@ -14,7 +14,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ref, computed, ComputedRef, unref } from "vue";
|
||||
import type { ComputedRef } from "vue";
|
||||
import { ref, computed, unref } from "vue";
|
||||
import { useEventListener } from "./useEventListener";
|
||||
import { screenMap, sizeEnum, screenEnum } from "./data";
|
||||
|
||||
@@ -31,7 +32,7 @@ export interface CreateCallbackParams {
|
||||
sizeEnum: typeof sizeEnum;
|
||||
}
|
||||
|
||||
export function useBreakpoint(): any {
|
||||
export function useBreakpoint(): Indexable {
|
||||
return {
|
||||
screenRef: computed(() => unref(globalScreenRef)),
|
||||
widthRef: globalWidthRef,
|
||||
@@ -40,9 +41,7 @@ export function useBreakpoint(): any {
|
||||
};
|
||||
}
|
||||
|
||||
export function createBreakpointListen(
|
||||
fn?: (opt: CreateCallbackParams) => void
|
||||
): any {
|
||||
export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void): Indexable {
|
||||
const screenRef = ref<sizeEnum>(sizeEnum.XL || "");
|
||||
const realWidthRef = ref(window.innerWidth);
|
||||
|
||||
|
@@ -16,19 +16,15 @@
|
||||
*/
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
import type { LayoutConfig } from "@/types/dashboard";
|
||||
|
||||
export default function getDashboard(param?: {
|
||||
name: string;
|
||||
layer: string;
|
||||
entity: string;
|
||||
}) {
|
||||
export default function getDashboard(param?: { name: string; layer: string; entity: string }) {
|
||||
const dashboardStore = useDashboardStore();
|
||||
const opt = param || dashboardStore.currentDashboard;
|
||||
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
|
||||
const dashboard = list.find(
|
||||
(d: { name: string; layer: string; entity: string }) =>
|
||||
d.name === opt.name && d.entity === opt.entity && d.layer === opt.layer
|
||||
d.name === opt.name && d.entity === opt.entity && d.layer === opt.layer,
|
||||
);
|
||||
const all = dashboardStore.layout;
|
||||
const widgets: LayoutConfig[] = [];
|
||||
@@ -63,7 +59,7 @@ export default function getDashboard(param?: {
|
||||
if (targetTabIndex[1] === undefined) {
|
||||
container = document.querySelector(".ds-main");
|
||||
} else {
|
||||
const w = widgets.find((d: any) => d.id === targetTabIndex[0]);
|
||||
const w = widgets.find((d: Indexable) => d.id === targetTabIndex[0]);
|
||||
container = document.querySelector(".tab-layout");
|
||||
const layout: Nullable<Element> = document.querySelector(".ds-main");
|
||||
if (w && layout) {
|
||||
|
@@ -14,13 +14,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
BarSeriesOption,
|
||||
LineSeriesOption,
|
||||
HeatmapSeriesOption,
|
||||
SankeySeriesOption,
|
||||
} from "echarts/charts";
|
||||
import {
|
||||
import type { BarSeriesOption, LineSeriesOption, HeatmapSeriesOption, SankeySeriesOption } from "echarts/charts";
|
||||
import type {
|
||||
TitleComponentOption,
|
||||
TooltipComponentOption,
|
||||
GridComponentOption,
|
||||
@@ -48,10 +43,7 @@ export type ECOption = echarts.ComposeOption<
|
||||
| SankeySeriesOption
|
||||
>;
|
||||
|
||||
export function useECharts(
|
||||
elRef: Ref<HTMLDivElement>,
|
||||
theme: "light" | "dark" | "default" = "default"
|
||||
): any {
|
||||
export function useECharts(elRef: Ref<HTMLDivElement>, theme: "light" | "dark" | "default" = "default"): Indexable {
|
||||
const getDarkMode = computed(() => {
|
||||
return theme === "default" ? "light" : theme;
|
||||
});
|
||||
@@ -131,7 +123,7 @@ export function useECharts(
|
||||
initCharts(theme as "default");
|
||||
setOptions(cacheOptions.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
|
@@ -20,7 +20,7 @@ import { useThrottleFn, useDebounceFn } from "@vueuse/core";
|
||||
|
||||
export type RemoveEventFn = () => void;
|
||||
export interface UseEventParams {
|
||||
el?: Element | Ref<Element | undefined> | Window | any;
|
||||
el?: Element | Ref<Element | undefined> | Window | Recordable;
|
||||
name: string;
|
||||
listener: EventListener;
|
||||
options?: boolean | AddEventListenerOptions;
|
||||
@@ -43,16 +43,13 @@ export function useEventListener({
|
||||
if (el) {
|
||||
const element = ref(el as Element) as Ref<Element>;
|
||||
|
||||
const handler = isDebounce
|
||||
? useDebounceFn(listener, wait)
|
||||
: useThrottleFn(listener, wait);
|
||||
const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait);
|
||||
const realHandler = wait ? handler : listener;
|
||||
const removeEventListener = (e: Element) => {
|
||||
isAddRef.value = true;
|
||||
e.removeEventListener(name, realHandler, options);
|
||||
};
|
||||
const addEventListener = (e: Element) =>
|
||||
e.addEventListener(name, realHandler, options);
|
||||
const addEventListener = (e: Element) => e.addEventListener(name, realHandler, options);
|
||||
|
||||
const removeWatch = watch(
|
||||
element,
|
||||
@@ -64,7 +61,7 @@ export function useEventListener({
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
remove = () => {
|
||||
|
313
src/hooks/useExpressionsProcessor.ts
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* 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 { RespFields } from "./data";
|
||||
import { ExpressionResultType } from "@/views/dashboard/data";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import type { MetricConfigOpt } from "@/types/dashboard";
|
||||
import type { Instance, Endpoint, Service } from "@/types/selector";
|
||||
|
||||
export async function useExpressionsQueryProcessor(config: Indexable) {
|
||||
function expressionsGraphqlPods() {
|
||||
if (!(config.metrics && config.metrics[0])) {
|
||||
return;
|
||||
}
|
||||
const appStore = useAppStoreWithOut();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const selectorStore = useSelectorStore();
|
||||
|
||||
if (!selectorStore.currentService && dashboardStore.entity !== "All") {
|
||||
return;
|
||||
}
|
||||
const conditions: Recordable = {
|
||||
duration: appStore.durationTime,
|
||||
};
|
||||
const variables: string[] = [`$duration: Duration!`];
|
||||
const isRelation = ["ServiceRelation", "ServiceInstanceRelation", "EndpointRelation", "ProcessRelation"].includes(
|
||||
dashboardStore.entity,
|
||||
);
|
||||
if (isRelation && !selectorStore.currentDestService) {
|
||||
return;
|
||||
}
|
||||
const fragment = config.metrics.map((name: string, index: number) => {
|
||||
variables.push(`$expression${index}: String!`, `$entity${index}: Entity!`);
|
||||
conditions[`expression${index}`] = name;
|
||||
const entity = {
|
||||
serviceName: dashboardStore.entity === "All" ? undefined : selectorStore.currentService.value,
|
||||
normal: dashboardStore.entity === "All" ? undefined : selectorStore.currentService.normal,
|
||||
serviceInstanceName: ["ServiceInstance", "ServiceInstanceRelation", "ProcessRelation", "Process"].includes(
|
||||
dashboardStore.entity,
|
||||
)
|
||||
? selectorStore.currentPod && selectorStore.currentPod.value
|
||||
: undefined,
|
||||
endpointName: dashboardStore.entity.includes("Endpoint")
|
||||
? selectorStore.currentPod && selectorStore.currentPod.value
|
||||
: undefined,
|
||||
processName: dashboardStore.entity.includes("Process")
|
||||
? selectorStore.currentProcess && selectorStore.currentProcess.value
|
||||
: undefined,
|
||||
destNormal: isRelation ? selectorStore.currentDestService.normal : undefined,
|
||||
destServiceName: isRelation ? selectorStore.currentDestService.value : undefined,
|
||||
destServiceInstanceName: ["ServiceInstanceRelation", "ProcessRelation"].includes(dashboardStore.entity)
|
||||
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
|
||||
: undefined,
|
||||
destEndpointName:
|
||||
dashboardStore.entity === "EndpointRelation"
|
||||
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
|
||||
: undefined,
|
||||
destProcessName: dashboardStore.entity.includes("ProcessRelation")
|
||||
? selectorStore.currentDestProcess && selectorStore.currentDestProcess.value
|
||||
: undefined,
|
||||
};
|
||||
conditions[`entity${index}`] = entity;
|
||||
|
||||
return `expression${index}: execExpression(expression: $expression${index}, entity: $entity${index}, duration: $duration)${RespFields.execExpression}`;
|
||||
});
|
||||
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
||||
|
||||
return {
|
||||
queryStr,
|
||||
conditions,
|
||||
};
|
||||
}
|
||||
|
||||
function expressionsSource(resp: { errors: string; data: Indexable }) {
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
return { source: {}, tips: [], typesOfMQE: [] };
|
||||
}
|
||||
if (!resp.data) {
|
||||
ElMessage.error("The query is wrong");
|
||||
return { source: {}, tips: [], typesOfMQE: [] };
|
||||
}
|
||||
const tips: string[] = [];
|
||||
const source: { [key: string]: unknown } = {};
|
||||
const keys = Object.keys(resp.data);
|
||||
const typesOfMQE: string[] = [];
|
||||
|
||||
for (let i = 0; i < config.metrics.length; i++) {
|
||||
const c: MetricConfigOpt = (config.metricConfig && config.metricConfig[i]) || {};
|
||||
const obj = resp.data[keys[i]] || {};
|
||||
const results = obj.results || [];
|
||||
const name = config.metrics[i];
|
||||
const type = obj.type;
|
||||
|
||||
tips.push(obj.error);
|
||||
typesOfMQE.push(type);
|
||||
if (!obj.error) {
|
||||
if (type === ExpressionResultType.TIME_SERIES_VALUES) {
|
||||
if (results.length === 1) {
|
||||
source[c.label || name] = results[0].values.map((d: { value: unknown }) => d.value) || [];
|
||||
} else {
|
||||
const labels = (c.label || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||
for (const item of results) {
|
||||
const values = item.values.map((d: { value: unknown }) => d.value) || [];
|
||||
const index = item.metric.labels[0].value;
|
||||
const indexNum = labels.findIndex((_, i: number) => i === Number(index));
|
||||
if (labels[indexNum] && indexNum > -1) {
|
||||
source[labels[indexNum]] = values;
|
||||
} else {
|
||||
source[index] = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type === ExpressionResultType.SINGLE_VALUE) {
|
||||
source[c.label || name] = (results[0].values[0] || {}).value;
|
||||
}
|
||||
if (([ExpressionResultType.RECORD_LIST, ExpressionResultType.SORTED_LIST] as string[]).includes(type)) {
|
||||
source[name] = results[0].values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { source, tips, typesOfMQE };
|
||||
}
|
||||
const params = await expressionsGraphqlPods();
|
||||
if (!params) {
|
||||
return { source: {}, tips: [], typesOfMQE: [] };
|
||||
}
|
||||
|
||||
const dashboardStore = useDashboardStore();
|
||||
const json = await dashboardStore.fetchMetricValue(params);
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return { source: {}, tips: [], typesOfMQE: [] };
|
||||
}
|
||||
const data = expressionsSource(json);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function useExpressionsQueryPodsMetrics(
|
||||
pods: Array<(Instance | Endpoint | Service) & Indexable>,
|
||||
config: {
|
||||
expressions: string[];
|
||||
subExpressions: string[];
|
||||
metricConfig: MetricConfigOpt[];
|
||||
},
|
||||
scope: string,
|
||||
) {
|
||||
function expressionsGraphqlPods() {
|
||||
const metrics: string[] = [];
|
||||
const subMetrics: string[] = [];
|
||||
config.expressions = config.expressions || [];
|
||||
config.subExpressions = config.subExpressions || [];
|
||||
|
||||
for (let i = 0; i < config.expressions.length; i++) {
|
||||
if (config.expressions[i]) {
|
||||
metrics.push(config.expressions[i]);
|
||||
subMetrics.push(config.subExpressions[i]);
|
||||
}
|
||||
}
|
||||
if (!metrics.length) {
|
||||
return;
|
||||
}
|
||||
const appStore = useAppStoreWithOut();
|
||||
const selectorStore = useSelectorStore();
|
||||
const conditions: { [key: string]: unknown } = {
|
||||
duration: appStore.durationTime,
|
||||
};
|
||||
const variables: string[] = [`$duration: Duration!`];
|
||||
const currentService = selectorStore.currentService || {};
|
||||
const fragmentList = pods.map((d: (Instance | Endpoint | Service) & Indexable, index: number) => {
|
||||
const entity = {
|
||||
serviceName: scope === "Service" ? d.label : currentService.label,
|
||||
serviceInstanceName: scope === "ServiceInstance" ? d.label : undefined,
|
||||
endpointName: scope === "Endpoint" ? d.label : undefined,
|
||||
normal: scope === "Service" ? d.normal : currentService.normal,
|
||||
};
|
||||
variables.push(`$entity${index}: Entity!`);
|
||||
conditions[`entity${index}`] = entity;
|
||||
const f = metrics.map((name: string, idx: number) => {
|
||||
variables.push(`$expression${index}${idx}: String!`);
|
||||
conditions[`expression${index}${idx}`] = name;
|
||||
let str = "";
|
||||
if (config.subExpressions[idx]) {
|
||||
variables.push(`$subExpression${index}${idx}: String!`);
|
||||
conditions[`subExpression${index}${idx}`] = config.subExpressions[idx];
|
||||
str = `subexpression${index}${idx}: execExpression(expression: $subExpression${index}${idx}, entity: $entity${index}, duration: $duration)${RespFields.execExpression}`;
|
||||
}
|
||||
|
||||
return (
|
||||
str +
|
||||
`expression${index}${idx}: execExpression(expression: $expression${index}${idx}, entity: $entity${index}, duration: $duration)${RespFields.execExpression}`
|
||||
);
|
||||
});
|
||||
return f;
|
||||
});
|
||||
const fragment = fragmentList.flat(1).join(" ");
|
||||
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
||||
|
||||
return { queryStr, conditions };
|
||||
}
|
||||
|
||||
function expressionsPodsSource(resp: { errors: string; data: Indexable }): Indexable {
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
return {};
|
||||
}
|
||||
const names: string[] = [];
|
||||
const subNames: string[] = [];
|
||||
const metricConfigArr: MetricConfigOpt[] = [];
|
||||
const metricTypesArr: string[] = [];
|
||||
const expressionsTips: string[] = [];
|
||||
const subExpressionsTips: string[] = [];
|
||||
const data = pods.map((d: any, idx: number) => {
|
||||
for (let index = 0; index < config.expressions.length; index++) {
|
||||
const c: MetricConfigOpt = (config.metricConfig && config.metricConfig[index]) || {};
|
||||
const k = "expression" + idx + index;
|
||||
const sub = "subexpression" + idx + index;
|
||||
const obj = resp.data[k] || {};
|
||||
const results = obj.results || [];
|
||||
const typesOfMQE = obj.type || "";
|
||||
const subObj = resp.data[sub] || {};
|
||||
const subResults = subObj.results || [];
|
||||
|
||||
expressionsTips.push(obj.error);
|
||||
subExpressionsTips.push(subObj.error);
|
||||
if (results.length > 1) {
|
||||
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 < results.length; i++) {
|
||||
let name = results[i].metric.labels[0].value || "";
|
||||
const subValues = subResults[i] && subResults[i].values.map((d: { value: unknown }) => d.value);
|
||||
const num = labelsIdx.findIndex((d: string) => d === results[i].metric.labels[0].value);
|
||||
|
||||
if (labels[num]) {
|
||||
name = labels[num];
|
||||
}
|
||||
if (!d[name]) {
|
||||
d[name] = {};
|
||||
}
|
||||
if (subValues) {
|
||||
d[name]["values"] = subValues;
|
||||
}
|
||||
d[name]["avg"] = (results[i].values[0] || {}).value;
|
||||
|
||||
const j = names.find((d: string) => d === name);
|
||||
|
||||
if (!j) {
|
||||
names.push(name);
|
||||
metricConfigArr.push({ ...c, index: i });
|
||||
metricTypesArr.push(typesOfMQE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!results[0]) {
|
||||
return d;
|
||||
}
|
||||
const name = config.expressions[index] || "";
|
||||
const subName = config.subExpressions[index] || "";
|
||||
if (!d[name]) {
|
||||
d[name] = {};
|
||||
}
|
||||
d[name]["avg"] = [(results[0].values[0] || {}).value];
|
||||
if (subResults[0]) {
|
||||
if (!d[subName]) {
|
||||
d[subName] = {};
|
||||
}
|
||||
d[subName]["values"] = subResults[0].values.map((d: { value: number }) => d.value);
|
||||
}
|
||||
const j = names.find((d: string) => d === name);
|
||||
if (!j) {
|
||||
names.push(name);
|
||||
subNames.push(subName);
|
||||
metricConfigArr.push(c);
|
||||
metricTypesArr.push(typesOfMQE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return d;
|
||||
});
|
||||
|
||||
return { data, names, subNames, metricConfigArr, metricTypesArr, expressionsTips, subExpressionsTips };
|
||||
}
|
||||
const dashboardStore = useDashboardStore();
|
||||
const params = await expressionsGraphqlPods();
|
||||
const json = await dashboardStore.fetchMetricValue(params);
|
||||
|
||||
if (json.errors) {
|
||||
ElMessage.error(json.errors);
|
||||
return {};
|
||||
}
|
||||
const expressionParams = expressionsPodsSource(json);
|
||||
|
||||
return expressionParams;
|
||||
}
|
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { LegendOptions } from "@/types/dashboard";
|
||||
import type { LegendOptions } from "@/types/dashboard";
|
||||
import { isDef } from "@/utils/is";
|
||||
|
||||
export default function useLegendProcess(legend?: LegendOptions) {
|
||||
@@ -37,14 +37,9 @@ export default function useLegendProcess(legend?: LegendOptions) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function aggregations(
|
||||
data: { [key: string]: number[] },
|
||||
intervalTime: string[]
|
||||
) {
|
||||
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 keys = Object.keys(data || {}).filter((i: string) => Array.isArray(data[i]) && data[i].length);
|
||||
const headers = [];
|
||||
|
||||
for (const [key, value] of keys.entries()) {
|
||||
@@ -58,12 +53,7 @@ export default function useLegendProcess(legend?: LegendOptions) {
|
||||
value: d,
|
||||
};
|
||||
})
|
||||
.sort(
|
||||
(
|
||||
a: { key: string; value: number },
|
||||
b: { key: string; value: number }
|
||||
) => b.value - a.value
|
||||
)
|
||||
.sort((a: { key: string; value: number }, b: { key: string; value: number }) => b.value - a.value)
|
||||
.filter((_: unknown, index: number) => index < 10),
|
||||
};
|
||||
if (legend) {
|
||||
|
@@ -15,27 +15,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { MetricQueryTypes, Calculations } from "./data";
|
||||
export function useListConfig(config: any, index: string) {
|
||||
import { MetricModes } from "@/views/dashboard/data";
|
||||
|
||||
export function useListConfig(config: Indexable, index: number) {
|
||||
if (config.metricModes === MetricModes.Expression) {
|
||||
return {
|
||||
isLinear: false,
|
||||
isAvg: true,
|
||||
};
|
||||
}
|
||||
const i = Number(index);
|
||||
const types = [
|
||||
Calculations.Average,
|
||||
Calculations.ApdexAvg,
|
||||
Calculations.PercentageAvg,
|
||||
];
|
||||
const calculation =
|
||||
config.metricConfig &&
|
||||
config.metricConfig[i] &&
|
||||
config.metricConfig[i].calculation;
|
||||
const types = [Calculations.Average, Calculations.ApdexAvg, Calculations.PercentageAvg];
|
||||
const calculation = config.metricConfig && config.metricConfig[i] && config.metricConfig[i].calculation;
|
||||
const isLinear =
|
||||
[
|
||||
MetricQueryTypes.ReadMetricsValues,
|
||||
MetricQueryTypes.ReadLabeledMetricsValues,
|
||||
].includes(config.metricTypes[i]) && !types.includes(calculation);
|
||||
[MetricQueryTypes.ReadMetricsValues, MetricQueryTypes.ReadLabeledMetricsValues].includes(config.metricTypes[i]) &&
|
||||
!types.includes(calculation);
|
||||
const isAvg =
|
||||
[
|
||||
MetricQueryTypes.ReadMetricsValues,
|
||||
MetricQueryTypes.ReadLabeledMetricsValues,
|
||||
].includes(config.metricTypes[i]) && types.includes(calculation);
|
||||
[MetricQueryTypes.ReadMetricsValues, MetricQueryTypes.ReadLabeledMetricsValues].includes(config.metricTypes[i]) &&
|
||||
types.includes(calculation);
|
||||
|
||||
return {
|
||||
isLinear,
|
||||
isAvg,
|
||||
|
@@ -20,11 +20,10 @@ import { ElMessage } from "element-plus";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import { MetricConfigOpt } from "@/types/dashboard";
|
||||
import { MetricCatalog } from "@/views/dashboard/data";
|
||||
import type { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import type { MetricConfigOpt } from "@/types/dashboard";
|
||||
|
||||
export function useQueryProcessor(config: any) {
|
||||
export function useQueryProcessor(config: Indexable) {
|
||||
if (!(config.metrics && config.metrics[0])) {
|
||||
return;
|
||||
}
|
||||
@@ -38,57 +37,35 @@ export function useQueryProcessor(config: any) {
|
||||
if (!selectorStore.currentService && dashboardStore.entity !== "All") {
|
||||
return;
|
||||
}
|
||||
const conditions: { [key: string]: unknown } = {
|
||||
const conditions: Recordable = {
|
||||
duration: appStore.durationTime,
|
||||
};
|
||||
const variables: string[] = [`$duration: Duration!`];
|
||||
const isRelation = [
|
||||
"ServiceRelation",
|
||||
"ServiceInstanceRelation",
|
||||
"EndpointRelation",
|
||||
"ProcessRelation",
|
||||
].includes(dashboardStore.entity);
|
||||
const isRelation = ["ServiceRelation", "ServiceInstanceRelation", "EndpointRelation", "ProcessRelation"].includes(
|
||||
dashboardStore.entity,
|
||||
);
|
||||
if (isRelation && !selectorStore.currentDestService) {
|
||||
return;
|
||||
}
|
||||
const fragment = config.metrics.map((name: string, index: number) => {
|
||||
const metricType = config.metricTypes[index] || "";
|
||||
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||
if (
|
||||
[
|
||||
MetricQueryTypes.ReadSampledRecords,
|
||||
MetricQueryTypes.SortMetrics,
|
||||
].includes(metricType)
|
||||
) {
|
||||
if ([MetricQueryTypes.ReadSampledRecords, MetricQueryTypes.SortMetrics].includes(metricType)) {
|
||||
variables.push(`$condition${index}: TopNCondition!`);
|
||||
conditions[`condition${index}`] = {
|
||||
name,
|
||||
parentService: ["All"].includes(dashboardStore.entity)
|
||||
? null
|
||||
: selectorStore.currentService.value,
|
||||
normal: selectorStore.currentService
|
||||
? selectorStore.currentService.normal
|
||||
: true,
|
||||
scope: config.catalog,
|
||||
topN: c.topN || 10,
|
||||
parentService: ["All"].includes(dashboardStore.entity) ? null : selectorStore.currentService.value,
|
||||
normal: selectorStore.currentService ? selectorStore.currentService.normal : true,
|
||||
topN: Number(c.topN) || 10,
|
||||
order: c.sortOrder || "DES",
|
||||
};
|
||||
} else {
|
||||
const entity = {
|
||||
scope: config.catalog,
|
||||
serviceName:
|
||||
dashboardStore.entity === "All"
|
||||
? undefined
|
||||
: selectorStore.currentService.value,
|
||||
normal:
|
||||
dashboardStore.entity === "All"
|
||||
? undefined
|
||||
: selectorStore.currentService.normal,
|
||||
serviceInstanceName: [
|
||||
"ServiceInstance",
|
||||
"ServiceInstanceRelation",
|
||||
"ProcessRelation",
|
||||
].includes(dashboardStore.entity)
|
||||
serviceName: dashboardStore.entity === "All" ? undefined : selectorStore.currentService.value,
|
||||
normal: dashboardStore.entity === "All" ? undefined : selectorStore.currentService.normal,
|
||||
serviceInstanceName: ["ServiceInstance", "ServiceInstanceRelation", "ProcessRelation"].includes(
|
||||
dashboardStore.entity,
|
||||
)
|
||||
? selectorStore.currentPod && selectorStore.currentPod.value
|
||||
: undefined,
|
||||
endpointName: dashboardStore.entity.includes("Endpoint")
|
||||
@@ -97,16 +74,9 @@ export function useQueryProcessor(config: any) {
|
||||
processName: dashboardStore.entity.includes("Process")
|
||||
? selectorStore.currentProcess && selectorStore.currentProcess.value
|
||||
: undefined,
|
||||
destNormal: isRelation
|
||||
? selectorStore.currentDestService.normal
|
||||
: undefined,
|
||||
destServiceName: isRelation
|
||||
? selectorStore.currentDestService.value
|
||||
: undefined,
|
||||
destServiceInstanceName: [
|
||||
"ServiceInstanceRelation",
|
||||
"ProcessRelation",
|
||||
].includes(dashboardStore.entity)
|
||||
destNormal: isRelation ? selectorStore.currentDestService.normal : undefined,
|
||||
destServiceName: isRelation ? selectorStore.currentDestService.value : undefined,
|
||||
destServiceInstanceName: ["ServiceInstanceRelation", "ProcessRelation"].includes(dashboardStore.entity)
|
||||
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
|
||||
: undefined,
|
||||
destEndpointName:
|
||||
@@ -114,8 +84,7 @@ export function useQueryProcessor(config: any) {
|
||||
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
|
||||
: undefined,
|
||||
destProcessName: dashboardStore.entity.includes("ProcessRelation")
|
||||
? selectorStore.currentDestProcess &&
|
||||
selectorStore.currentDestProcess.value
|
||||
? selectorStore.currentDestProcess && selectorStore.currentDestProcess.value
|
||||
: undefined,
|
||||
};
|
||||
if ([MetricQueryTypes.ReadRecords].includes(metricType)) {
|
||||
@@ -123,15 +92,12 @@ export function useQueryProcessor(config: any) {
|
||||
conditions[`condition${index}`] = {
|
||||
name,
|
||||
parentEntity: entity,
|
||||
topN: c.topN || 10,
|
||||
topN: Number(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, ""));
|
||||
const labels = (c.labelsIndex || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||
variables.push(`$labels${index}: [String!]!`);
|
||||
conditions[`labels${index}`] = labels;
|
||||
}
|
||||
@@ -144,9 +110,10 @@ export function useQueryProcessor(config: any) {
|
||||
}
|
||||
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||
return `${name}${index}: ${metricType}(condition: $condition${index}, labels: $labels${index}, duration: $duration)${RespFields[metricType]}`;
|
||||
} else {
|
||||
return `${name}${index}: ${metricType}(condition: $condition${index}, duration: $duration)${RespFields[metricType]}`;
|
||||
}
|
||||
const t = metricType === MetricQueryTypes.ReadMetricsValue ? MetricQueryTypes.ReadNullableMetricsValue : metricType;
|
||||
|
||||
return `${name}${index}: ${t}(condition: $condition${index}, duration: $duration)${RespFields[t]}`;
|
||||
});
|
||||
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
||||
|
||||
@@ -156,12 +123,12 @@ export function useQueryProcessor(config: any) {
|
||||
};
|
||||
}
|
||||
export function useSourceProcessor(
|
||||
resp: { errors: string; data: { [key: string]: any } },
|
||||
resp: { errors: string; data: Indexable },
|
||||
config: {
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
metricConfig: MetricConfigOpt[];
|
||||
}
|
||||
},
|
||||
) {
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
@@ -179,22 +146,15 @@ export function useSourceProcessor(
|
||||
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||
|
||||
if (type === MetricQueryTypes.ReadMetricsValues) {
|
||||
source[c.label || m] =
|
||||
(resp.data[keys[index]] &&
|
||||
calculateExp(resp.data[keys[index]].values.values, c)) ||
|
||||
[];
|
||||
source[c.label || m] = (resp.data[keys[index]] && calculateExp(resp.data[keys[index]].values.values, c)) || [];
|
||||
}
|
||||
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||
const resVal = Object.values(resp.data)[0] || [];
|
||||
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, ""));
|
||||
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 (const item of resVal) {
|
||||
const values = item.values.values.map((d: { value: number }) =>
|
||||
aggregation(Number(d.value), c)
|
||||
const values = item.values.values.map((d: { value: number; isEmptyValue: boolean }) =>
|
||||
d.isEmptyValue ? NaN : aggregation(Number(d.value), c),
|
||||
);
|
||||
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
|
||||
if (labels[indexNum] && indexNum > -1) {
|
||||
@@ -205,28 +165,23 @@ export function useSourceProcessor(
|
||||
}
|
||||
}
|
||||
if (type === MetricQueryTypes.ReadMetricsValue) {
|
||||
source[m] = aggregation(Number(Object.values(resp.data)[0]), c);
|
||||
const v = Object.values(resp.data)[0] || {};
|
||||
source[m] = v.isEmptyValue ? NaN : aggregation(Number(v.value), c);
|
||||
}
|
||||
if (
|
||||
(
|
||||
[
|
||||
MetricQueryTypes.ReadRecords,
|
||||
MetricQueryTypes.ReadSampledRecords,
|
||||
MetricQueryTypes.SortMetrics,
|
||||
] as string[]
|
||||
[MetricQueryTypes.ReadRecords, MetricQueryTypes.ReadSampledRecords, MetricQueryTypes.SortMetrics] as string[]
|
||||
).includes(type)
|
||||
) {
|
||||
source[m] = (Object.values(resp.data)[0] || []).map(
|
||||
(d: { value: unknown; name: string }) => {
|
||||
d.value = aggregation(Number(d.value), c);
|
||||
source[m] = (Object.values(resp.data)[0] || []).map((d: { value: unknown; name: string }) => {
|
||||
d.value = aggregation(Number(d.value), c);
|
||||
|
||||
return d;
|
||||
}
|
||||
);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
if (type === MetricQueryTypes.READHEATMAP) {
|
||||
const resVal = Object.values(resp.data)[0] || {};
|
||||
const nodes = [] as any;
|
||||
const nodes = [] as Indexable[];
|
||||
if (!(resVal && resVal.values)) {
|
||||
source[m] = { nodes: [] };
|
||||
return;
|
||||
@@ -236,14 +191,9 @@ export function useSourceProcessor(
|
||||
|
||||
nodes.push(...grids);
|
||||
});
|
||||
let buckets = [] as any;
|
||||
let buckets = [] as Indexable[];
|
||||
if (resVal.buckets.length) {
|
||||
buckets = [
|
||||
resVal.buckets[0].min,
|
||||
...resVal.buckets.map(
|
||||
(item: { min: string; max: string }) => item.max
|
||||
),
|
||||
];
|
||||
buckets = [resVal.buckets[0].min, ...resVal.buckets.map((item: { min: string; max: string }) => item.max)];
|
||||
}
|
||||
|
||||
source[m] = { nodes, buckets }; // nodes: number[][]
|
||||
@@ -254,13 +204,13 @@ export function useSourceProcessor(
|
||||
}
|
||||
|
||||
export function useQueryPodsMetrics(
|
||||
pods: Array<Instance | Endpoint | Service | any>,
|
||||
pods: Array<(Instance | Endpoint | Service) & Indexable>,
|
||||
config: {
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
metricConfig: MetricConfigOpt[];
|
||||
},
|
||||
scope: string
|
||||
scope: string,
|
||||
) {
|
||||
const metricTypes = (config.metricTypes || []).filter((m: string) => m);
|
||||
if (!metricTypes.length) {
|
||||
@@ -277,40 +227,34 @@ export function useQueryPodsMetrics(
|
||||
};
|
||||
const variables: string[] = [`$duration: Duration!`];
|
||||
const currentService = selectorStore.currentService || {};
|
||||
const fragmentList = pods.map(
|
||||
(
|
||||
d: (Instance | Endpoint | Service) & { normal: boolean },
|
||||
index: number
|
||||
) => {
|
||||
const param = {
|
||||
scope,
|
||||
serviceName: scope === "Service" ? d.label : currentService.label,
|
||||
serviceInstanceName: scope === "ServiceInstance" ? d.label : undefined,
|
||||
endpointName: scope === "Endpoint" ? d.label : undefined,
|
||||
normal: scope === "Service" ? d.normal : currentService.normal,
|
||||
const fragmentList = pods.map((d: (Instance | Endpoint | Service) & Indexable, index: number) => {
|
||||
const param = {
|
||||
serviceName: scope === "Service" ? d.label : currentService.label,
|
||||
serviceInstanceName: scope === "ServiceInstance" ? d.label : undefined,
|
||||
endpointName: scope === "Endpoint" ? d.label : undefined,
|
||||
normal: scope === "Service" ? d.normal : currentService.normal,
|
||||
};
|
||||
const f = metrics.map((name: string, idx: number) => {
|
||||
const metricType = metricTypes[idx] || "";
|
||||
variables.push(`$condition${index}${idx}: MetricsCondition!`);
|
||||
conditions[`condition${index}${idx}`] = {
|
||||
name,
|
||||
entity: param,
|
||||
};
|
||||
const f = metrics.map((name: string, idx: number) => {
|
||||
const metricType = metricTypes[idx] || "";
|
||||
variables.push(`$condition${index}${idx}: MetricsCondition!`);
|
||||
conditions[`condition${index}${idx}`] = {
|
||||
name,
|
||||
entity: param,
|
||||
};
|
||||
let labelStr = "";
|
||||
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;
|
||||
}
|
||||
);
|
||||
let labelStr = "";
|
||||
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;
|
||||
}
|
||||
const t =
|
||||
metricType === MetricQueryTypes.ReadMetricsValue ? MetricQueryTypes.ReadNullableMetricsValue : metricType;
|
||||
return `${name}${index}${idx}: ${t}(condition: $condition${index}${idx}, ${labelStr}duration: $duration)${RespFields[t]}`;
|
||||
});
|
||||
return f;
|
||||
});
|
||||
const fragment = fragmentList.flat(1).join(" ");
|
||||
const queryStr = `query queryData(${variables}) {${fragment}}`;
|
||||
|
||||
@@ -319,13 +263,13 @@ export function useQueryPodsMetrics(
|
||||
|
||||
export function usePodsSource(
|
||||
pods: Array<Instance | Endpoint>,
|
||||
resp: { errors: string; data: { [key: string]: any } },
|
||||
resp: { errors: string; data: Indexable },
|
||||
config: {
|
||||
metrics: string[];
|
||||
metricTypes: string[];
|
||||
metricConfig: MetricConfigOpt[];
|
||||
}
|
||||
): any {
|
||||
},
|
||||
): Indexable {
|
||||
if (resp.errors) {
|
||||
ElMessage.error(resp.errors);
|
||||
return {};
|
||||
@@ -333,12 +277,13 @@ export function usePodsSource(
|
||||
const names: string[] = [];
|
||||
const metricConfigArr: MetricConfigOpt[] = [];
|
||||
const metricTypesArr: string[] = [];
|
||||
const data = pods.map((d: Instance | any, idx: number) => {
|
||||
const data = pods.map((d: any, idx: number) => {
|
||||
config.metrics.map((name: string, index: number) => {
|
||||
const c: any = (config.metricConfig && config.metricConfig[index]) || {};
|
||||
const key = name + idx + index;
|
||||
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
|
||||
d[name] = aggregation(resp.data[key], c);
|
||||
const v = resp.data[key];
|
||||
d[name] = v.isEmptyValue ? NaN : aggregation(v.value, c);
|
||||
if (idx === 0) {
|
||||
names.push(name);
|
||||
metricConfigArr.push(c);
|
||||
@@ -347,17 +292,11 @@ export function usePodsSource(
|
||||
}
|
||||
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
|
||||
d[name] = {};
|
||||
if (
|
||||
[
|
||||
Calculations.Average,
|
||||
Calculations.ApdexAvg,
|
||||
Calculations.PercentageAvg,
|
||||
].includes(c.calculation)
|
||||
) {
|
||||
if ([Calculations.Average, Calculations.ApdexAvg, Calculations.PercentageAvg].includes(c.calculation)) {
|
||||
d[name]["avg"] = calculateExp(resp.data[key].values.values, c);
|
||||
}
|
||||
d[name]["values"] = resp.data[key].values.values.map(
|
||||
(val: { value: number }) => aggregation(val.value, c)
|
||||
d[name]["values"] = resp.data[key].values.values.map((val: { value: number; isEmptyValue: boolean }) =>
|
||||
val.isEmptyValue ? NaN : aggregation(val.value, c),
|
||||
);
|
||||
if (idx === 0) {
|
||||
names.push(name);
|
||||
@@ -365,20 +304,14 @@ export function usePodsSource(
|
||||
metricTypesArr.push(config.metricTypes[index]);
|
||||
}
|
||||
}
|
||||
if (
|
||||
config.metricTypes[index] === MetricQueryTypes.ReadLabeledMetricsValues
|
||||
) {
|
||||
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, ""));
|
||||
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 values = item.values.values.map((d: { value: number; isEmptyValue: boolean }) =>
|
||||
d.isEmptyValue ? NaN : aggregation(Number(d.value), c),
|
||||
);
|
||||
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
|
||||
let key = item.label;
|
||||
@@ -388,13 +321,7 @@ export function usePodsSource(
|
||||
if (!d[key]) {
|
||||
d[key] = {};
|
||||
}
|
||||
if (
|
||||
[
|
||||
Calculations.Average,
|
||||
Calculations.ApdexAvg,
|
||||
Calculations.PercentageAvg,
|
||||
].includes(c.calculation)
|
||||
) {
|
||||
if ([Calculations.Average, Calculations.ApdexAvg, Calculations.PercentageAvg].includes(c.calculation)) {
|
||||
d[key]["avg"] = calculateExp(item.values.values, c);
|
||||
}
|
||||
d[key]["values"] = values;
|
||||
@@ -435,13 +362,12 @@ export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
|
||||
|
||||
return { queryStr, conditions };
|
||||
}
|
||||
function calculateExp(
|
||||
arr: { value: number }[],
|
||||
config: { calculation?: string }
|
||||
export function calculateExp(
|
||||
list: { value: number; isEmptyValue: boolean }[],
|
||||
config: { calculation?: string },
|
||||
): (number | string)[] {
|
||||
const sum = arr
|
||||
.map((d: { value: number }) => d.value)
|
||||
.reduce((a, b) => a + b);
|
||||
const arr = list.filter((d: { value: number; isEmptyValue: boolean }) => !d.isEmptyValue);
|
||||
const sum = arr.length ? arr.map((d: { value: number }) => Number(d.value)).reduce((a, b) => a + b) : 0;
|
||||
let data: (number | string)[] = [];
|
||||
switch (config.calculation) {
|
||||
case Calculations.Average:
|
||||
@@ -454,16 +380,15 @@ function calculateExp(
|
||||
data = [(sum / arr.length / 10000).toFixed(2)];
|
||||
break;
|
||||
default:
|
||||
data = arr.map((d) => aggregation(d.value, config));
|
||||
data = list.map((d: { value: number; isEmptyValue: boolean }) =>
|
||||
d.isEmptyValue ? NaN : aggregation(d.value, config),
|
||||
);
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export function aggregation(
|
||||
val: number,
|
||||
config: { calculation?: string }
|
||||
): number | string {
|
||||
export function aggregation(val: number, config: { calculation?: string }): number | string {
|
||||
let data: number | string = Number(val);
|
||||
|
||||
switch (config.calculation) {
|
||||
@@ -491,9 +416,6 @@ export function aggregation(
|
||||
case Calculations.ConvertMilliseconds:
|
||||
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
|
||||
break;
|
||||
case Calculations.Precision:
|
||||
data = data.toFixed(2);
|
||||
break;
|
||||
case Calculations.MsToS:
|
||||
data = (val / 1000).toFixed(2);
|
||||
break;
|
||||
@@ -503,6 +425,9 @@ export function aggregation(
|
||||
case Calculations.NanosecondToMillisecond:
|
||||
data = (val / 1000 / 1000).toFixed(2);
|
||||
break;
|
||||
case Calculations.ApdexAvg:
|
||||
data = (val / 10000).toFixed(2);
|
||||
break;
|
||||
default:
|
||||
data;
|
||||
break;
|
||||
@@ -510,28 +435,3 @@ export function aggregation(
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function useGetMetricEntity(metric: string, metricType: any) {
|
||||
if (!metric || !metricType) {
|
||||
return;
|
||||
}
|
||||
let catalog = "";
|
||||
const dashboardStore = useDashboardStore();
|
||||
if (
|
||||
[
|
||||
MetricQueryTypes.ReadSampledRecords,
|
||||
MetricQueryTypes.SortMetrics,
|
||||
MetricQueryTypes.ReadRecords,
|
||||
].includes(metricType)
|
||||
) {
|
||||
const res = await dashboardStore.fetchMetricList(metric);
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
return;
|
||||
}
|
||||
const c: string = res.data.metrics[0].catalog;
|
||||
catalog = (MetricCatalog as any)[c];
|
||||
}
|
||||
|
||||
return catalog;
|
||||
}
|
||||
|
@@ -18,11 +18,7 @@ import { ref, watch } from "vue";
|
||||
import { tryOnUnmounted } from "@vueuse/core";
|
||||
import { isFunction } from "@/utils/is";
|
||||
|
||||
export function useTimeoutFn(
|
||||
handle: Fn<any>,
|
||||
wait: number,
|
||||
native = false
|
||||
): any {
|
||||
export function useTimeoutFn(handle: Fn<any>, wait: number, native = false): any {
|
||||
if (!isFunction(handle)) {
|
||||
throw new Error("handle is not Function!");
|
||||
}
|
||||
@@ -36,13 +32,13 @@ export function useTimeoutFn(
|
||||
(maturity) => {
|
||||
maturity && handle();
|
||||
},
|
||||
{ immediate: false }
|
||||
{ immediate: false },
|
||||
);
|
||||
}
|
||||
return { readyRef, stop, start };
|
||||
}
|
||||
|
||||
export function useTimeoutRef(wait: number): any {
|
||||
export function useTimeoutRef(wait: number) {
|
||||
const readyRef = ref(false);
|
||||
|
||||
let timer: TimeoutHandle;
|
||||
|
@@ -22,15 +22,15 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { AppMain, SideBar, NavBar } from "./components";
|
||||
import { AppMain, SideBar, NavBar } from "./components";
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.app-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
.app-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
flex-grow: 2;
|
||||
height: 100%;
|
||||
}
|
||||
.main-container {
|
||||
flex-grow: 2;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@@ -22,8 +22,9 @@ limitations under the License. -->
|
||||
</section>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.app-main {
|
||||
height: calc(100% - 40px);
|
||||
background: #f7f9fa;
|
||||
}
|
||||
.app-main {
|
||||
height: calc(100% - 40px);
|
||||
background: #f7f9fa;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="nav-bar flex-h">
|
||||
<div class="title">{{ appStore.pageTitle || t(pageName) }}</div>
|
||||
<div class="title">{{ route.name === "ViewWidget" ? "" : appStore.pageTitle || t(pageName) }}</div>
|
||||
<div class="app-config">
|
||||
<span class="red" v-show="timeRange">{{ t("timeTips") }}</span>
|
||||
<TimePicker
|
||||
@@ -23,21 +23,12 @@ limitations under the License. -->
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeTimeRange"
|
||||
/>
|
||||
<span>
|
||||
UTC{{ appStore.utcHour >= 0 ? "+" : ""
|
||||
}}{{ `${appStore.utcHour}:${appStore.utcMin}` }}
|
||||
</span>
|
||||
<span> UTC{{ appStore.utcHour >= 0 ? "+" : "" }}{{ `${appStore.utcHour}:${appStore.utcMin}` }} </span>
|
||||
<span title="refresh" class="ghost ml-5 cp" @click="handleReload">
|
||||
<Icon iconName="retry" :loading="appStore.autoRefresh" class="middle" />
|
||||
</span>
|
||||
<span class="version ml-5 cp">
|
||||
<el-popover
|
||||
trigger="hover"
|
||||
width="250"
|
||||
placement="bottom"
|
||||
effect="light"
|
||||
:content="appStore.version"
|
||||
>
|
||||
<el-popover trigger="hover" width="250" placement="bottom" effect="light" :content="appStore.version">
|
||||
<template #reference>
|
||||
<span>
|
||||
<Icon iconName="info_outline" size="middle" />
|
||||
@@ -49,92 +40,91 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const route = useRoute();
|
||||
const pageName = ref<string>("");
|
||||
const timeRange = ref<number>(0);
|
||||
/*global Indexable */
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const route = useRoute();
|
||||
const pageName = ref<string>("");
|
||||
const timeRange = ref<number>(0);
|
||||
|
||||
resetDuration();
|
||||
getVersion();
|
||||
const setConfig = (value: string) => {
|
||||
pageName.value = value || "";
|
||||
};
|
||||
resetDuration();
|
||||
getVersion();
|
||||
const setConfig = (value: string) => {
|
||||
pageName.value = value || "";
|
||||
};
|
||||
|
||||
function handleReload() {
|
||||
const gap =
|
||||
appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||
const dates: Date[] = [new Date(new Date().getTime() - gap), new Date()];
|
||||
appStore.setDuration(timeFormat(dates));
|
||||
}
|
||||
|
||||
function changeTimeRange(val: Date[] | any) {
|
||||
timeRange.value =
|
||||
val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
|
||||
if (timeRange.value) {
|
||||
return;
|
||||
function handleReload() {
|
||||
const gap = appStore.duration.end.getTime() - appStore.duration.start.getTime();
|
||||
const dates: Date[] = [new Date(new Date().getTime() - gap), new Date()];
|
||||
appStore.setDuration(timeFormat(dates));
|
||||
}
|
||||
appStore.setDuration(timeFormat(val));
|
||||
}
|
||||
setConfig(String(route.meta.title));
|
||||
watch(
|
||||
() => route.meta.title,
|
||||
(title: unknown) => {
|
||||
setConfig(String(title));
|
||||
}
|
||||
);
|
||||
async function getVersion() {
|
||||
const res = await appStore.fetchVersion();
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
}
|
||||
}
|
||||
function resetDuration() {
|
||||
const { duration }: any = route.params;
|
||||
if (duration) {
|
||||
const d = JSON.parse(duration);
|
||||
|
||||
appStore.updateDurationRow({
|
||||
start: new Date(d.start),
|
||||
end: new Date(d.end),
|
||||
step: d.step,
|
||||
});
|
||||
appStore.updateUTC(d.utc);
|
||||
function changeTimeRange(val: Date[]) {
|
||||
timeRange.value = val[1].getTime() - val[0].getTime() > 60 * 24 * 60 * 60 * 1000 ? 1 : 0;
|
||||
if (timeRange.value) {
|
||||
return;
|
||||
}
|
||||
appStore.setDuration(timeFormat(val));
|
||||
}
|
||||
setConfig(String(route.meta.title));
|
||||
watch(
|
||||
() => route.meta.title,
|
||||
(title: unknown) => {
|
||||
setConfig(String(title));
|
||||
},
|
||||
);
|
||||
async function getVersion() {
|
||||
const res = await appStore.fetchVersion();
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
}
|
||||
}
|
||||
function resetDuration() {
|
||||
const { duration }: Indexable = route.params;
|
||||
if (duration) {
|
||||
const d = JSON.parse(duration);
|
||||
|
||||
appStore.updateDurationRow({
|
||||
start: new Date(d.start),
|
||||
end: new Date(d.end),
|
||||
step: d.step,
|
||||
});
|
||||
appStore.updateUTC(d.utc);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.nav-bar {
|
||||
padding: 5px 10px 5px 28px;
|
||||
text-align: left;
|
||||
justify-content: space-between;
|
||||
background-color: #fafbfc;
|
||||
border-bottom: 1px solid #dfe4e8;
|
||||
color: #222;
|
||||
font-size: 12px;
|
||||
}
|
||||
.nav-bar {
|
||||
padding: 5px 10px;
|
||||
text-align: left;
|
||||
justify-content: space-between;
|
||||
background-color: #fafbfc;
|
||||
border-bottom: 1px solid #dfe4e8;
|
||||
color: #222;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nav-bar.dark {
|
||||
background-color: #333840;
|
||||
border-bottom: 1px solid #252a2f;
|
||||
color: #fafbfc;
|
||||
}
|
||||
.nav-bar.dark {
|
||||
background-color: #333840;
|
||||
border-bottom: 1px solid #252a2f;
|
||||
color: #fafbfc;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
padding: 10px;
|
||||
}
|
||||
.nav-tabs {
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -13,190 +13,180 @@ 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="side-bar">
|
||||
<div class="side-bar" v-if="showMenu" @click="isCollapse = false" @mouseleave="closeMenu">
|
||||
<div :class="isCollapse ? 'logo-icon-collapse' : 'logo-icon'">
|
||||
<Icon
|
||||
:size="isCollapse ? 'xl' : 'logo'"
|
||||
:iconName="isCollapse ? 'logo' : 'logo-sw'"
|
||||
/>
|
||||
<Icon :size="isCollapse ? 'xl' : 'logo'" :iconName="isCollapse ? 'logo' : 'logo-sw'" />
|
||||
</div>
|
||||
<el-menu
|
||||
active-text-color="#448dfe"
|
||||
background-color="#252a2f"
|
||||
class="el-menu-vertical"
|
||||
:default-active="name"
|
||||
text-color="#efefef"
|
||||
:unique-opened="true"
|
||||
:collapse="isCollapse"
|
||||
:style="{ border: 'none' }"
|
||||
>
|
||||
<template v-for="(menu, index) in routes" :key="index">
|
||||
<el-sub-menu :index="String(menu.name)" v-if="menu.meta.hasGroup">
|
||||
<template #title>
|
||||
<router-link class="items" :to="menu.path">
|
||||
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
|
||||
<Icon size="lg" :iconName="menu.meta.icon" />
|
||||
</el-icon>
|
||||
<span class="title" :class="isCollapse ? 'collapse' : ''">
|
||||
{{ t(menu.meta.title) }}
|
||||
</span>
|
||||
</router-link>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<el-menu-item
|
||||
v-for="(m, idx) in filterMenus(menu.children)"
|
||||
:index="m.name"
|
||||
:key="idx"
|
||||
>
|
||||
<router-link class="items" :to="m.path">
|
||||
<span class="title">{{ m.meta && t(m.meta.title) }}</span>
|
||||
<div class="menu scroll_bar_dark" :style="isCollapse ? {} : { width: '220px' }">
|
||||
<el-menu
|
||||
active-text-color="#448dfe"
|
||||
background-color="#252a2f"
|
||||
class="el-menu-vertical"
|
||||
:default-active="name"
|
||||
text-color="#efefef"
|
||||
:collapse="isCollapse"
|
||||
:collapse-transition="false"
|
||||
:style="{ border: 'none' }"
|
||||
>
|
||||
<template v-for="(menu, index) in routes" :key="index">
|
||||
<el-sub-menu :index="String(menu.name)" v-if="menu.meta.hasGroup" popper-class="sub-list">
|
||||
<template #title>
|
||||
<router-link class="items" :to="menu.path">
|
||||
<el-icon class="menu-icons" :style="{ marginRight: '12px' }" @mouseover="setCollapse">
|
||||
<Icon size="lg" :iconName="menu.meta.icon" />
|
||||
</el-icon>
|
||||
<span class="title" :class="isCollapse ? 'collapse' : ''">
|
||||
{{ t(menu.meta.title) }}
|
||||
</span>
|
||||
</router-link>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</el-sub-menu>
|
||||
<el-menu-item
|
||||
:index="String(menu.name)"
|
||||
@click="changePage(menu)"
|
||||
v-else
|
||||
>
|
||||
<el-icon class="menu-icons" :style="{ marginRight: '12px' }">
|
||||
<router-link class="items" :to="menu.children[0].path">
|
||||
<Icon size="lg" :iconName="menu.meta.icon" />
|
||||
</router-link>
|
||||
</el-icon>
|
||||
<template #title>
|
||||
<router-link class="items" :to="menu.children[0].path">
|
||||
<span class="title">{{ t(menu.meta.title) }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-menu>
|
||||
<div
|
||||
class="menu-control"
|
||||
:class="isCollapse ? 'collapse' : ''"
|
||||
:style="{
|
||||
color: theme === 'light' ? '#eee' : '#252a2f',
|
||||
}"
|
||||
>
|
||||
<Icon
|
||||
size="middle"
|
||||
iconName="format_indent_decrease"
|
||||
@click="controlMenu"
|
||||
/>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<el-menu-item v-for="(m, idx) in filterMenus(menu.children)" :index="m.name" :key="idx">
|
||||
<router-link class="items" :to="m.path">
|
||||
<span class="title">{{ m.meta && t(m.meta.title) }}</span>
|
||||
</router-link>
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</el-sub-menu>
|
||||
<el-menu-item :index="String(menu.name)" @click="changePage(menu)" v-else>
|
||||
<el-icon class="menu-icons" :style="{ marginRight: '12px' }" @mouseover="setCollapse">
|
||||
<router-link class="items menu-title" :to="menu.children[0].path">
|
||||
<Icon size="lg" :iconName="menu.meta.icon" />
|
||||
</router-link>
|
||||
</el-icon>
|
||||
<template #title>
|
||||
<router-link class="items menu-title" :to="menu.children[0].path">
|
||||
<span class="title">{{ t(menu.meta.title) }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useRouter, RouteRecordRaw } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Icon from "@/components/Icon.vue";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { ref } from "vue";
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Icon from "@/components/Icon.vue";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
|
||||
const appStore = useAppStoreWithOut();
|
||||
const { t } = useI18n();
|
||||
const name = ref<string>(String(useRouter().currentRoute.value.name));
|
||||
const theme = ["VirtualMachine", "Kubernetes"].includes(name.value || "")
|
||||
? ref("light")
|
||||
: ref("black");
|
||||
const routes = ref<RouteRecordRaw[] | any>(useRouter().options.routes);
|
||||
if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
|
||||
appStore.setIsMobile(true);
|
||||
} else {
|
||||
appStore.setIsMobile(false);
|
||||
}
|
||||
const isCollapse = ref(false);
|
||||
const controlMenu = () => {
|
||||
isCollapse.value = !isCollapse.value;
|
||||
};
|
||||
const changePage = (menu: RouteRecordRaw) => {
|
||||
theme.value = ["VirtualMachine", "Kubernetes"].includes(String(menu.name))
|
||||
? "light"
|
||||
: "black";
|
||||
};
|
||||
const filterMenus = (menus: any[]) => {
|
||||
return menus.filter((d) => d.meta && !d.meta.notShow);
|
||||
};
|
||||
/*global Recordable*/
|
||||
const appStore = useAppStoreWithOut();
|
||||
const { t } = useI18n();
|
||||
const name = ref<string>(String(useRouter().currentRoute.value.name));
|
||||
const theme = ["VirtualMachine", "Kubernetes"].includes(name.value || "") ? ref("light") : ref("black");
|
||||
const routes = ref<RouteRecordRaw[] | any>(useRouter().options.routes);
|
||||
const route = useRoute();
|
||||
const isCollapse = ref(true);
|
||||
const showMenu = ref(true);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
if (/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
|
||||
appStore.setIsMobile(true);
|
||||
} else {
|
||||
appStore.setIsMobile(false);
|
||||
}
|
||||
if (route.name === "ViewWidget") {
|
||||
showMenu.value = false;
|
||||
}
|
||||
const changePage = (menu: RouteRecordRaw) => {
|
||||
theme.value = ["VirtualMachine", "Kubernetes"].includes(String(menu.name)) ? "light" : "black";
|
||||
};
|
||||
const filterMenus = (menus: Recordable[]) => {
|
||||
return menus.filter((d) => d.meta && !d.meta.notShow);
|
||||
};
|
||||
function setCollapse() {
|
||||
open.value = true;
|
||||
setTimeout(() => {
|
||||
if (open.value) {
|
||||
isCollapse.value = false;
|
||||
}
|
||||
open.value = false;
|
||||
}, 1000);
|
||||
}
|
||||
function closeMenu() {
|
||||
isCollapse.value = true;
|
||||
open.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.side-bar {
|
||||
background: #252a2f;
|
||||
height: 100%;
|
||||
margin-bottom: 100px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.side-bar {
|
||||
background: #252a2f;
|
||||
height: 100%;
|
||||
margin-bottom: 180px;
|
||||
}
|
||||
|
||||
.el-menu-vertical:not(.el-menu--collapse) {
|
||||
width: 220px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.menu {
|
||||
height: calc(100% - 30px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.logo-icon-collapse {
|
||||
width: 65px;
|
||||
margin: 15px 0 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.menu:hover {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
span.collapse {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
.el-menu-vertical:not(.el-menu--collapse) {
|
||||
width: 220px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
margin: 15px 0 10px 20px;
|
||||
width: 110px;
|
||||
}
|
||||
.logo-icon-collapse {
|
||||
width: 65px;
|
||||
margin: 5px 0 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu-control {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: 220px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s linear;
|
||||
z-index: 99;
|
||||
color: #252a2f;
|
||||
}
|
||||
span.collapse {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.menu-control.collapse {
|
||||
left: 70px;
|
||||
}
|
||||
.logo-icon {
|
||||
margin: 15px 0 10px 20px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.el-icon.el-sub-menu__icon-arrow {
|
||||
height: 12px;
|
||||
}
|
||||
.menu-control.collapse {
|
||||
left: 70px;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
.el-icon.el-sub-menu__icon-arrow {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.version {
|
||||
color: #eee;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
padding-left: 23px;
|
||||
margin-bottom: 10px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 10px;
|
||||
}
|
||||
.items {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
.version {
|
||||
color: #eee;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
padding-left: 23px;
|
||||
margin-bottom: 10px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
max-width: 110px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.empty {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
max-width: 200px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
@@ -32,6 +32,7 @@ if (!savedLanguage) {
|
||||
}
|
||||
language = savedLanguage ? savedLanguage : language;
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: language,
|
||||
messages,
|
||||
});
|
||||
|
@@ -32,7 +32,7 @@ const msg = {
|
||||
dashboards: "Dashboards",
|
||||
profiles: "Profiles",
|
||||
database: "Database",
|
||||
mySQL: "MySQL",
|
||||
mySQL: "MySQL/MariaDB",
|
||||
serviceName: "Service Name",
|
||||
technologies: "Technologies",
|
||||
generalServicePanel: "General Service Panel",
|
||||
@@ -176,11 +176,16 @@ const msg = {
|
||||
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",
|
||||
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",
|
||||
iframeWidgetTip: "Add a link to a widget",
|
||||
iframeSrc: "Iframe Link",
|
||||
generateLink: "Generate Link",
|
||||
setDuration: "Lock Query Duration",
|
||||
openFunction: "OpenFunction",
|
||||
period: "Period",
|
||||
windows: "Windows",
|
||||
seconds: "Seconds",
|
||||
hourTip: "Select Hour",
|
||||
minuteTip: "Select Minute",
|
||||
@@ -199,7 +204,7 @@ const msg = {
|
||||
topology: "Topology",
|
||||
trace: "Trace",
|
||||
alarm: "Alerting",
|
||||
auto: "Auto",
|
||||
auto: "Auto Fresh",
|
||||
reload: "Reload",
|
||||
version: "Version",
|
||||
copy: "Copy",
|
||||
@@ -315,10 +320,10 @@ const msg = {
|
||||
viewLogs: "View Logs",
|
||||
logsTagsTip: `Only tags defined in the core/default/searchableLogsTags are searchable.
|
||||
Check more details on the Configuration Vocabulary page`,
|
||||
keywordsOfContentLogTips:
|
||||
"Current storage of SkyWalking OAP server does not support this.",
|
||||
keywordsOfContentLogTips: "Current storage of SkyWalking OAP server does not support this.",
|
||||
setEvent: "Set Event",
|
||||
viewAttributes: "View",
|
||||
attributes: "Attributes",
|
||||
serviceEvents: "Service Events",
|
||||
select: "Select",
|
||||
eventID: "Event ID",
|
||||
@@ -370,10 +375,35 @@ const msg = {
|
||||
addKeywordsOfContent: "Please input a keyword of content",
|
||||
addExcludingKeywordsOfContent: "Please input a keyword of excluding content",
|
||||
noticeTag: "Please press Enter after inputting a tag(key=value).",
|
||||
conditionNotice:
|
||||
"Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
|
||||
conditionNotice: "Notice: Please press Enter after inputting a key of content, exclude key of content(key=value).",
|
||||
language: "Language",
|
||||
gateway: "Gateway",
|
||||
virtualMQ: "Virtual MQ",
|
||||
AWSCloud: "AWS Cloud",
|
||||
AWSCloudEKS: "EKS",
|
||||
AWSCloudS3: "S3",
|
||||
AWSCloudDynamoDB: "DynamoDB",
|
||||
AWSGateway: "AWS API Gateway",
|
||||
APIGateway: "API Gateway",
|
||||
redis: "Redis",
|
||||
elasticsearch: "Elasticsearch",
|
||||
mq: "MQ",
|
||||
rabbitMQ: "RabbitMQ",
|
||||
save: "Save",
|
||||
editStrategy: "Edit Policies",
|
||||
policyList: "Policy List",
|
||||
targetTypes: "Target Type",
|
||||
monitorType: "Monitor Type",
|
||||
count: "Count",
|
||||
threshold: "Threshold",
|
||||
uriRegex: "URI Regex",
|
||||
uriList: "URI List",
|
||||
processes: "Processes",
|
||||
monitorInstances: "Monitor Instances",
|
||||
processDashboards: "Process Dashboards",
|
||||
instanceDashboards: "Instance Dashboards",
|
||||
detailLabel: "Detail Label",
|
||||
summary: "Summary",
|
||||
detail: "Detail",
|
||||
};
|
||||
export default msg;
|
||||
|
@@ -32,7 +32,7 @@ const msg = {
|
||||
dashboards: "Paneles",
|
||||
profiles: "Perfiles",
|
||||
database: "Base de Datos",
|
||||
mySQL: "MySQL",
|
||||
mySQL: "MySQL/MariaDB",
|
||||
serviceName: "Nombre Servicio",
|
||||
technologies: "Tecnologías",
|
||||
generalServicePanel: "Panel Servicio General",
|
||||
@@ -77,10 +77,8 @@ const msg = {
|
||||
editGraph: "Editar Opciones",
|
||||
dashboardName: "Selecciona Nombre del Panel",
|
||||
linkDashboard: "Nombre del panel relacionado con llamadas de la topología",
|
||||
linkServerMetrics:
|
||||
"Métricas de servidor relacionadas con llamadas de la topología",
|
||||
linkClientMetrics:
|
||||
"Métricas de cliente relacionadas con llamadas de la topología",
|
||||
linkServerMetrics: "Métricas de servidor relacionadas con llamadas de la topología",
|
||||
linkClientMetrics: "Métricas de cliente relacionadas con llamadas de la topología",
|
||||
nodeDashboard: "Nombre del panel relacionado con nodos de la topología",
|
||||
nodeMetrics: "Mêtricas relacionas con nodos de la topología",
|
||||
instanceDashboard: "Nombre del panel relacionado con instancias de servicio",
|
||||
@@ -164,6 +162,11 @@ const msg = {
|
||||
latency: "Retraso",
|
||||
metricValues: "Valor métrico",
|
||||
legendValues: "Valor de la leyenda",
|
||||
iframeWidgetTip: "Añadir enlaces a los gadgets",
|
||||
iframeSrc: "Enlace Iframe",
|
||||
generateLink: "Generar enlaces",
|
||||
setDuration: "Duración de la consulta de bloqueo",
|
||||
openFunction: "OpenFunction",
|
||||
seconds: "Segundos",
|
||||
hourTip: "Seleccione Hora",
|
||||
minuteTip: "Seleccione Minuto",
|
||||
@@ -180,11 +183,11 @@ const msg = {
|
||||
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",
|
||||
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",
|
||||
period: "Period",
|
||||
windows: "Windows",
|
||||
second: "s",
|
||||
yearSuffix: "Año",
|
||||
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
|
||||
@@ -199,7 +202,7 @@ const msg = {
|
||||
topology: "Topología",
|
||||
trace: "Traza",
|
||||
alarm: "Recordatorio en curso",
|
||||
auto: "Auto",
|
||||
auto: "Auto Fresh",
|
||||
reload: "Recargar",
|
||||
version: "Versión",
|
||||
copy: "Copiar",
|
||||
@@ -315,8 +318,7 @@ const msg = {
|
||||
viewLogs: "Ver Registro de Datos",
|
||||
logsTagsTip: `Solamente etiquetas definidas en core/default/searchableLogsTags pueden ser buscadas.
|
||||
Más información en la página de Vocabulario de Configuración`,
|
||||
keywordsOfContentLogTips:
|
||||
"El almacenamiento actual del servidor SkyWalking OAP no lo soporta.",
|
||||
keywordsOfContentLogTips: "El almacenamiento actual del servidor SkyWalking OAP no lo soporta.",
|
||||
setEvent: "Establecer Evento",
|
||||
viewAttributes: "Ver",
|
||||
serviceEvents: "Eventos Servico",
|
||||
@@ -347,8 +349,7 @@ const msg = {
|
||||
destEndpoint: "Endpoint Destinación",
|
||||
eventSource: "Fuente Envento",
|
||||
modalTitle: "Inspección",
|
||||
selectRedirectPage:
|
||||
"Quiere inspeccionar las Trazas or Registros de datos del servicio %s?",
|
||||
selectRedirectPage: "Quiere inspeccionar las Trazas or Registros de datos del servicio %s?",
|
||||
logAnalysis: "Lenguaje de Análisis de Registro de Datos",
|
||||
logDataBody: "Contenido del Registro de Datos",
|
||||
addType: "Por favor introduzca un tipo",
|
||||
@@ -369,14 +370,39 @@ const msg = {
|
||||
addTraceID: "Por favor introduzca el ID de la traza",
|
||||
addTags: "Por favor introduzaca una etiqueta",
|
||||
addKeywordsOfContent: "Por favor introduzca una clave de contenido",
|
||||
addExcludingKeywordsOfContent:
|
||||
"Por favor introduzca una clave excluyente de contenido",
|
||||
noticeTag:
|
||||
"Por favor presione Intro después de introducir una etiqueta(clave=valor).",
|
||||
addExcludingKeywordsOfContent: "Por favor introduzca una clave excluyente de contenido",
|
||||
noticeTag: "Por favor presione Intro después de introducir una etiqueta(clave=valor).",
|
||||
conditionNotice:
|
||||
"Aviso: Por favor presione Intro después de introducir una clave de contenido, excluir clave de contenido(clave=valor).",
|
||||
language: "Lenguaje",
|
||||
gateway: "Puerta",
|
||||
virtualMQ: "MQ virtual",
|
||||
AWSCloud: "AWS Cloud",
|
||||
AWSCloudEKS: "EKS",
|
||||
AWSCloudS3: "S3",
|
||||
AWSCloudDynamoDB: "DynamoDB",
|
||||
AWSGateway: "AWS API Gateway",
|
||||
APIGateway: "API Gateway",
|
||||
redis: "Redis",
|
||||
elasticsearch: "Elasticsearch",
|
||||
mq: "MQ",
|
||||
rabbitMQ: "RabbitMQ",
|
||||
save: "Salvar",
|
||||
editStrategy: "Estrategia editorial",
|
||||
policyList: "Lista de políticas",
|
||||
targetTypes: "Tipo de objetivo",
|
||||
monitorType: "Tipo de Monitor",
|
||||
count: "Contar",
|
||||
threshold: "Umbral",
|
||||
uriRegex: "Lista URI",
|
||||
uriList: "Lista URI",
|
||||
processes: "Proceso",
|
||||
attributes: "Atributos",
|
||||
monitorInstances: "Ejemplo de Monitor",
|
||||
processDashboards: "Tablero de proceso",
|
||||
instanceDashboards: "Tablero de ejemplo",
|
||||
detailLabel: "Detail Label",
|
||||
summary: "Summary",
|
||||
detail: "Detail",
|
||||
};
|
||||
export default msg;
|
||||
|
@@ -32,7 +32,7 @@ const msg = {
|
||||
dashboards: "仪表盘",
|
||||
profiles: "性能剖析",
|
||||
database: "数据库",
|
||||
mySQL: "MySQL",
|
||||
mySQL: "MySQL/MariaDB",
|
||||
serviceName: "服务名称",
|
||||
technologies: "技术",
|
||||
health: "健康",
|
||||
@@ -176,6 +176,13 @@ const msg = {
|
||||
when4xx: "当响应代码介于400和499之间时,带有跟踪的HTTP请求和响应示例",
|
||||
when5xx: "当响应代码介于500和599之间时,带有跟踪的HTTP请求和响应示例",
|
||||
taskTitle: "HTTP请求和响应收集规则",
|
||||
iframeWidgetTip: "添加widget的链接",
|
||||
iframeSrc: "Iframe链接",
|
||||
generateLink: "生成链接",
|
||||
setDuration: "锁定查询持续时间",
|
||||
openFunction: "OpenFunction",
|
||||
period: "周期",
|
||||
windows: "Windows",
|
||||
seconds: "秒",
|
||||
hourTip: "选择小时",
|
||||
minuteTip: "选择分钟",
|
||||
@@ -194,7 +201,7 @@ const msg = {
|
||||
trace: "追踪",
|
||||
alarm: "告警",
|
||||
event: "事件",
|
||||
auto: "自动",
|
||||
auto: "自动更新",
|
||||
reload: "刷新",
|
||||
editmode: "编辑模式",
|
||||
version: "版本",
|
||||
@@ -282,12 +289,9 @@ const msg = {
|
||||
chartType: "图表类型",
|
||||
currentDepth: "当前深度",
|
||||
defaultDepth: "默认深度",
|
||||
traceTagsTip:
|
||||
"只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
logTagsTip:
|
||||
"只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
alarmTagsTip:
|
||||
"只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
traceTagsTip: "只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
logTagsTip: "只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
alarmTagsTip: "只有core/default/searchableTracesTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
tagsLink: "配置词汇页",
|
||||
addTag: "请添加标签",
|
||||
logCategory: "日志类别",
|
||||
@@ -310,8 +314,7 @@ const msg = {
|
||||
contentType: "内容类型",
|
||||
content: "内容",
|
||||
viewLogs: "查看日志",
|
||||
logsTagsTip:
|
||||
"只有core/default/searchableLogsTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
logsTagsTip: "只有core/default/searchableLogsTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。",
|
||||
keywordsOfContentLogTips: "SkyWalking OAP服务器的当前存储不支持此操作",
|
||||
setEvent: "设置事件",
|
||||
viewAttributes: "查看",
|
||||
@@ -368,10 +371,36 @@ const msg = {
|
||||
addKeywordsOfContent: "请输入一个内容关键词",
|
||||
addExcludingKeywordsOfContent: "请输入一个内容不包含的关键词",
|
||||
noticeTag: "请输入一个标签(key=value)之后回车",
|
||||
conditionNotice:
|
||||
"请输入一个内容关键词或者内容不包含的关键词(key=value)之后回车",
|
||||
conditionNotice: "请输入一个内容关键词或者内容不包含的关键词(key=value)之后回车",
|
||||
language: "语言",
|
||||
gateway: "网关",
|
||||
virtualMQ: "虚拟消息队列",
|
||||
AWSCloud: "AWS云服务",
|
||||
AWSCloudEKS: "EKS",
|
||||
AWSCloudS3: "S3",
|
||||
AWSCloudDynamoDB: "DynamoDB",
|
||||
AWSGateway: "AWS API Gateway",
|
||||
APIGateway: "API Gateway",
|
||||
redis: "Redis",
|
||||
elasticsearch: "Elasticsearch",
|
||||
mq: "消息队列",
|
||||
rabbitMQ: "RabbitMQ",
|
||||
save: "保存",
|
||||
editStrategy: "编辑策略",
|
||||
policyList: "策略列表",
|
||||
targetTypes: "目标类型",
|
||||
monitorType: "监视器类型",
|
||||
count: "总数",
|
||||
threshold: "阈值",
|
||||
uriRegex: "URI规则",
|
||||
uriList: "URI列表",
|
||||
processes: "进程",
|
||||
attributes: "属性",
|
||||
monitorInstances: "监视实例",
|
||||
processDashboards: "进程仪表板",
|
||||
instanceDashboards: "实例仪表板",
|
||||
detailLabel: "详细标签",
|
||||
summary: "概括",
|
||||
detail: "详细",
|
||||
};
|
||||
export default msg;
|
||||
|
@@ -22,6 +22,7 @@ import components from "@/components";
|
||||
import i18n from "./locales";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import "./styles/index.ts";
|
||||
import "virtual:svg-icons-register";
|
||||
|
||||
const app = createApp(App);
|
||||
const appStore = useAppStoreWithOut();
|
||||
|
50
src/mock/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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 Mock from "mockjs";
|
||||
|
||||
const Random = Mock.Random;
|
||||
const nodes = Mock.mock({
|
||||
"nodes|500": [
|
||||
{
|
||||
//id
|
||||
id: "@guid",
|
||||
name: "@name",
|
||||
"type|1": ["ActiveMQ", "activemq-consumer", "H2", "APISIX", "Express", "USER", "Flash"],
|
||||
"isReal|1": [true, false],
|
||||
},
|
||||
],
|
||||
});
|
||||
const calls = Mock.mock({
|
||||
"links|500": [
|
||||
{
|
||||
//id
|
||||
id: "@guid",
|
||||
detectPoints: ["SERVER", "CLIENT"],
|
||||
source: function () {
|
||||
const d = Random.integer(0, 250);
|
||||
return nodes.nodes[d].id;
|
||||
},
|
||||
target: function () {
|
||||
const d = Random.integer(250, 499);
|
||||
return nodes.nodes[d].id;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const callsMock = calls.links;
|
||||
const nodesMock = nodes.nodes;
|
||||
export { callsMock, nodesMock };
|
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import Layout from "@/layout/Index.vue";
|
||||
|
||||
export const routesAlarm: Array<RouteRecordRaw> = [
|
||||
@@ -31,8 +31,7 @@ export const routesAlarm: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "/alerting",
|
||||
name: "Alarm",
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "alerting" */ "@/views/Alarm.vue"),
|
||||
component: () => import("@/views/Alarm.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import Layout from "@/layout/Index.vue";
|
||||
|
||||
export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
@@ -30,10 +30,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/list",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/List.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/List.vue"),
|
||||
name: "List",
|
||||
meta: {
|
||||
title: "dashboardList",
|
||||
@@ -41,10 +38,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
},
|
||||
{
|
||||
path: "/dashboard/new",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/New.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/New.vue"),
|
||||
name: "New",
|
||||
meta: {
|
||||
title: "dashboardNew",
|
||||
@@ -54,38 +48,26 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
path: "",
|
||||
redirect: "/dashboard/:layerId/:entity/:name",
|
||||
name: "Create",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "CreateChild",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "CreateActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "View",
|
||||
redirect: "/dashboard/:layerId/:entity/:serviceId/:name",
|
||||
meta: {
|
||||
@@ -94,30 +76,20 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewChild",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect:
|
||||
"/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
redirect: "/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name",
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewServiceRelation",
|
||||
meta: {
|
||||
notShow: true,
|
||||
@@ -125,18 +97,12 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewServiceRelation",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/related/:layerId/:entity/:serviceId/:destServiceId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewServiceRelationActiveTabIndex",
|
||||
},
|
||||
],
|
||||
@@ -144,10 +110,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "",
|
||||
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewPod",
|
||||
meta: {
|
||||
notShow: true,
|
||||
@@ -155,30 +118,41 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewPod",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewPodActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect:
|
||||
"/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:name",
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewProcess",
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:name",
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewProcess",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:name/tab/:activeTabIndex",
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewProcessActiveTabIndex",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
redirect: "/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name",
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "PodRelation",
|
||||
meta: {
|
||||
notShow: true,
|
||||
@@ -186,18 +160,12 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewPodRelation",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:destServiceId/:destPodId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewPodRelationActiveTabIndex",
|
||||
},
|
||||
],
|
||||
@@ -206,10 +174,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
path: "",
|
||||
redirect:
|
||||
"/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ProcessRelation",
|
||||
meta: {
|
||||
notShow: true,
|
||||
@@ -217,30 +182,36 @@ export const routesDashboard: Array<RouteRecordRaw> = [
|
||||
children: [
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewProcessRelation",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name/tab/:activeTabIndex",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewProcessRelationActiveTabIndex",
|
||||
},
|
||||
{
|
||||
path: "/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name/duration/:duration",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
|
||||
),
|
||||
component: () => import("@/views/dashboard/Edit.vue"),
|
||||
name: "ViewProcessRelationDuration",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
name: "Widget",
|
||||
component: () => import("@/views/dashboard/Widget.vue"),
|
||||
meta: {
|
||||
notShow: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/page/:layer/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:config/:duration?",
|
||||
component: () => import("@/views/dashboard/Widget.vue"),
|
||||
name: "ViewWidget",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
95
src/router/data/aws.ts
Normal 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.
|
||||
*/
|
||||
|
||||
export default [
|
||||
{
|
||||
path: "",
|
||||
name: "AWSCloud",
|
||||
meta: {
|
||||
title: "AWSCloud",
|
||||
icon: "cloud_queue",
|
||||
hasGroup: true,
|
||||
},
|
||||
redirect: "/aws-eks",
|
||||
children: [
|
||||
{
|
||||
path: "/aws-eks",
|
||||
name: "AWSCloudEKS",
|
||||
meta: {
|
||||
title: "AWSCloudEKS",
|
||||
layer: "AWS_EKS",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-eks/tab/:activeTabIndex",
|
||||
name: "EKSActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "AWS_EKS",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-s3",
|
||||
name: "AWSCloudS3",
|
||||
meta: {
|
||||
title: "AWSCloudS3",
|
||||
layer: "AWS_S3",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-s3/tab/:activeTabIndex",
|
||||
name: "S3ActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "AWS_S3",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-dynamodb",
|
||||
name: "AWSCloudDynamoDB",
|
||||
meta: {
|
||||
title: "AWSCloudDynamoDB",
|
||||
layer: "AWS_DYNAMODB",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-dynamodb/tab/:activeTabIndex",
|
||||
name: "DynamoDBActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "AWS_DYNAMODB",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-api-gateway",
|
||||
name: "APIGateway",
|
||||
meta: {
|
||||
title: "APIGateway",
|
||||
layer: "AWS_GATEWAY",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-api-gateway/tab/:activeTabIndex",
|
||||
name: "APIGatewayActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "AWS_GATEWAY",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
@@ -58,6 +58,54 @@ export default [
|
||||
layer: "POSTGRESQL",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-dynamodb",
|
||||
name: "AWSCloudDynamoDB",
|
||||
meta: {
|
||||
title: "AWSCloudDynamoDB",
|
||||
layer: "AWS_DYNAMODB",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-dynamodb/tab/:activeTabIndex",
|
||||
name: "DynamoDBActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "AWS_DYNAMODB",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/redis",
|
||||
name: "Redis",
|
||||
meta: {
|
||||
title: "redis",
|
||||
layer: "REDIS",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/redis/tab/:activeTabIndex",
|
||||
name: "RedisActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "REDIS",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/elasticsearch",
|
||||
name: "Elasticsearch",
|
||||
meta: {
|
||||
title: "elasticsearch",
|
||||
layer: "ELASTICSEARCH",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/elasticsearch/tab/:activeTabIndex",
|
||||
name: "ElasticsearchActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "ELASTICSEARCH",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -21,18 +21,25 @@ export default [
|
||||
name: "Functions",
|
||||
meta: {
|
||||
title: "functions",
|
||||
icon: "cloud_queue",
|
||||
layer: "FAAS",
|
||||
icon: "functions",
|
||||
hasGroup: true,
|
||||
},
|
||||
redirect: "/functions",
|
||||
children: [
|
||||
{
|
||||
path: "/functions",
|
||||
name: "Functions",
|
||||
path: "/openFunction",
|
||||
name: "OpenFunction",
|
||||
meta: {
|
||||
title: "openFunction",
|
||||
layer: "FAAS",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/functions/tab/:activeTabIndex",
|
||||
name: "FunctionsActiveTabIndex",
|
||||
path: "/openFunction/tab/:activeTabIndex",
|
||||
name: "OpenFunctionActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "FAAS",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -42,6 +42,22 @@ export default [
|
||||
layer: "APISIX",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-gateway",
|
||||
name: "AWSGateway",
|
||||
meta: {
|
||||
title: "AWSGateway",
|
||||
layer: "AWS_GATEWAY",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/aws-gateway/tab/:activeTabIndex",
|
||||
name: "GatewayActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "AWS_GATEWAY",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -23,6 +23,8 @@ import functions from "./functions";
|
||||
import browser from "./browser";
|
||||
import k8s from "./k8s";
|
||||
import gateway from "./gateway";
|
||||
import aws from "./aws";
|
||||
import mq from "./mq";
|
||||
|
||||
export default [
|
||||
...general,
|
||||
@@ -30,8 +32,10 @@ export default [
|
||||
...functions,
|
||||
...k8s,
|
||||
...infrastructure,
|
||||
...aws,
|
||||
...browser,
|
||||
...gateway,
|
||||
...database,
|
||||
...mq,
|
||||
...selfObservability,
|
||||
];
|
||||
|
@@ -43,6 +43,23 @@ export default [
|
||||
layer: "OS_LINUX",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/windows",
|
||||
name: "Windows",
|
||||
meta: {
|
||||
title: "windows",
|
||||
layer: "OS_WINDOWS",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/windows/tab/:activeTabIndex",
|
||||
name: "WindowsActiveTabIndex",
|
||||
meta: {
|
||||
title: "windows",
|
||||
notShow: true,
|
||||
layer: "OS_WINDOWS",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
47
src/router/data/mq.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: "MQ",
|
||||
meta: {
|
||||
title: "mq",
|
||||
icon: "mq",
|
||||
hasGroup: true,
|
||||
},
|
||||
redirect: "/rabbitMQ",
|
||||
children: [
|
||||
{
|
||||
path: "/rabbitMQ",
|
||||
name: "RabbitMQ",
|
||||
meta: {
|
||||
title: "rabbitMQ",
|
||||
layer: "RABBITMQ",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/rabbitMQ/tab/:activeTabIndex",
|
||||
name: "RabbitMQActiveTabIndex",
|
||||
meta: {
|
||||
notShow: true,
|
||||
layer: "RABBITMQ",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
@@ -14,21 +14,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { routesDashboard } from "./dashboard";
|
||||
import { routesSetting } from "./setting";
|
||||
import { routesAlarm } from "./alarm";
|
||||
import routesLayers from "./layer";
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
...routesLayers,
|
||||
...routesDashboard,
|
||||
...routesAlarm,
|
||||
...routesSetting,
|
||||
];
|
||||
const routes: Array<RouteRecordRaw> = [...routesLayers, ...routesDashboard, ...routesAlarm, ...routesSetting];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes,
|
||||
});
|
||||
|
||||
|
@@ -22,8 +22,7 @@ function layerDashboards() {
|
||||
item.component = Layout;
|
||||
if (item.children) {
|
||||
item.children = item.children.map((d: any) => {
|
||||
d.component = () =>
|
||||
import(/* webpackChunkName: "layer" */ "@/views/Layer.vue");
|
||||
d.component = () => import("@/views/Layer.vue");
|
||||
return d;
|
||||
});
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import Layout from "@/layout/Index.vue";
|
||||
|
||||
export const routesSetting: Array<RouteRecordRaw> = [
|
||||
@@ -36,8 +36,7 @@ export const routesSetting: Array<RouteRecordRaw> = [
|
||||
icon: "settings",
|
||||
hasGroup: false,
|
||||
},
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "settings" */ "@/views/Settings.vue"),
|
||||
component: () => import("@/views/Settings.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -37,3 +37,19 @@ export const TimeRangeConfig = {
|
||||
textAlign: "center",
|
||||
text: "text",
|
||||
};
|
||||
|
||||
export const ControlsTypes = [
|
||||
"Trace",
|
||||
"Profile",
|
||||
"Log",
|
||||
"DemandLog",
|
||||
"Ebpf",
|
||||
"NetworkProfiling",
|
||||
"ThirdPartyApp",
|
||||
"ContinuousProfiling",
|
||||
"TaskTimeline",
|
||||
];
|
||||
export enum EBPFProfilingTriggerType {
|
||||
FIXED_TIME = "FIXED_TIME",
|
||||
CONTINUOUS_PROFILING = "CONTINUOUS_PROFILING",
|
||||
}
|
||||
|
@@ -17,8 +17,8 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { Alarm } from "@/types/alarm";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import type { Alarm } from "@/types/alarm";
|
||||
|
||||
interface AlarmState {
|
||||
loading: boolean;
|
||||
@@ -34,10 +34,8 @@ export const alarmStore = defineStore({
|
||||
total: 0,
|
||||
}),
|
||||
actions: {
|
||||
async getAlarms(params: any) {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryAlarms")
|
||||
.params(params);
|
||||
async getAlarms(params: Recordable) {
|
||||
const res: AxiosResponse = await graphql.query("queryAlarms").params(params);
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
@@ -50,6 +48,6 @@ export const alarmStore = defineStore({
|
||||
},
|
||||
});
|
||||
|
||||
export function useAlarmStore(): any {
|
||||
export function useAlarmStore(): Recordable {
|
||||
return alarmStore(store);
|
||||
}
|
||||
|
@@ -17,24 +17,24 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "@/store";
|
||||
import graphql from "@/graphql";
|
||||
import { Duration, DurationTime } from "@/types/app";
|
||||
import type { Duration, DurationTime } from "@/types/app";
|
||||
import getLocalTime from "@/utils/localtime";
|
||||
import { AxiosResponse } from "axios";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import dateFormatStep, { dateFormatTime } from "@/utils/dateFormat";
|
||||
import { TimeType } from "@/constants/data";
|
||||
/*global Nullable*/
|
||||
interface AppState {
|
||||
durationRow: any;
|
||||
durationRow: Recordable;
|
||||
utc: string;
|
||||
utcHour: number;
|
||||
utcMin: number;
|
||||
eventStack: (() => unknown)[];
|
||||
timer: Nullable<any>;
|
||||
timer: Nullable<TimeoutHandle>;
|
||||
autoRefresh: boolean;
|
||||
pageTitle: string;
|
||||
version: string;
|
||||
isMobile: boolean;
|
||||
reloadTimer: Nullable<any>;
|
||||
reloadTimer: Nullable<IntervalHandle>;
|
||||
}
|
||||
|
||||
export const appStore = defineStore({
|
||||
@@ -92,9 +92,7 @@ export const appStore = defineStore({
|
||||
this.duration.start.getMonth());
|
||||
break;
|
||||
}
|
||||
const utcSpace =
|
||||
(this.utcHour + new Date().getTimezoneOffset() / 60) * 3600000 +
|
||||
this.utcMin * 60000;
|
||||
const utcSpace = (this.utcHour + new Date().getTimezoneOffset() / 60) * 3600000 + this.utcMin * 60000;
|
||||
const startUnix: number = this.duration.start.getTime();
|
||||
const endUnix: number = this.duration.end.getTime();
|
||||
const timeIntervals: number[] = [];
|
||||
@@ -154,16 +152,14 @@ export const appStore = defineStore({
|
||||
}
|
||||
this.timer = setTimeout(
|
||||
() =>
|
||||
this.eventStack.forEach((event: any) => {
|
||||
this.eventStack.forEach((event: Function) => {
|
||||
setTimeout(event(), 0);
|
||||
}),
|
||||
500
|
||||
500,
|
||||
);
|
||||
},
|
||||
async queryOAPTimeInfo() {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryOAPTimeInfo")
|
||||
.params({});
|
||||
const res: AxiosResponse = await graphql.query("queryOAPTimeInfo").params({});
|
||||
if (res.data.errors) {
|
||||
this.utc = -(new Date().getTimezoneOffset() / 60) + ":0";
|
||||
} else {
|
||||
@@ -176,20 +172,18 @@ export const appStore = defineStore({
|
||||
return res.data;
|
||||
},
|
||||
async fetchVersion(): Promise<void> {
|
||||
const res: AxiosResponse = await graphql
|
||||
.query("queryOAPVersion")
|
||||
.params({});
|
||||
const res: AxiosResponse = await graphql.query("queryOAPVersion").params({});
|
||||
if (res.data.errors) {
|
||||
return res.data;
|
||||
}
|
||||
this.version = res.data.data.version;
|
||||
return res.data;
|
||||
},
|
||||
setReloadTimer(timer: any): void {
|
||||
setReloadTimer(timer: IntervalHandle) {
|
||||
this.reloadTimer = timer;
|
||||
},
|
||||
},
|
||||
});
|
||||
export function useAppStoreWithOut(): any {
|
||||
export function useAppStoreWithOut(): Recordable {
|
||||
return appStore(store);
|
||||
}
|
||||
|