79 Commits

Author SHA1 Message Date
Fine0830
6c4991bc56 fix: view related trace (#285) 2023-06-16 11:13:36 +08:00
Fine0830
77c1694383 build: check component types (#283) 2023-06-14 09:35:35 +08:00
Fine0830
7fe3257c32 fix: query process metrics (#284) 2023-06-13 20:11:38 +08:00
Fine0830
8c9c339417 feat: filter tasks with current process ID (#282) 2023-06-13 14:48:38 +08:00
Fine0830
62a82590c9 fix: bugs fix and polish components for continue profiling (#281) 2023-06-12 18:24:13 +08:00
Fine0830
22db68646c feat: Implement task timeline and policy list widget for continous profiling (#280) 2023-06-12 16:17:38 +08:00
Fine0830
7738695601 fix: show error messages and update style for no data (#279) 2023-06-09 21:03:51 +08:00
Fine0830
9b1a5f7a74 fix: expressions (#278) 2023-06-09 18:23:33 +08:00
Fine0830
54997794b4 feat: remove returnTypeOfMQE and add detail label (#277) 2023-06-08 20:52:44 +08:00
Fine0830
372aee2eb6 fix: remove name (#276) 2023-06-08 09:21:17 +08:00
Fine0830
d662a0fb54 feat: remove metric name from queries (#275) 2023-06-07 16:58:30 +08:00
Fine0830
b293f4ddb5 build: bump vite and fix code style (#272) 2023-06-07 13:37:41 +08:00
Fine0830
279ec60915 Revert "build(deps): bump @antfu/utils, unplugin-auto-import and unplugin-vue-components (#269)" (#274)
This reverts commit ec67b4148c.
2023-06-07 13:20:04 +08:00
Fine0830
05688f0888 Revert "build(deps-dev): bump vite from 4.0.0 to 4.0.5 (#271)" (#273)
This reverts commit 15cd839480.
2023-06-07 12:31:54 +08:00
dependabot[bot]
15cd839480 build(deps-dev): bump vite from 4.0.0 to 4.0.5 (#271) 2023-06-06 15:06:47 +08:00
Fine0830
dc22f8da6e feat: supporting expressions to query metrics data (#270) 2023-06-04 14:09:36 +08:00
dependabot[bot]
ec67b4148c build(deps): bump @antfu/utils, unplugin-auto-import and unplugin-vue-components (#269) 2023-06-03 16:02:34 +08:00
Fine0830
986fe8b90e fix: topn type (#267) 2023-05-25 09:12:46 +08:00
Fine0830
fe28fae27d fix: pod list (#266) 2023-05-15 10:52:54 +08:00
xu1009
359197a1c5 add grizzly icon (#265)
Co-authored-by: litexu <litexu@tencent.com>
2023-05-11 00:01:15 +08:00
Fine0830
903cf8e9bd fix: set endpoint and instance selectors with url parameters (#264) 2023-05-03 00:16:09 +08:00
xu1009
921c961dc1 add jersey icon (#263) 2023-04-28 18:57:16 +08:00
innerpeacez
d129c75c8c feat: add mq menu (#262)
Signed-off-by: innerpeacez <innerpeace.zhai@gmail.com>
Co-authored-by: Fine <fanxue0830@gmail.com>
2023-04-27 17:38:13 +08:00
dependabot[bot]
a4a2c4fefc build(deps): bump yaml, @commitlint/load, lint-staged, stylelint, stylelint-config-standard and stylelint-order (#261) 2023-04-25 10:20:27 +08:00
Fine0830
7dde4820de build(deps): bump element-plus from 2.0.2 to 2.1.0 (#260) 2023-04-16 10:53:09 +08:00
Fine0830
7257858921 feat: update options for line graph (#259) 2023-04-14 17:09:30 +08:00
Fine0830
ce585d6e08 feat: set default value for showSymbol in the line graph (#258) 2023-04-14 10:35:30 +08:00
Fine0830
3f178f89f8 feat: support isEmptyValue flag for metrics query (#256) 2023-04-13 21:29:23 +08:00
innerpeacez
ceae10cbfa feat: add elasticsearch menu (#257) 2023-04-13 17:08:00 +08:00
Fine0830
49a5481fb0 types: optimize data types (#254) 2023-04-06 21:50:57 +08:00
yswdqz
8bb45bb453 Add Redis Menu (#253) 2023-04-05 22:20:11 +08:00
hadesy
8077043c7e fix:incorrect node name length calculation (#252) 2023-04-05 17:23:26 +08:00
Fine0830
0e15c023cc fix: polish tooltips for trace profiling widget (#251) 2023-04-02 20:52:25 +08:00
Fine0830
55e4828adc feat: update trace profiling protocol (#250) 2023-03-30 09:50:14 +08:00
Fine0830
68eea52e88 fix: update menu (#249) 2023-03-27 21:37:45 +08:00
pg.yang
5973632f0f Add AWS API Gateway menu (#248) 2023-03-26 10:01:05 +08:00
Fine0830
1d189e82a7 fix: set filter ID when ReadRecords metric associates with trace (#247) 2023-03-23 21:40:38 +08:00
Fine0830
2491ab7176 fix: incorrect operation menu content in the topology widget (#246) 2023-03-23 11:13:03 +08:00
Fine0830
449dccdf36 refactor: redesign and implement new topology (#243) 2023-03-22 17:00:24 +08:00
Fine0830
8031c1b463 revert: cpm5d (#245) 2023-03-21 09:27:02 +08:00
Fine0830
1c905aeb06 fix: alerting link (#244) 2023-03-16 17:44:25 +08:00
Fine0830
4e64b9a4b1 fix: operation pop-up window (#242) 2023-02-28 16:26:33 +08:00
Fine0830
1be572a95f feat: add cpm4d + avg calculations (#241) 2023-02-28 12:50:55 +08:00
Fine0830
5cc913a332 fix: data (#240) 2023-02-24 17:46:46 +08:00
Fine0830
220525a2d9 feat: add a calculation for cpm5d (#239) 2023-02-22 09:14:23 +08:00
yswdqz
72060f8227 Add AWS DynanoDB menu (#237) 2023-02-20 20:57:52 +08:00
Fine0830
b247ed1c24 feat: optimize menus and add Windows monitoring menu (#236) 2023-02-17 13:29:42 +08:00
Fine0830
b2707e0e62 feat: add period (#235) 2023-02-15 13:37:14 +08:00
Fine0830
a1066f09e4 refactor: optimize the attached events visualization in the trace widget (#234) 2023-02-15 13:32:32 +08:00
XinweiLyu
efed817f73 Add logo for HTTPX (#232) 2023-02-13 09:57:55 +08:00
Fine0830
4613149759 fix: remove duplicate query message (#231) 2023-02-10 20:56:15 +08:00
Fine0830
1877776720 refactor: optimize side bar component to make it more friendly (#230) 2023-02-09 17:54:11 +08:00
Fine0830
2b88266d67 build: bump postcss to a non-vulnerable version (#229) 2023-02-09 15:02:26 +08:00
dependabot[bot]
17b627a5d9 build(deps): bump @sideway/formula from 3.0.0 to 3.0.1 (#228) 2023-02-09 13:56:40 +08:00
pg.yang
5b0a68fe18 Add AWS S3 menu (#227) 2023-02-08 22:43:05 +08:00
Fine0830
d93ed2c5d3 fix: reset duration for query conditions after time range changes (#226) 2023-02-08 12:08:45 +08:00
Fine0830
c73322a504 fix: clear trace ID on the Log and Trace widgets after using association (#225) 2023-02-07 21:23:13 +08:00
Fine0830
4486684183 feat: add auto fresh to widgets independent mode (#224) 2023-02-07 16:14:38 +08:00
Fine0830
1768a1641c feat: update menus for OpenFunction (#223) 2023-02-06 14:36:13 +08:00
Fine0830
224053c0f4 feat: Implement independent mode for widgets (#221) 2023-02-06 13:38:19 +08:00
dependabot[bot]
ca38366a60 build(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#222) 2023-02-06 10:04:39 +08:00
Superskyyy
45f2985549 Add websockets icon (#220)
* Add websockets icon

* Better one

* compress png to 12k
2023-01-17 21:46:57 -05:00
wuwen
3ef790dc07 fix: Formatted display of json content type (#219) 2023-01-16 20:34:51 +08:00
Superskyyy (COVID)
70df7605cb Add missing fastapi logo (#218) 2023-01-14 07:23:49 +08:00
dependabot[bot]
4fc451f370 build(deps): bump json5 from 1.0.1 to 1.0.2 (#217) 2023-01-08 21:11:59 +08:00
Fine0830
163de5e5cf refactor: optimize graph tooltips to make them more friendly (#216) 2023-01-05 17:44:28 +08:00
Fine0830
8785817efe feat: add a iframe widget for zipkin ui (#215) 2023-01-03 16:12:45 +08:00
Fine0830
db793e6c05 docs: update (#214) 2022-12-29 17:55:44 +08:00
Fine0830
d11ceab59d fix: add ElPopconfirm (#213) 2022-12-28 19:02:44 +08:00
吴晟 Wu Sheng
1278454148 Update .gitignore (#212) 2022-12-28 10:51:39 +08:00
Fine0830
7768f6ef16 fix: drag nodes (#211) 2022-12-20 16:52:14 +08:00
Fine0830
210b9ba491 build: update vite config (#210) 2022-12-18 11:16:21 +08:00
Fine0830
969580b770 fix: Instance Relation and Endpoint Relation dashboards show up (#209) 2022-12-17 15:59:59 +08:00
Fine0830
44dcb1e7f6 build: migrate the build tool from vue-cli to vite4 (#208) 2022-12-17 14:07:03 +08:00
Mahmoud Anwer
1e0c253488 Update MySQL UI to support MariaDB (#207) 2022-12-12 10:45:07 +08:00
dependabot[bot]
141a288542 build(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#204) 2022-12-08 11:10:01 +08:00
Fine0830
154372615e update menu icons (#203) 2022-12-07 11:53:45 +08:00
pg.yang
253f5c9261 Add AWS menu for supporting AWS monitoring (#202) 2022-12-06 21:41:15 +08:00
Marcin Grzejszczak
aa11a681ce Added Micrometer icon (#201) 2022-12-02 23:57:35 +08:00
282 changed files with 32325 additions and 55028 deletions

33
.eslintignore Normal file
View 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
View 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",
},
};

View File

@@ -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
View File

@@ -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
View File

@@ -0,0 +1,27 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#!/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
View File

@@ -0,0 +1,27 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#!/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
View 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
View 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/*

View File

@@ -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
View 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
View 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",
},
});

View File

@@ -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
View 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"]
}
}

View File

@@ -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"
}

View File

@@ -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 {};

View File

@@ -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
View 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;
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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": [

View File

@@ -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
View 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",
};

View File

@@ -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>

View 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

View File

@@ -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

View File

@@ -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

View 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

View 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
View 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

View 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
View 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

View 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
View 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

View File

@@ -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

View 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

View File

@@ -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;
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

File diff suppressed because it is too large Load Diff

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
});
});

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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
}
}`,
};

View File

@@ -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
}
`,
};

View File

@@ -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) {

View File

@@ -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;
})

View File

@@ -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}}`;

View File

@@ -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}}`;

View File

@@ -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}}`;

View File

@@ -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}}`;

View File

@@ -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
}`,
};

View File

@@ -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 };
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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(() => {

View File

@@ -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 = () => {

View 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;
}

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -32,6 +32,7 @@ if (!savedLanguage) {
}
language = savedLanguage ? savedLanguage : language;
const i18n = createI18n({
legacy: false,
locale: language,
messages,
});

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
View 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 };

View File

@@ -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"),
},
],
},

View File

@@ -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
View File

@@ -0,0 +1,95 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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",
},
},
],
},
];

View File

@@ -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",
},
},
],
},
];

View File

@@ -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",
},
},
],
},

View File

@@ -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",
},
},
],
},
];

View File

@@ -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,
];

View File

@@ -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
View 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",
},
},
],
},
];

View File

@@ -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,
});

View File

@@ -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;
});
}

View File

@@ -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"),
},
],
},

View File

@@ -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",
}

View File

@@ -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);
}

View File

@@ -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);
}

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