diff --git a/package-lock.json b/package-lock.json index fa83fcd0d..032899f3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "n9e-fe", - "version": "v8.5.0", + "version": "v9.0.0-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "n9e-fe", - "version": "v8.5.0", + "version": "v9.0.0-beta.1", "dependencies": { "@ag-grid-community/locale": "^34.1.1", "@ant-design/graphs": "^1.3.6", @@ -32,7 +32,7 @@ "@dnd-kit/sortable": "7.0.2", "@fc-components/codemirror-promql": "^0.19.13", "@fc-components/es-query": "^0.0.3", - "@fc-components/monaco-editor": "^0.3.1", + "@fc-components/monaco-editor": "^0.3.6", "@fc-components/use-antd-resizable-header": "^2.8.16", "@fc-plot/ts-graph": "^0.27.2", "@microsoft/fetch-event-source": "^2.0.1", @@ -4482,9 +4482,9 @@ } }, "node_modules/@fc-components/monaco-editor": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@fc-components/monaco-editor/-/monaco-editor-0.3.1.tgz", - "integrity": "sha512-Rr+ZCTZq75t/8mKMYxIpWabdQnctZfdu67hAFMLZcCZmKkFJVyJM6fbZy+GiiABsL7EYg3muDYSm86Oju/bwpg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@fc-components/monaco-editor/-/monaco-editor-0.3.6.tgz", + "integrity": "sha512-MwB420GGZwCc/eBxYXb2fRw8owmnku/oLx5WXoN377qHgcRhgkpUOM4S0yE8j2f9LJ/QS8fjVybgyAk1RDb/vg==", "license": "MIT", "dependencies": { "@emotion/css": "11.13.4", @@ -4497,6 +4497,7 @@ "monaco-promql": "^1.7.4", "pluralize": "^8.0.0", "react-monaco-editor": "^0.58.0", + "sql-formatter": "^15.8.0", "uuidv4": "^6.2.13" }, "engines": { @@ -10234,6 +10235,12 @@ "direction": "cli.js" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "license": "MIT" + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", @@ -16301,6 +16308,12 @@ "resolved": "https://registry.npmjs.org/monaco-promql/-/monaco-promql-1.7.4.tgz", "integrity": "sha512-ehiDBi7or/ndoRL98Lj9NgttXcAKNlH85OatV1bfdpseeVA8ziUm7Yi7mhICW6ORc7FZDDfUFcYg0SVrFYEpnw==" }, + "node_modules/moo": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.3.tgz", + "integrity": "sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA==", + "license": "BSD-3-Clause" + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/mri/-/mri-1.2.0.tgz", @@ -16395,6 +16408,34 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, + "node_modules/nearley/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/needle": { "version": "3.3.1", "resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz", @@ -17340,6 +17381,25 @@ "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-2.0.3.tgz", "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "license": "CC0-1.0" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "license": "MIT", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/rc-align": { "version": "4.0.15", "resolved": "https://registry.npmmirror.com/rc-align/-/rc-align-4.0.15.tgz", @@ -19402,7 +19462,6 @@ "version": "0.1.15", "resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, "engines": { "node": ">=0.12" } @@ -20307,6 +20366,19 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sql-formatter": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.8.0.tgz", + "integrity": "sha512-HnjdRHlSsO4Ap2erB5YXAvWggrnk/S4TezUn8zmpq9J/hEKn9+6gGaqiKPyDtI10Xf4zJmHYPREGjMjZmmP1fg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "nearley": "^2.20.1" + }, + "bin": { + "sql-formatter": "bin/sql-formatter-cli.cjs" + } + }, "node_modules/stack-generator": { "version": "2.0.10", "resolved": "https://registry.npmmirror.com/stack-generator/-/stack-generator-2.0.10.tgz", @@ -26503,9 +26575,9 @@ "requires": {} }, "@fc-components/monaco-editor": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@fc-components/monaco-editor/-/monaco-editor-0.3.1.tgz", - "integrity": "sha512-Rr+ZCTZq75t/8mKMYxIpWabdQnctZfdu67hAFMLZcCZmKkFJVyJM6fbZy+GiiABsL7EYg3muDYSm86Oju/bwpg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@fc-components/monaco-editor/-/monaco-editor-0.3.6.tgz", + "integrity": "sha512-MwB420GGZwCc/eBxYXb2fRw8owmnku/oLx5WXoN377qHgcRhgkpUOM4S0yE8j2f9LJ/QS8fjVybgyAk1RDb/vg==", "requires": { "@emotion/css": "11.13.4", "@fc-components/lezer-metricsql": "^0.0.2", @@ -26517,6 +26589,7 @@ "monaco-promql": "^1.7.4", "pluralize": "^8.0.0", "react-monaco-editor": "^0.58.0", + "sql-formatter": "^15.8.0", "uuidv4": "^6.2.13" }, "dependencies": { @@ -31120,6 +31193,11 @@ "resolved": "https://registry.npmmirror.com/direction/-/direction-2.0.1.tgz", "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==" }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==" + }, "dlv": { "version": "1.1.3", "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", @@ -36413,6 +36491,11 @@ "resolved": "https://registry.npmjs.org/monaco-promql/-/monaco-promql-1.7.4.tgz", "integrity": "sha512-ehiDBi7or/ndoRL98Lj9NgttXcAKNlH85OatV1bfdpseeVA8ziUm7Yi7mhICW6ORc7FZDDfUFcYg0SVrFYEpnw==" }, + "moo": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.3.tgz", + "integrity": "sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA==" + }, "mri": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/mri/-/mri-1.2.0.tgz", @@ -36484,6 +36567,24 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "requires": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, "needle": { "version": "3.3.1", "resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz", @@ -37207,6 +37308,20 @@ "fast-diff": "1.1.2" } }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==" + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, "rc-align": { "version": "4.0.15", "resolved": "https://registry.npmmirror.com/rc-align/-/rc-align-4.0.15.tgz", @@ -38953,8 +39068,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "reusify": { "version": "1.0.4", @@ -39641,6 +39755,15 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "sql-formatter": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.8.0.tgz", + "integrity": "sha512-HnjdRHlSsO4Ap2erB5YXAvWggrnk/S4TezUn8zmpq9J/hEKn9+6gGaqiKPyDtI10Xf4zJmHYPREGjMjZmmP1fg==", + "requires": { + "argparse": "^2.0.1", + "nearley": "^2.20.1" + } + }, "stack-generator": { "version": "2.0.10", "resolved": "https://registry.npmmirror.com/stack-generator/-/stack-generator-2.0.10.tgz", diff --git a/package.json b/package.json index f9aa586ea..a1cb6bf18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n9e-fe", - "version": "v8.5.0", + "version": "v9.0.0-beta.1", "scripts": { "dev": "vite --port 8765 --host", "dev:advanced": "vite --port 8765 --host --mode advanced", @@ -36,7 +36,7 @@ "@dnd-kit/sortable": "7.0.2", "@fc-components/codemirror-promql": "^0.19.13", "@fc-components/es-query": "^0.0.3", - "@fc-components/monaco-editor": "^0.3.1", + "@fc-components/monaco-editor": "^0.3.6", "@fc-components/use-antd-resizable-header": "^2.8.16", "@fc-plot/ts-graph": "^0.27.2", "@microsoft/fetch-event-source": "^2.0.1", diff --git a/plugins/PlusPlaceholder.tsx b/plugins/PlusPlaceholder.tsx index e1bc1998f..d0e73789a 100644 --- a/plugins/PlusPlaceholder.tsx +++ b/plugins/PlusPlaceholder.tsx @@ -57,6 +57,9 @@ const AckBtnDefault = () => { }; const getBrainLicense = null; const options = []; +const esQueryBuilder = async () => { + return {}; +}; export { AlertRule, QueryBuilder, @@ -83,4 +86,5 @@ export { AckBtnDefault, getBrainLicense, options, + esQueryBuilder, }; diff --git a/public/font/NotoSansSC-Medium.ttf b/public/font/NotoSansSC-Medium.ttf new file mode 100644 index 000000000..901afd40c Binary files /dev/null and b/public/font/NotoSansSC-Medium.ttf differ diff --git a/public/font/NotoSansSC-Regular.ttf b/public/font/NotoSansSC-Regular.ttf new file mode 100644 index 000000000..9c21fd3fe Binary files /dev/null and b/public/font/NotoSansSC-Regular.ttf differ diff --git a/public/font/NotoSansSC-SemiBold.ttf b/public/font/NotoSansSC-SemiBold.ttf new file mode 100644 index 000000000..707bc3448 Binary files /dev/null and b/public/font/NotoSansSC-SemiBold.ttf differ diff --git a/scripts/generate_antd_dark_less.js b/scripts/generate_antd_dark_less.js index 26bff30cd..a711cc55a 100644 --- a/scripts/generate_antd_dark_less.js +++ b/scripts/generate_antd_dark_less.js @@ -39,9 +39,6 @@ function saveLess(filePath, filename, callback) { 'border-color-base': 'rgba(204,204,220,0.2)', // input框的边框颜色 'border-color-split': 'rgb(57, 60, 77)', 'component-background': 'rgb(22 22 24)', //fc-fill-2 - // 'table-header-bg': 'rgb(34 37 43)', //fc-fill-3 - // 'table-row-hover-bg': 'rgba(204, 204, 220, 0.08)', - 'table-header-sort-bg': 'rgba(204, 204, 220, 0.08)', 'popover-background': 'rgb(22 22 24)', //fc-fill-2 'normal-color': 'rgb(79, 82, 99)', @@ -53,12 +50,16 @@ function saveLess(filePath, filename, callback) { 'table-body-sort-bg': 'var(--fc-fill-2-5)', 'table-row-hover-bg': 'rgb(var(--fc-fill-5-rgb) / 0.2)', 'table-selected-row-color': 'inherit', - // Keep Less color functions compile-safe; runtime CSS vars are patched in theme/default.less. + // AntD wraps @table-selected-row-bg in darken() (default.less:644), so it must + // be a Less-parsable color literal, not a var(). Keep this explicit neutral overlay. 'table-selected-row-bg': 'rgba(228, 228, 231, 0.15)', 'table-body-selected-sort-bg': '@table-selected-row-bg', + // Explicit override so AntD's darken(@table-selected-row-bg) default is not used. 'table-selected-row-hover-bg': 'rgba(228, 228, 231, 0.25)', 'table-expanded-row-bg': 'var(--fc-fill-2-5)', - 'table-border-color': '#e4e4e7', + // AntD uses @table-border-color in Less color functions such as lighten(), + // so this must be the compiled dark value of --fc-border-color, not a CSS var(). + 'table-border-color': 'rgba(255, 255, 255, 0.06)', 'table-padding-vertical': '16px', 'table-padding-horizontal': '16px', 'table-padding-vertical-md': '(@table-padding-vertical * 3 / 4)', diff --git a/scripts/generate_antd_gold_less.js b/scripts/generate_antd_gold_less.js index 79b1ddd66..d8bbb3684 100644 --- a/scripts/generate_antd_gold_less.js +++ b/scripts/generate_antd_gold_less.js @@ -45,38 +45,13 @@ function saveLess(filePath, filename, callback) { // 'checkbox-check-color': '#333', 'btn-primary-color': '#222', + 'table-header-bg': '#fff8e6', //fc-fill-3 + 'table-row-hover-bg': '#fff2cb', + 'table-header-sort-bg': '#fff8e6', 'font-family': 'Helvetica Neue,sans-serif,PingFangSC-Regular,microsoft yahei ui,microsoft yahei,simsun,"sans-serif"', - // 2026-05 table design + // 2026-03 release-23 版本,table 的 header 和 hover 背景色不调整还是以当前主题色为主 'table-bg': 'var(--fc-fill-2)', - 'table-header-bg': 'var(--fc-fill-2-5)', - 'table-header-color': 'var(--fc-text-3)', - 'table-header-sort-bg': 'var(--fc-fill-2-5)', - 'table-body-sort-bg': 'var(--fc-fill-2-5)', - 'table-row-hover-bg': 'rgb(var(--fc-fill-5-rgb) / 0.2)', - 'table-selected-row-color': 'inherit', - // Keep Less color functions compile-safe; runtime CSS vars are patched in theme/default.less. - 'table-selected-row-bg': 'rgba(228, 228, 231, 0.15)', - 'table-body-selected-sort-bg': '@table-selected-row-bg', - 'table-selected-row-hover-bg': 'rgba(228, 228, 231, 0.25)', - 'table-expanded-row-bg': 'var(--fc-fill-2-5)', - 'table-border-color': '#e4e4e7', - 'table-padding-vertical': '16px', - 'table-padding-horizontal': '16px', - 'table-padding-vertical-md': '(@table-padding-vertical * 3 / 4)', - 'table-padding-horizontal-md': '(@table-padding-horizontal / 2)', - 'table-padding-vertical-sm': '(@table-padding-vertical / 2)', - 'table-padding-horizontal-sm': '(@table-padding-horizontal / 2)', - 'table-border-radius-base': '@border-radius-base', - 'table-footer-bg': '@table-header-bg', - 'table-footer-color': '@table-header-color', - 'table-header-bg-sm': '@table-header-bg', - 'table-font-size': '12px', - 'table-font-size-md': '14px', - 'table-font-size-sm': '@table-font-size', - 'table-header-cell-split-color': 'var(--fc-border-color)', - 'table-header-sort-active-bg': 'rgb(var(--fc-fill-5-rgb) / 0.4)', - 'table-fixed-header-sort-active-bg': 'var(--fc-fill-3)', 'border-radius-base': '8px', 'border-radius-sm': '4px', 'checkbox-border-radius': '2px', diff --git a/src/App.less b/src/App.less index 11813ab0b..5356ab6e5 100644 --- a/src/App.less +++ b/src/App.less @@ -381,26 +381,6 @@ input::placeholder { } } -// antd表格排序icon特殊处理 -.ant-table-column-has-sorters .ant-table-column-sorter-inner { - .ant-table-column-sorter-up, - .ant-table-column-sorter-down { - transition: all 0.2s; - } - - &:has(.ant-table-column-sorter-up.active) { - .ant-table-column-sorter-down { - opacity: 0; - } - } - - &:has(.ant-table-column-sorter-down.active) { - .ant-table-column-sorter-up { - opacity: 0; - } - } -} - .theme-light.theme-light-gold { .login-main.integration { .ant-btn-primary { diff --git a/src/components/AdvancedWrap/utils.ts b/src/components/AdvancedWrap/utils.ts index a46d59f27..f9494d1e0 100644 --- a/src/components/AdvancedWrap/utils.ts +++ b/src/components/AdvancedWrap/utils.ts @@ -178,3 +178,14 @@ export const getGraphProByCate = (cate: string) => { const currentCate = _.find(allCates, { value: cate }); return currentCate?.graphPro; }; + +export const getPrimaryTypeByCate = (cate: string): 'metric' | 'logging' | undefined => { + const currentCate = _.find(allCates, { value: cate }); + if (currentCate) { + const primaryType = currentCate.type[0]; + if (primaryType === 'metric' || primaryType === 'logging') { + return primaryType; + } + } + return undefined; +}; diff --git a/src/components/AiChatNG/AiChatContainer.tsx b/src/components/AiChatNG/AiChatContainer.tsx index aa14548bb..11fed0986 100644 --- a/src/components/AiChatNG/AiChatContainer.tsx +++ b/src/components/AiChatNG/AiChatContainer.tsx @@ -14,7 +14,7 @@ interface IAiChatContainerProps { export default function AiChatContainer(props: IAiChatContainerProps) { const { drawerWidth = 900, floatingStorageKey = 'ai-chat-floating-panel' } = props; - const { visible, mode, closeAiChat, queryPageFrom, queryAction, promptList, onExecuteQueryForQueryContent } = useAiChatContext(); + const { visible, mode, closeAiChat, queryPageFrom, queryAction, promptList, initialMessage, onExecuteQueryForQueryContent } = useAiChatContext(); const DRAWER_MIN_WIDTH = 440; const DRAWER_WIDTH_STORAGE_KEY = 'ai-chat-drawer-width'; @@ -149,6 +149,7 @@ export default function AiChatContainer(props: IAiChatContainerProps) { queryPageFrom={ensuredPageFrom} queryAction={queryAction} promptList={promptList} + initialMessage={initialMessage} onExecuteQueryForQueryContent={onExecuteQueryForQueryContent} /> )} diff --git a/src/components/AiChatNG/ChatPanel.tsx b/src/components/AiChatNG/ChatPanel.tsx index f3a2c3a4d..5da073b03 100644 --- a/src/components/AiChatNG/ChatPanel.tsx +++ b/src/components/AiChatNG/ChatPanel.tsx @@ -16,7 +16,7 @@ const POLLING_INTERVAL = 3000; export default function ChatPanel(props: IAiChatProps) { const { t } = useTranslation(NAME_SPACE); - const { placeholder, chatId, queryPageFrom, queryAction, promptList, onExecuteQueryForQueryContent, onChatChange, onError, welcomeSlot } = props; + const { placeholder, chatId, queryPageFrom, queryAction, promptList, initialMessage, onExecuteQueryForQueryContent, onChatChange, onError, welcomeSlot } = props; const [activeChat, setActiveChat] = useState(); const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); @@ -194,6 +194,8 @@ export default function ChatPanel(props: IAiChatProps) { }; }, [cleanupPolling, stopStream]); + const initialMessageSentRef = useRef(false); + const createNewChat = useCallback(async () => { try { const chat = await createChat(queryPageFrom); @@ -279,6 +281,13 @@ export default function ChatPanel(props: IAiChatProps) { [activeChat, chatId, createNewChat, handleError, inputValue, mergeMessage, onChatChange, queryAction, queryPageFrom, startPolling, submitting, syncMessageDetail, t], ); + useEffect(() => { + if (initialMessage && !initialMessageSentRef.current) { + initialMessageSentRef.current = true; + sendUserMessage(undefined, initialMessage); + } + }, [initialMessage, sendUserMessage]); + const handleStop = useCallback(async () => { if (!streamingLocator) return; try { diff --git a/src/components/AiChatNG/FlashAiButton.tsx b/src/components/AiChatNG/FlashAiButton.tsx index 9ba398db1..b32557bcd 100644 --- a/src/components/AiChatNG/FlashAiButton.tsx +++ b/src/components/AiChatNG/FlashAiButton.tsx @@ -17,6 +17,7 @@ function FlashAiButtonContent() { return ( - - + {!collapsed && ( + + )} + + diff --git a/src/components/SideMenu/menu.less b/src/components/SideMenu/menu.less index dc14e7360..9998154a0 100644 --- a/src/components/SideMenu/menu.less +++ b/src/components/SideMenu/menu.less @@ -26,8 +26,23 @@ } } -.side-menu-profile-submenu { +.side-menu-profile-menu { + .ant-dropdown-menu-item-icon, + .anticon { + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; + vertical-align: middle; + } + .ant-dropdown-menu-item-icon svg, + .anticon svg { + display: block; + } +} + +.side-menu-profile-submenu { .ant-dropdown-menu-item-active:not(:hover), .ant-dropdown-menu-submenu-title.ant-dropdown-menu-item-active:not(:hover), .ant-dropdown-menu-submenu-active>.ant-dropdown-menu-submenu-title:not(:hover) { @@ -161,8 +176,8 @@ } .side-menu-footer { - padding-top: 6px; - padding-bottom: 8px; + padding-top: 0; + padding-bottom: 0; } .side-menu-profile-row { @@ -177,6 +192,12 @@ flex: 1 1 auto; } +.side-menu-profile-setting-button { + width: 28px; + height: 28px; + opacity: 0.72; +} + .side-menu-profile-detail-on-dark { color: rgba(255, 255, 255, 0.68); } @@ -428,4 +449,4 @@ border-color: #e3e4e6; border-radius: 16px; } -} \ No newline at end of file +} diff --git a/src/components/SideMenu/menu.tsx b/src/components/SideMenu/menu.tsx index 8035ae4e7..a55f2adaa 100644 --- a/src/components/SideMenu/menu.tsx +++ b/src/components/SideMenu/menu.tsx @@ -2,6 +2,7 @@ * 这是开源版的菜单配置,专业版和企业版的配置在其他对应的仓库文件里 */ import React from 'react'; +import { RobotOutlined } from '@ant-design/icons'; import IconFont from '@/components/IconFont'; @@ -141,6 +142,18 @@ export const getMenuList = (embeddedProductMenu: MenuItem[] = [], hideDeprecated ...embeddedProductMenu, ], }, + { + key: 'ai-config', + label: 'menu.ai_config', + type: 'tabs', + icon: , + children: [ + { key: '/ai-config/agents', label: 'menu.ai_config_agents' }, + { key: '/ai-config/llm-configs', label: 'menu.ai_config_llm_configs' }, + { key: '/ai-config/skills', label: 'menu.ai_config_skills' }, + { key: '/ai-config/mcp-servers', label: 'menu.ai_config_mcp_servers' }, + ], + }, { key: 'organization', label: 'menu.organization', @@ -165,17 +178,6 @@ export const getMenuList = (embeddedProductMenu: MenuItem[] = [], hideDeprecated label: 'menu.setting', icon: , children: [ - { - key: 'ai-config', - label: 'menu.ai_config', - type: 'tabs', - children: [ - { key: '/ai-config/agents', label: 'menu.ai_config_agents' }, - { key: '/ai-config/llm-configs', label: 'menu.ai_config_llm_configs' }, - { key: '/ai-config/skills', label: 'menu.ai_config_skills' }, - { key: '/ai-config/mcp-servers', label: 'menu.ai_config_mcp_servers' }, - ], - }, { key: '/system/site-settings', label: 'menu.site_setting', diff --git a/src/components/SideMenu/utils.ts b/src/components/SideMenu/utils.ts index a79139b8d..2576ef592 100644 --- a/src/components/SideMenu/utils.ts +++ b/src/components/SideMenu/utils.ts @@ -22,6 +22,20 @@ export const findMenuByPath = (path: string, menuList: MenuItem[]): MenuMatchRes for (const parent of menuList) { if (!parent.children) continue; + // Handle top-level items with type: 'tabs' (2-level hierarchy: parent → tab item) + if (parent.type === 'tabs') { + for (const child of parent.children) { + if (child.key === path) { + return { + currentItem: child, + parentItem: parent, + showTabs: true, + icon: parent.icon, + }; + } + } + } + for (const child of parent.children) { if (child.children) { for (const grandChild of child.children) { @@ -30,7 +44,7 @@ export const findMenuByPath = (path: string, menuList: MenuItem[]): MenuMatchRes currentItem: grandChild, parentItem: child, showTabs: child.type === 'tabs', - icon: parent?.icon, + icon: parent.icon, }; } } diff --git a/src/components/TableActionDropdown/index.tsx b/src/components/TableActionDropdown/index.tsx new file mode 100644 index 000000000..6d1c2112a --- /dev/null +++ b/src/components/TableActionDropdown/index.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { Button } from 'antd'; +import type { ButtonProps } from 'antd/lib/button'; +import { + CheckCircle, + Copy, + ExternalLink, + Eye, + Link as LinkIcon, + MoreVertical, + Network, + Pencil, + Play, + Search, + Settings, + ShieldCheck, + Sparkles, + Trash2, +} from 'lucide-react'; +import classNames from 'classnames'; +import { Link, LinkProps } from 'react-router-dom'; + +import './style.less'; + +const tableActionIconMap = { + default: CheckCircle, + edit: Pencil, + view: Eye, + settings: Settings, + access: Network, + permission: ShieldCheck, + copy: Copy, + delete: Trash2, + run: Play, + search: Search, + open: ExternalLink, + link: LinkIcon, + ai: Sparkles, +}; + +export type TableActionIconName = keyof typeof tableActionIconMap; + +export function TableActionIcon({ name }: { name: TableActionIconName }) { + const Icon = tableActionIconMap[name]; + return ; +} + +interface TableActionButtonProps extends Omit { + actionIcon?: TableActionIconName; + icon?: React.ReactNode; +} + +export function TableActionButton({ actionIcon, icon, className, type = 'link', ...rest }: TableActionButtonProps) { + return ( + ); } diff --git a/src/components/pageLayout/PageLayoutWithTabs/index.tsx b/src/components/pageLayout/PageLayoutWithTabs/index.tsx index e8875c6de..b2c8b8e56 100644 --- a/src/components/pageLayout/PageLayoutWithTabs/index.tsx +++ b/src/components/pageLayout/PageLayoutWithTabs/index.tsx @@ -54,7 +54,7 @@ interface IPageLayoutProps { } const DEFAULT_DOCUMENT_URL_ENT = '/docs/content/flashcat/overview/'; -const DEFAULT_DOCUMENT_URL = 'https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/introduction/?ask_ai=1'; +const DEFAULT_DOCUMENT_URL = 'https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/prologue/introduction/'; const PageLayout: React.FC = ({ icon, title, rightArea, introIcon, children, customArea, showBack, backPath, doc, tabGroup }) => { const { t, i18n } = useTranslation('pageLayout'); @@ -130,20 +130,20 @@ const PageLayout: React.FC = ({ icon, title, rightArea, introI
{introIcon}
- {rightArea} {!IS_ENT && !IS_PLUS && ( )} + + {rightArea} {!IS_ENT && !IS_PLUS && ( - )} - diff --git a/src/locales/common/locale/en_US.ts b/src/locales/common/locale/en_US.ts index bcc92ffc6..0b421061a 100644 --- a/src/locales/common/locale/en_US.ts +++ b/src/locales/common/locale/en_US.ts @@ -18,6 +18,7 @@ const en_US = { unit: 'Unit', page_help: 'Help', document_title: 'Documentation', + product_document_title: 'Product documentation', more_document_link: 'More documents', and: 'And', yes: 'Yes', @@ -172,6 +173,8 @@ const en_US = { disabling: 'Disabling', select_event: 'Select an alert event', click_to_view_doc: 'Click to view documentation', + tags_popover_title: '{{count}} tags', + format_sql: 'Format SQL', tpl: 'Self-healing', 'tpl.create': 'Create', diff --git a/src/locales/common/locale/ja_JP.ts b/src/locales/common/locale/ja_JP.ts index 0d7a66500..69e66588f 100644 --- a/src/locales/common/locale/ja_JP.ts +++ b/src/locales/common/locale/ja_JP.ts @@ -18,6 +18,7 @@ const ja_JP = { unit: '単位', page_help: '使用説明', document_title: 'ドキュメント', + product_document_title: '製品ドキュメント', more_document_link: 'さらにドキュメント', and: 'および', yes: 'はい', @@ -172,6 +173,8 @@ const ja_JP = { disabling: '無効化中', select_event: 'アラートイベントを選択', click_to_view_doc: 'クリックしてドキュメントを表示します', + tags_popover_title: '{{count}} タグ', + format_sql: 'SQLをフォーマット', tpl: '自己修復スクリプト', 'tpl.create': '作成', diff --git a/src/locales/common/locale/ru_RU.ts b/src/locales/common/locale/ru_RU.ts index 1ea77043a..1b5fe14a4 100644 --- a/src/locales/common/locale/ru_RU.ts +++ b/src/locales/common/locale/ru_RU.ts @@ -18,6 +18,7 @@ const ru_RU = { unit: 'Единица', page_help: 'Инструкция', document_title: 'Документация', + product_document_title: 'Документация продукта', more_document_link: 'Больше документов', and: 'И', yes: 'Да', @@ -172,6 +173,8 @@ const ru_RU = { disabling: 'Отключение', select_event: 'Выбрать событие оповещения', click_to_view_doc: 'Нажмите, чтобы просмотреть документацию', + tags_popover_title: '{{count}} тегов', + format_sql: 'Форматировать SQL', tpl: 'Скрипт самовосстановления', 'tpl.create': 'Создать', diff --git a/src/locales/common/locale/zh_CN.ts b/src/locales/common/locale/zh_CN.ts index 19579b0cd..0ff7b0d51 100644 --- a/src/locales/common/locale/zh_CN.ts +++ b/src/locales/common/locale/zh_CN.ts @@ -18,6 +18,7 @@ const zh_CN = { unit: '单位', page_help: '使用说明', document_title: '说明文档', + product_document_title: '产品文档', more_document_link: '更多文档', and: '且', yes: '是', @@ -171,6 +172,8 @@ const zh_CN = { disabling: '禁用中', select_event: '选择告警事件', click_to_view_doc: '点击查看文档', + tags_popover_title: '{{count}} 个标签', + format_sql: '格式化 SQL', // 临时把 task 的翻译放在这里 tpl: '自愈脚本', diff --git a/src/locales/common/locale/zh_HK.ts b/src/locales/common/locale/zh_HK.ts index 655be7af8..4e7cb41fa 100644 --- a/src/locales/common/locale/zh_HK.ts +++ b/src/locales/common/locale/zh_HK.ts @@ -18,6 +18,7 @@ const zh_HK = { unit: '單位', page_help: '使用說明', document_title: '說明文檔', + product_document_title: '產品文檔', more_document_link: '更多文檔', and: '且', yes: '是', @@ -171,6 +172,8 @@ const zh_HK = { disabling: '禁用中', select_event: '選擇告警事件', click_to_view_doc: '點擊查看文檔', + tags_popover_title: '{{count}} 個標籤', + format_sql: '格式化 SQL', tpl: '自愈腳本', 'tpl.create': '創建', diff --git a/src/main.tsx b/src/main.tsx index e58aff816..4f871bd73 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,6 +16,7 @@ */ import React from 'react'; import ReactDOM from 'react-dom'; +import { Table } from 'antd'; import { i18nInit } from './i18n'; // loaded and initialized first import App from './App'; import { I18nextProvider } from 'react-i18next'; @@ -24,6 +25,9 @@ import { initTheme } from './utils/darkMode'; // 在页面渲染前初始化主题,避免样式闪烁 initTheme(); +// 2026-05 table 设计规范:点击循环改为 降序 → 升序 → 取消 +Table.defaultProps = { ...Table.defaultProps, sortDirections: ['descend', 'ascend'] }; + ReactDOM.render( diff --git a/src/pages/aiConfig/agents/pages/List.tsx b/src/pages/aiConfig/agents/pages/List.tsx index a170f675e..d04a4ab4e 100644 --- a/src/pages/aiConfig/agents/pages/List.tsx +++ b/src/pages/aiConfig/agents/pages/List.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, Space, Switch, Table, Tooltip, Modal, message } from 'antd'; -import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; +import { Button, Dropdown, Menu, Space, Switch, Table, Tooltip, Modal, message } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; import { useRequest } from 'ahooks'; import PageLayout from '@/components/pageLayout'; @@ -11,6 +11,7 @@ import { NS } from '../constants'; import { getList, deleteItem, putItem } from '../services'; import AddDrawer from './AddDrawer'; import EditDrawer from './EditDrawer'; +import { TableActionButton, TableActionTrigger } from '@/components/TableActionDropdown'; export default function List() { const { t } = useTranslation(NS); @@ -97,43 +98,57 @@ export default function List() { }, { title: t('common:table.operations'), - width: 100, + width: 64, + fixed: 'right' as const, render: (record) => { return ( - - + )} + {(IS_PLUS || !_.includes(['firemap', 'northstar'], record?.rule_prod)) && } - + } > -
); diff --git a/src/pages/alertCurEvent/pages/List/index.tsx b/src/pages/alertCurEvent/pages/List/index.tsx index 3cc49d98c..9c9436655 100644 --- a/src/pages/alertCurEvent/pages/List/index.tsx +++ b/src/pages/alertCurEvent/pages/List/index.tsx @@ -1,6 +1,7 @@ import React, { useContext, useState, useMemo, useEffect, useRef, useCallback } from 'react'; -import { Input, Checkbox, Collapse, Segmented, Button, Space, Row, Col } from 'antd'; +import { Input, Checkbox, Collapse, Segmented, Button, Space, Tooltip } from 'antd'; import { AlertOutlined, SearchOutlined } from '@ant-design/icons'; +import { ListChevronsDownUp, ListChevronsUpDown } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import _ from 'lodash'; import queryString from 'query-string'; @@ -109,6 +110,7 @@ const AlertCurEvent: React.FC = () => { ); const [refreshFlag, setRefreshFlag] = useState(_.uniqueId('refresh_')); const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [eventColumnExpanded, setEventColumnExpanded] = useState(false); const params = getRequestParamsByFilter(filter); type RuleCardsRequestParams = { @@ -133,10 +135,7 @@ const AlertCurEvent: React.FC = () => { return requestParams; }, [filter.aggr_rule_id, JSON.stringify(params)]); - const { - refresh: reloadRuleCards, - data: ruleCardsData, - } = useRequest( + const { refresh: reloadRuleCards, data: ruleCardsData } = useRequest( () => { // ready 会保证这里不会在 undefined 时执行 return getAlertCards(ruleCardsRequestParams as RuleCardsRequestParams); @@ -188,7 +187,7 @@ const AlertCurEvent: React.FC = () => { }, [JSON.stringify(filter)]); return ( - } title={t('title')}> + } title={t('title')} doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/events/cur-events/'>
@@ -323,7 +322,20 @@ const AlertCurEvent: React.FC = () => { }} >
- +
+ + +
@@ -381,6 +393,7 @@ const AlertCurEvent: React.FC = () => { setSelectedRowKeys={setSelectedRowKeys} params={params} setRefreshFlag={setRefreshFlag} + eventColumnExpanded={eventColumnExpanded} />
diff --git a/src/pages/alertCurEvent/style.less b/src/pages/alertCurEvent/style.less index c8bfcfaa4..c059a69cd 100644 --- a/src/pages/alertCurEvent/style.less +++ b/src/pages/alertCurEvent/style.less @@ -108,6 +108,75 @@ text-overflow: unset !important; } + .alert-event-summary-toolbar { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + + > :first-child { + flex: 1; + min-width: 0; + } + } + + .alert-event-expand-btn { + flex: none; + width: 28px; + height: 28px; + padding: 0; + + .ant-btn-icon { + display: inline-flex; + align-items: center; + justify-content: center; + } + } + + .alert-event-content { + min-width: 0; + } + + .alert-event-title { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .alert-event-tags { + display: flex; + gap: 4px; + + &.is-expanded { + flex-wrap: wrap; + } + + &.is-collapsed { + flex-wrap: nowrap; + overflow: hidden; + } + + .ant-tag { + display: inline-flex; + align-items: center; + max-width: 240px; + margin-right: 0; + margin-bottom: 0; + + > div { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + + .alert-event-more-tag { + flex: none; + color: var(--fc-text-3); + background: var(--fc-fill-2-5); + } + } + .ant-table table { border-collapse: collapse !important; @@ -123,4 +192,4 @@ border-left: 10px solid #23252b !important; } } -} \ No newline at end of file +} diff --git a/src/pages/alertRules/Form/Base.tsx b/src/pages/alertRules/Form/Base.tsx index f0ff6ab37..6d4e68dd8 100644 --- a/src/pages/alertRules/Form/Base.tsx +++ b/src/pages/alertRules/Form/Base.tsx @@ -37,7 +37,7 @@ export default function Base() { title={ {t('basic_configs')} - + } > diff --git a/src/pages/alertRules/Form/Effective/index.tsx b/src/pages/alertRules/Form/Effective/index.tsx index 4ba32253f..ed9a76edd 100644 --- a/src/pages/alertRules/Form/Effective/index.tsx +++ b/src/pages/alertRules/Form/Effective/index.tsx @@ -82,7 +82,7 @@ export default function index({ initialValues }: { initialValues: any }) { > {t('effective_configs')} {collapsed ? : } - {!collapsed && } + {!collapsed && } } diff --git a/src/pages/alertRules/Form/Notify/index.tsx b/src/pages/alertRules/Form/Notify/index.tsx index 175e860e4..2c3b27041 100644 --- a/src/pages/alertRules/Form/Notify/index.tsx +++ b/src/pages/alertRules/Form/Notify/index.tsx @@ -207,7 +207,9 @@ export default function index({ disabled }) { }} + components={{ + a: , + }} /> } overlayClassName='ant-tooltip-max-width-600 ant-tooltip-with-link' diff --git a/src/pages/alertRules/Form/PipelineConfigsNG/Processor.tsx b/src/pages/alertRules/Form/PipelineConfigsNG/Processor.tsx index 49ded05ce..2220c33e9 100644 --- a/src/pages/alertRules/Form/PipelineConfigsNG/Processor.tsx +++ b/src/pages/alertRules/Form/PipelineConfigsNG/Processor.tsx @@ -110,7 +110,7 @@ export default function Processor(props: Props) { } > { + setFilterDisabled(val); + saveFilter({ disabled: val }); + }} + /> {businessGroup.isLeaf && gids !== '-2' && (
diff --git a/src/pages/recordingRules/index.tsx b/src/pages/recordingRules/index.tsx index a6a607746..8431ac8ac 100644 --- a/src/pages/recordingRules/index.tsx +++ b/src/pages/recordingRules/index.tsx @@ -18,7 +18,7 @@ const Strategy: React.FC = () => { const [gids, setGids] = useState(getDefaultGids(N9E_GIDS_LOCALKEY, businessGroup)); return ( - }> + } doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/data-query/metrics/recording-rules/'>
diff --git a/src/pages/recordingRules/locale/en_US.ts b/src/pages/recordingRules/locale/en_US.ts index 1b5199fe0..8fc7e1799 100644 --- a/src/pages/recordingRules/locale/en_US.ts +++ b/src/pages/recordingRules/locale/en_US.ts @@ -13,6 +13,11 @@ const en_US = { append_tags_msg1: 'Tag length should be less than or equal to 64 bits', append_tags_msg2: 'Tag format should be key=value. And the key starts with a letter or underscore, and is composed of letters, numbers and underscores.', append_tags_placeholder: 'Tag format is key=value, use Enter or Space to separate', + filter_disabled: { + placeholder: 'Enabled status', + 0: 'Enabled', + 1: 'Disabled', + }, batch: { must_select_one: 'No rule selected', import: { diff --git a/src/pages/recordingRules/locale/ja_JP.ts b/src/pages/recordingRules/locale/ja_JP.ts index 24adfe0a9..ccecc6142 100644 --- a/src/pages/recordingRules/locale/ja_JP.ts +++ b/src/pages/recordingRules/locale/ja_JP.ts @@ -13,6 +13,11 @@ const ja_JP = { append_tags_msg1: 'タグの長さは 64 文字以下にしてください', append_tags_msg2: 'タグの形式は key=value です。key はアルファベットまたはアンダースコアで始まり、アルファベット、数字、アンダースコアで構成されます', append_tags_placeholder: 'タグの形式は key=value です。改行または空白で区切ってください', + filter_disabled: { + placeholder: 'フィルターを選択', + 0: '有効', + 1: '無効', + }, batch: { must_select_one: 'どのルールも選択されていません', import: { diff --git a/src/pages/recordingRules/locale/ru_RU.ts b/src/pages/recordingRules/locale/ru_RU.ts index bd7a46bca..9badcb031 100644 --- a/src/pages/recordingRules/locale/ru_RU.ts +++ b/src/pages/recordingRules/locale/ru_RU.ts @@ -13,6 +13,11 @@ const ru_RU = { append_tags_msg1: 'Длина тега должна быть не более 64 символов', append_tags_msg2: 'Формат тега должен быть key=value. key должен начинаться с буквы или подчеркивания и состоять из букв, цифр и подчеркиваний.', append_tags_placeholder: 'Формат тега key=value, используйте Enter или пробел для разделения', + filter_disabled: { + placeholder: 'Статус активации', + 0: 'Активно', + 1: 'Не активно', + }, batch: { must_select_one: 'Не выбрано ни одно правило', import: { diff --git a/src/pages/recordingRules/locale/zh_CN.ts b/src/pages/recordingRules/locale/zh_CN.ts index 5483cff39..34cb3fbb8 100644 --- a/src/pages/recordingRules/locale/zh_CN.ts +++ b/src/pages/recordingRules/locale/zh_CN.ts @@ -13,6 +13,11 @@ const zh_CN = { append_tags_msg1: '标签长度应小于等于 64 位', append_tags_msg2: '标签格式应为 key=value。且 key 以字母或下划线开头,由字母、数字和下划线组成。', append_tags_placeholder: '标签格式为 key=value ,使用回车或空格分隔', + filter_disabled: { + placeholder: '启用状态', + 0: '启用', + 1: '未启用', + }, batch: { must_select_one: '未选择任何规则', import: { diff --git a/src/pages/recordingRules/locale/zh_HK.ts b/src/pages/recordingRules/locale/zh_HK.ts index 235390090..d10c1ec6f 100644 --- a/src/pages/recordingRules/locale/zh_HK.ts +++ b/src/pages/recordingRules/locale/zh_HK.ts @@ -13,6 +13,11 @@ const zh_HK = { append_tags_msg1: '標籤長度應小於等於 64 位', append_tags_msg2: '標籤格式應為 key=value。且 key 以字母或下劃線開頭,由字母、數字和下劃線組成。', append_tags_placeholder: '標籤格式為 key=value ,使用回車或空格分隔', + filter_disabled: { + placeholder: '啟用狀態', + 0: '啟用', + 1: '未啟用', + }, batch: { must_select_one: '未選擇任何規則', import: { diff --git a/src/pages/siteSettings/index.tsx b/src/pages/siteSettings/index.tsx index 20724fea1..5ea890f6d 100644 --- a/src/pages/siteSettings/index.tsx +++ b/src/pages/siteSettings/index.tsx @@ -31,7 +31,7 @@ export default function index() { }, []); return ( - {t('title')}} doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/system-configuration/site-settings/'> + {t('title')}} doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/system-configuration/site-settings/'>
diff --git a/src/pages/targets/List.tsx b/src/pages/targets/List.tsx index 19c6745ed..c8eba3df1 100644 --- a/src/pages/targets/List.tsx +++ b/src/pages/targets/List.tsx @@ -20,6 +20,7 @@ import TargetMetaDrawer from './TargetMetaDrawer'; import Explorer from './components/Explorer'; import EditBusinessGroups from './components/EditBusinessGroups'; import HostsSelect from './components/HostsSelect'; +import Tags from '@/components/TableTags/Tags'; // @ts-ignore import CollectsDrawer from 'plus:/pages/collects/CollectsDrawer'; @@ -46,8 +47,9 @@ export interface ITargetProps { id: number; cluster: string; group_id: number; - group_objs: object[] | null; + group_objs: { name: string }[] | null; ident: string; + host_ip?: string; note: string; tags: string[]; beat_time: number; @@ -96,7 +98,7 @@ export default function List(props: IProps) { { title: ( - {t('common:table.ident')} + {t('host_ip')} { + render: (text, record: ITargetProps) => { + const groupNames = _.map(record.group_objs, 'name'); return ( -
- - {import.meta.env['VITE_IS_PRO'] && ( - - { - setCollectsDrawerVisible(true); - setCollectsDrawerIdent(text); - }} - /> - - )} +
+
+ + {import.meta.env['VITE_IS_PRO'] && ( + + { + setCollectsDrawerVisible(true); + setCollectsDrawerIdent(text); + }} + /> + + )} +
+ + {record.host_ip && record.host_ip !== text && {text}} + {!_.isEmpty(groupNames) ? {groupNames.join(' / ')} : {t('common:not_grouped')}} + {record.note && {record.note}} +
); }, @@ -178,13 +188,7 @@ export default function List(props: IProps) { _.forEach(columnsConfigs, (item) => { if (!item.visible) return; - if (item.name === 'host_ip') { - columns.push({ - title: t('host_ip'), - dataIndex: 'host_ip', - className: 'n9e-hosts-table-column-ip', - }); - } + if (item.name === 'host_ip' || item.name === 'group_obj') return; if (item.name === 'host_tags') { columns.push({ title: ( @@ -201,30 +205,19 @@ export default function List(props: IProps) { showTitle: false, }, render(tagArr) { - const content = - tagArr && - tagArr.map((item) => ( - { - if (!tableQueryContent.includes(item)) { - isAddTagToQueryInput.current = true; - const val = tableQueryContent ? `${tableQueryContent.trim()} ${item}` : item; - setTableQueryContent(val); - setSearchVal(val); - } - }} - > - {item} - - )); return ( - tagArr && ( - document.body} overlayClassName='mon-manage-table-tooltip'> - {content} - - ) + + data={tagArr} + maxWidth={180} + onTagClick={(item) => { + if (!tableQueryContent.includes(item)) { + isAddTagToQueryInput.current = true; + const val = tableQueryContent ? `${tableQueryContent.trim()} ${item}` : item; + setTableQueryContent(val); + setSearchVal(val); + } + }} + /> ); }, }); @@ -273,33 +266,6 @@ export default function List(props: IProps) { }, }); } - if (item.name === 'group_obj') { - columns.push({ - title: t('group_obj'), - dataIndex: 'group_objs', - className: 'n9e-hosts-table-column-tags', - ellipsis: { - showTitle: false, - }, - render(tagArr) { - if (_.isEmpty(tagArr)) return t('common:not_grouped'); - const content = - tagArr && - tagArr.map((item) => ( - - {item.name} - - )); - return ( - tagArr && ( - document.body}> - {content} - - ) - ); - }, - }); - } if (item.name === 'mem_util') { columns.push({ title: t('mem_util'), diff --git a/src/pages/targets/components/Explorer/ExplorerPanels/Panel.tsx b/src/pages/targets/components/Explorer/ExplorerPanels/Panel.tsx index 143031ff3..0129eacf2 100644 --- a/src/pages/targets/components/Explorer/ExplorerPanels/Panel.tsx +++ b/src/pages/targets/components/Explorer/ExplorerPanels/Panel.tsx @@ -6,6 +6,7 @@ import { CloseCircleOutlined } from '@ant-design/icons'; import PrometheusExplorer from '@/pages/explorer/Prometheus'; import InputGroupWithFormItem from '@/components/InputGroupWithFormItem'; import EmptyDatasourcePopover from '@/components/DatasourceSelect/EmptyDatasourcePopover'; +import { getPrimaryTypeByCate } from '@/components/AdvancedWrap/utils'; import { IRawTimeRange } from '@/components/TimeRangePicker'; import { getDefaultDatasourceValue, setDefaultDatasourceValue } from '@/utils'; import { CommonStateContext } from '@/App'; @@ -61,7 +62,7 @@ export default function Panel(props: Props) { {({ getFieldValue }) => { const cate = getFieldValue('datasourceCate'); return ( - + { }, [businessGroup.ids]); return ( - } title={t('title')} doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/infrastructure/server-list/'> + } title={t('title')} doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/infrastructure/server-list/'>
('middle'); return ( diff --git a/src/pages/task/add.tsx b/src/pages/task/add.tsx index db7ee0be1..5a0fd751d 100644 --- a/src/pages/task/add.tsx +++ b/src/pages/task/add.tsx @@ -101,6 +101,7 @@ const Add = (props: any) => { ) } + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/create-temporary-task/' >
diff --git a/src/pages/task/index.tsx b/src/pages/task/index.tsx index 0f7f7a26c..80eef6ffc 100644 --- a/src/pages/task/index.tsx +++ b/src/pages/task/index.tsx @@ -16,13 +16,14 @@ */ import React, { useContext, useState, useEffect } from 'react'; import { Link, useHistory } from 'react-router-dom'; -import { Table, Divider, Checkbox, Row, Col, Select, Button, Space } from 'antd'; +import { Table, Checkbox, Row, Col, Select, Button, Space, Dropdown, Menu } from 'antd'; import { CodeOutlined } from '@ant-design/icons'; import { ColumnProps } from 'antd/lib/table'; import _ from 'lodash'; import moment from 'moment'; import { useTranslation } from 'react-i18next'; import { useAntdTable } from 'ahooks'; +import { TableActionButton, TableActionLink, TableActionTrigger } from '@/components/TableActionDropdown'; import request from '@/utils/request'; import api from '@/utils/api'; @@ -118,57 +119,81 @@ const index = (_props: any) => { }); }; + const showBusinessGroup = !(businessGroup.isLeaf && gids !== '-2'); const columns: ColumnProps[] = _.concat( - businessGroup.isLeaf && gids !== '-2' - ? [] - : ([ - { - title: t('common:business_group'), - dataIndex: 'group_id', - width: 100, - render: (id) => { - return _.find(busiGroups, { id })?.name; - }, - }, - ] as any), [ - { - title: 'ID', - dataIndex: 'id', - width: 100, - }, { title: t('task.title'), dataIndex: 'title', - width: 200, + width: 240, render: (text, record) => { - return {text}; - }, - }, - { - title: t('table.operations'), - width: 150, - render: (_text, record) => { + const groupName = _.find(busiGroups, { id: record.group_id })?.name; return ( - - {t('task.clone')} - - handleOpenMetaDrawer(record)}>{t('task.meta')} - +
+ {text} + + ID: {record.id} + {showBusinessGroup && groupName && {groupName}} + +
); }, }, + ] as any, + [ { title: t('task.creator'), dataIndex: 'create_by', width: 100, + render: (val, record: any) => ( +
+
{val}
+ {record.create_by_nickname &&
{record.create_by_nickname}
} +
+ ), }, { title: t('task.created'), dataIndex: 'create_at', width: 160, render: (text) => { - return moment.unix(text).format('YYYY-MM-DD HH:mm:ss'); + const m = moment.unix(text); + return ( +
+
{m.format('YYYY-MM-DD')}
+
{m.format('HH:mm:ss')}
+
+ ); + }, + }, + { + title: t('table.operations'), + width: 64, + fixed: 'right' as const, + render: (_text, record) => { + return ( + + + + {t('task.clone')} + + + + handleOpenMetaDrawer(record)}> + {t('task.meta')} + + + + } + > + + + ); }, }, ], @@ -178,7 +203,7 @@ const index = (_props: any) => { } title={{t('task')}} - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/self-healing/create-temporary-task/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/create-temporary-task/' >
diff --git a/src/pages/taskTpl/add.tsx b/src/pages/taskTpl/add.tsx index 1e09fa68e..f1be0d37e 100644 --- a/src/pages/taskTpl/add.tsx +++ b/src/pages/taskTpl/add.tsx @@ -54,7 +54,7 @@ const Add = (props: any) => { {t('tpl')} } - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/self-healing/self-healing-script/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/self-healing-script/' >
diff --git a/src/pages/taskTpl/clone.tsx b/src/pages/taskTpl/clone.tsx index 3e30a9e9e..fce37bf4c 100644 --- a/src/pages/taskTpl/clone.tsx +++ b/src/pages/taskTpl/clone.tsx @@ -75,7 +75,7 @@ const Add = (props: any) => { {t('tpl')} } - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/self-healing/self-healing-script/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/self-healing-script/' >
diff --git a/src/pages/taskTpl/detail.tsx b/src/pages/taskTpl/detail.tsx index 639b22a0e..673c74319 100644 --- a/src/pages/taskTpl/detail.tsx +++ b/src/pages/taskTpl/detail.tsx @@ -65,7 +65,7 @@ const Detail = (props: any) => { {t('tpl')} } - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/self-healing/self-healing-script/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/self-healing-script/' >
diff --git a/src/pages/taskTpl/index.tsx b/src/pages/taskTpl/index.tsx index 6cc933572..91fd21c2c 100644 --- a/src/pages/taskTpl/index.tsx +++ b/src/pages/taskTpl/index.tsx @@ -16,13 +16,15 @@ */ import React, { useState, useContext, useEffect } from 'react'; import { Link } from 'react-router-dom'; -import { Table, Divider, Popconfirm, Tag, Row, Col, Button, Dropdown, Menu, message, Space } from 'antd'; +import { Table, Modal, Tag, Row, Col, Button, Dropdown, Menu, message, Space } from 'antd'; import { DownOutlined, CodeOutlined } from '@ant-design/icons'; import { ColumnProps } from 'antd/lib/table'; import _ from 'lodash'; import moment from 'moment'; import { useAntdTable } from 'ahooks'; import { useTranslation } from 'react-i18next'; +import { TableActionButton, TableActionLink, TableActionTrigger } from '@/components/TableActionDropdown'; +import Tags from '@/components/TableTags/Tags'; import request from '@/utils/request'; import { RequestMethod } from '@/store/common'; @@ -112,81 +114,115 @@ const index = (_props: any) => { } } + const showBusinessGroup = !(businessGroup.isLeaf && gids !== '-2'); const columns: ColumnProps[] = _.concat( - businessGroup.isLeaf && gids !== '-2' - ? [] - : ([ - { - title: t('common:business_group'), - dataIndex: 'group_id', - width: 100, - render: (id) => { - return _.find(busiGroups, { id })?.name; - }, - }, - ] as any), [ - { - title: 'ID', - dataIndex: 'id', - }, { title: t('tpl.title'), dataIndex: 'title', + width: 360, render: (text, record) => { - return {text}; + const groupName = _.find(busiGroups, { id: record.group_id })?.name; + return ( +
+ {text} + + ID: {record.id} + {showBusinessGroup && groupName && {groupName}} + +
+ ); }, }, + ] as any, + [ { title: t('tpl.tags'), dataIndex: 'tags', + width: 280, render: (text) => { - return _.map(text, (item) => ( - handleTagClick(item)}> - {item} - - )); + return ; }, }, { title: t('tpl.creator'), dataIndex: 'create_by', - width: 100, + width: 120, + render: (val, record: any) => ( +
+
{val}
+ {record.create_by_nickname &&
{record.create_by_nickname}
} +
+ ), }, { title: t('tpl.last_updated'), dataIndex: 'update_at', - width: 160, + width: 180, render: (text) => { - return moment.unix(text).format('YYYY-MM-DD HH:mm:ss'); + const m = moment.unix(text); + return ( +
+
{m.format('YYYY-MM-DD')}
+
{m.format('HH:mm:ss')}
+
+ ); }, }, { title: t('table.operations'), - width: 220, + width: 64, + fixed: 'right' as const, render: (_text, record) => { return ( - - {t('task.create')} - - {t('common:btn.edit')} - - {t('common:btn.clone')} - - {t('common:confirm.delete')}
} - onConfirm={() => { - request(`${api.tasktpl(record.group_id)}/${record.id}`, { - method: 'DELETE', - }).then(() => { - message.success(t('msg.delete.success')); - refresh(); - }); - }} - > -
{t('common:btn.delete')} - - + + + + {t('task.create')} + + + + + {t('common:btn.edit')} + + + + + {t('common:btn.clone')} + + + + + { + Modal.confirm({ + title: t('common:confirm.delete'), + onOk: () => { + return request(`${api.tasktpl(record.group_id)}/${record.id}`, { + method: 'DELETE', + }).then(() => { + message.success(t('msg.delete.success')); + refresh(); + }); + }, + }); + }} + > + {t('common:btn.delete')} + + + + } + > + + ); }, }, @@ -197,12 +233,12 @@ const index = (_props: any) => { } title={{t('tpl')}} - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/self-healing/self-healing-script/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/self-healing-script/' >
{gids ? ( -
+
{ { ...tableProps.pagination, showSizeChanger: true, - pageSizeOptions: ['10', '50', '100', '500', '1000'], + pageSizeOptions: ['10', '15', '50', '100', '500', '1000'], showTotal: (total) => { return i18n.language == 'en' ? `Total ${total} items` : `共 ${total} 条`; }, diff --git a/src/pages/taskTpl/modify.tsx b/src/pages/taskTpl/modify.tsx index 037b1897b..f8379b5ad 100644 --- a/src/pages/taskTpl/modify.tsx +++ b/src/pages/taskTpl/modify.tsx @@ -74,7 +74,7 @@ const Modify = (props: any) => { {t('tpl')} } - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/self-healing/self-healing-script/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/self-healing/self-healing-script/' >
diff --git a/src/pages/user/business.tsx b/src/pages/user/business.tsx index c41161a62..ddc1ebdc5 100644 --- a/src/pages/user/business.tsx +++ b/src/pages/user/business.tsx @@ -19,13 +19,14 @@ import moment from 'moment'; import _ from 'lodash'; import classNames from 'classnames'; import PageLayout, { HelpLink } from '@/components/pageLayout'; -import { Button, Table, Input, message, Row, Col, Modal, Space } from 'antd'; +import { Button, Table, Input, message, Row, Col, Modal, Space, Dropdown, Menu } from 'antd'; import { EditOutlined, DeleteOutlined, SearchOutlined, UserOutlined, InfoCircleOutlined } from '@ant-design/icons'; import UserInfoModal from './component/createModal'; import { deleteBusinessTeamMember, getBusinessTeamList, getBusinessTeamInfo, deleteBusinessTeam } from '@/services/manage'; import { Team, ActionType } from '@/store/manageInterface'; import { CommonStateContext } from '@/App'; import { ColumnsType } from 'antd/lib/table'; +import { TableActionButton, TableActionTrigger } from '@/components/TableActionDropdown'; import { useTranslation } from 'react-i18next'; import { useQuery } from '@/utils'; import { listToTree, getCollapsedKeys, getLocaleExpandedKeys, setLocaleExpandedKeys, getDefaultBusiness } from '@/components/BusinessGroup'; @@ -69,35 +70,49 @@ const Resource: React.FC = () => { }, { title: t('common:table.operations'), - width: '100px', + width: 64, + fixed: 'right' as const, render: (text: string, record) => ( - + + ), }, ]; @@ -169,7 +184,7 @@ const Resource: React.FC = () => { {t('business.title')}} icon={} - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/personnel-permissions/business-group/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/personnel-permissions/business-group/' >
diff --git a/src/pages/user/component/Tags/index.tsx b/src/pages/user/component/Tags/index.tsx deleted file mode 100644 index f916d686c..000000000 --- a/src/pages/user/component/Tags/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import _ from 'lodash'; -import { Tag, Tooltip } from 'antd'; -import { Link } from 'react-router-dom'; - -interface Props { - data: { id: number; name: string }[]; - tagLinkTo: (item) => { - pathname: string; - search: string; - }; -} - -export default function index(props: Props) { - const { data, tagLinkTo } = props; - const displayData = _.slice(data, 0, 3); - return ( - -
- {_.map(displayData, (item) => { - return ( - - -
- {item.name} -
-
- - ); - })} - {data.length > 2 && ( -
- ... -
- )} -
-
- ); -} diff --git a/src/pages/user/constants.ts b/src/pages/user/constants.ts index a5ecc63a7..1426f0667 100644 --- a/src/pages/user/constants.ts +++ b/src/pages/user/constants.ts @@ -24,21 +24,6 @@ export const defaultColumnsConfigs = [ i18nKey: 'account:profile.username', visible: true, }, - { - name: 'nickname', - i18nKey: 'account:profile.nickname', - visible: true, - }, - { - name: 'email', - i18nKey: 'account:profile.email', - visible: false, - }, - { - name: 'phone', - i18nKey: 'account:profile.phone', - visible: false, - }, { name: 'roles', i18nKey: 'account:profile.role', diff --git a/src/pages/user/groups.tsx b/src/pages/user/groups.tsx index 86ee61915..ac636cac9 100644 --- a/src/pages/user/groups.tsx +++ b/src/pages/user/groups.tsx @@ -20,16 +20,18 @@ import _ from 'lodash'; import { useLocation } from 'react-router-dom'; import queryString from 'query-string'; import PageLayout, { HelpLink } from '@/components/pageLayout'; -import { Button, Table, Input, message, List, Row, Col, Modal, Space, Tag } from 'antd'; +import { Button, Table, Input, message, List, Row, Col, Modal, Space, Tag, Dropdown, Menu } from 'antd'; import { EditOutlined, DeleteOutlined, SearchOutlined, UserOutlined, InfoCircleOutlined, PlusSquareOutlined } from '@ant-design/icons'; import UserInfoModal from './component/createModal'; import { getTeamInfoList, getTeamInfo, deleteTeam, deleteMember } from '@/services/manage'; import { User, Team, UserType, ActionType, TeamInfo } from '@/store/manageInterface'; import { ColumnsType } from 'antd/lib/table'; +import { TableActionButton, TableActionTrigger } from '@/components/TableActionDropdown'; import { useTranslation } from 'react-i18next'; import { listToTree } from '@/components/BusinessGroup'; import { CommonStateContext } from '@/App'; import Tree from '@/components/BusinessGroup/components/Tree'; +import Tags from '@/components/TableTags/Tags'; import './index.less'; import './locale'; import usePagination from '@/components/usePagination'; @@ -102,9 +104,7 @@ const Resource: React.FC = () => { dataIndex: 'busi_groups', render: (text: string, record) => { if (_.isEmpty(record.busi_groups)) return '-'; - return _.map(record.busi_groups, (item) => { - return {item.name}; - }); + return item.id} getLabel={(item) => item.name} />; }, }, ]; @@ -113,30 +113,43 @@ const Resource: React.FC = () => { ...userColumn, { title: t('common:table.operations'), - width: '100px', + width: 64, + fixed: 'right' as const, render: (text: string, record) => ( - + + ), }, ]; @@ -235,7 +248,7 @@ const Resource: React.FC = () => { {t('team.title')}} icon={} - doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/personnel-permissions/team-management/' + doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/personnel-permissions/team-management/' >
diff --git a/src/pages/user/users.tsx b/src/pages/user/users.tsx index 47b1815e1..4a37a9ebe 100644 --- a/src/pages/user/users.tsx +++ b/src/pages/user/users.tsx @@ -18,11 +18,12 @@ import React, { useState, useContext } from 'react'; import moment from 'moment'; import _ from 'lodash'; import { Button, Input, message, Row, Modal, Table, Space, Dropdown, Menu } from 'antd'; -import { SearchOutlined, UserOutlined, EyeOutlined, MoreOutlined } from '@ant-design/icons'; +import { SearchOutlined, UserOutlined, EyeOutlined } from '@ant-design/icons'; import { ColumnsType } from 'antd/lib/table'; import { useTranslation } from 'react-i18next'; import { useAntdTable } from 'ahooks'; -import PageLayout, { HelpLink } from '@/components/pageLayout'; +import PageLayout from '@/components/pageLayout'; +import { TableActionButton, TableActionTrigger } from '@/components/TableActionDropdown'; import UserInfoModal from './component/createModal'; import { getUserInfoList, deleteUser } from '@/services/manage'; import { User, UserType, ActionType } from '@/store/manageInterface'; @@ -31,7 +32,7 @@ import usePagination from '@/components/usePagination'; import TimeRangePicker, { IRawTimeRange, parseRange } from '@/components/TimeRangePicker'; import OrganizeColumns, { getDefaultColumnsConfigs, setDefaultColumnsConfigs, ajustColumns } from '@/components/OrganizeColumns'; import { defaultColumnsConfigs, LOCAL_STORAGE_KEY } from './constants'; -import Tags from './component/Tags'; +import Tags from '@/components/TableTags/Tags'; import './index.less'; import './locale'; @@ -45,30 +46,24 @@ const Resource: React.FC = () => { const [memberId, setMemberId] = useState(''); const [query, setQuery] = useState(''); const [range, setRange] = useState(); - const { profile, perms } = useContext(CommonStateContext); + const { perms } = useContext(CommonStateContext); const pagination = usePagination({ PAGESIZE_KEY: 'users' }); const [columnsConfigs, setColumnsConfigs] = useState<{ name: string; visible: boolean }[]>(getDefaultColumnsConfigs(defaultColumnsConfigs, LOCAL_STORAGE_KEY)); const userColumn: ColumnsType = [ { title: t('account:profile.username'), dataIndex: 'username', + width: 260, ellipsis: true, - }, - { - title: t('account:profile.nickname'), - dataIndex: 'nickname', - ellipsis: true, - render: (text: string, record) => record.nickname || '-', - }, - { - title: t('account:profile.email'), - dataIndex: 'email', - render: (text: string, record) => record.email || '-', - }, - { - title: t('account:profile.phone'), - dataIndex: 'phone', - render: (text: string, record) => record.phone || '-', + sorter: true, + render: (text: string, record) => { + return ( +
+ {record.nickname || text} + {text} +
+ ); + }, }, ]; const userColumns: ColumnsType = [ @@ -76,20 +71,22 @@ const Resource: React.FC = () => { { title: t('account:profile.role'), dataIndex: 'roles', + width: 120, render: (text: []) => text.join(', '), }, { title: t('user.busi_groups'), dataIndex: 'busi_groups', - render: (value) => { + width: 160, + render: (value: { id: number; name: string }[]) => { return ( { - return { - pathname: '/busi-groups', - search: `?id=${item.id}`, - }; + maxWidth={220} + getKey={(item) => item.id} + getLabel={(item) => item.name} + onTagClick={(item) => { + if (typeof item !== 'string') window.open(`/busi-groups?id=${item.id}`, '_blank'); }} /> ); @@ -98,15 +95,16 @@ const Resource: React.FC = () => { { title: t('user.user_groups'), dataIndex: 'user_groups', - render: (value) => { + width: 160, + render: (value: { id: number; name: string }[]) => { return ( { - return { - pathname: '/user-groups', - search: `?id=${item.id}`, - }; + maxWidth={220} + getKey={(item) => item.id} + getLabel={(item) => item.name} + onTagClick={(item) => { + if (typeof item !== 'string') window.open(`/user-groups?id=${item.id}`, '_blank'); }} /> ); @@ -115,6 +113,7 @@ const Resource: React.FC = () => { { title: t('common:table.create_at'), dataIndex: 'create_at', + width: 170, render: (text) => { return moment.unix(text).format('YYYY-MM-DD HH:mm:ss'); }, @@ -123,6 +122,7 @@ const Resource: React.FC = () => { { title: t('user.last_active_time'), dataIndex: 'last_active_time', + width: 170, render: (text) => { if (!text) { return '-'; @@ -133,26 +133,27 @@ const Resource: React.FC = () => { }, { title: t('common:table.operations'), - width: i18n.language === 'en_US' || i18n.language === 'ru_RU' ? 80 : 40, + width: 64, + fixed: 'right' as const, render: (text: string, record) => { return ( {_.includes(perms, '/users/put') && ( handleClick(ActionType.EditUser, record.id)}> - + {t('common:btn.edit')} )} {_.includes(perms, '/users/put') && ( handleClick(ActionType.Reset, record.id)}> - + {t('account:password.reset')} )} + {_.includes(perms, '/users/del') && } {_.includes(perms, '/users/del') && ( { @@ -168,15 +169,15 @@ const Resource: React.FC = () => { }); }} > - + )} } > - - - { - deleteVariableConfigs(record.id).then(() => { - message.success(t('common:success.delete')); - fetchData(); - }); - }} - > - - - + + + { + FormModal({ + title: t('common:btn.clone'), + rsaConfig, + data: record, + onOk: (values) => { + return postVariableConfigs(values).then(() => { + fetchData(); + message.success(t('common:success.clone')); + }); + }, + }); + }} + > + {t('common:btn.clone')} + + + + { + FormModal({ + title: t('common:btn.edit'), + rsaConfig, + data: record, + onOk: (values) => { + return putVariableConfigs(record.id, values).then(() => { + fetchData(); + message.success(t('common:success.edit')); + }); + }, + }); + }} + > + {t('common:btn.edit')} + + + + + { + Modal.confirm({ + title: t('common:confirm.delete'), + onOk: () => { + deleteVariableConfigs(record.id).then(() => { + message.success(t('common:success.delete')); + fetchData(); + }); + }, + }); + }} + > + {t('common:btn.delete')} + + + + } + > + + ); }, }, diff --git a/src/pages/warning/shield/add.tsx b/src/pages/warning/shield/add.tsx index 5d2e87ace..2632492a3 100644 --- a/src/pages/warning/shield/add.tsx +++ b/src/pages/warning/shield/add.tsx @@ -98,7 +98,7 @@ const AddShield: React.FC = () => { }, [search]); return ( - +
{eventDetail && }
); diff --git a/src/pages/warning/shield/edit.tsx b/src/pages/warning/shield/edit.tsx index aa9d01f7d..ae8e6b22a 100644 --- a/src/pages/warning/shield/edit.tsx +++ b/src/pages/warning/shield/edit.tsx @@ -61,7 +61,7 @@ const EditShield: React.FC = () => { }, [busiId, id]); return ( - +
{!_.isEmpty(values) ? ( diff --git a/src/pages/warning/shield/index.tsx b/src/pages/warning/shield/index.tsx index 6b36d1f56..63e23ec70 100644 --- a/src/pages/warning/shield/index.tsx +++ b/src/pages/warning/shield/index.tsx @@ -15,15 +15,16 @@ * */ import React, { useState, useEffect, useContext } from 'react'; -import { Button, Input, Table, Tooltip, message, Modal, Switch, Space, Tag } from 'antd'; +import { Button, Input, Table, Tooltip, message, Modal, Switch, Space, Tag, Dropdown, Menu, Select } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { CloseCircleOutlined, ExclamationCircleOutlined, SearchOutlined } from '@ant-design/icons'; import moment from 'moment'; import _ from 'lodash'; import { useTranslation } from 'react-i18next'; import { useHistory, Link } from 'react-router-dom'; +import { TableActionButton, TableActionTrigger } from '@/components/TableActionDropdown'; -import Tags from '@/components/Tags'; +import Tags from '@/components/TableTags/Tags'; import PageLayout from '@/components/pageLayout'; import { getBusiGroupsAlertMutes, deleteShields, updateShields } from '@/services/shield'; import { shieldItem, strategyStatus } from '@/store/warningInterface'; @@ -32,7 +33,7 @@ import RefreshIcon from '@/components/RefreshIcon'; import { DatasourceSelect } from '@/components/DatasourceSelect'; import { CommonStateContext } from '@/App'; import usePagination from '@/components/usePagination'; -import { allCates } from '@/components/AdvancedWrap/utils'; +import { allCates, getCateDisplayLabel } from '@/components/AdvancedWrap/utils'; import DeleteMutesModal from './components/DeleteMutesModal'; import './locale'; @@ -47,12 +48,13 @@ const N9E_GIDS_LOCALKEY = 'n9e_mutes_gids'; interface Filter { query?: string; datasourceIds?: number[]; + disabled?: 0 | 1; } const FILTER_SESSION_STORAGE_KEY = 'alert-mutes-filter'; const Shield: React.FC = () => { - const { t } = useTranslation('alertMutes'); + const { t, i18n } = useTranslation('alertMutes'); const history = useHistory(); const { datasourceList, groupedDatasourceList, businessGroup, busiGroups } = useContext(CommonStateContext); const [gids, setGids] = useState(getDefaultGids(N9E_GIDS_LOCALKEY, businessGroup)); @@ -67,68 +69,68 @@ const Shield: React.FC = () => { const [currentShieldData, setCurrentShieldData] = useState>([]); const [loading, setLoading] = useState(false); const [datasourceIds, setDatasourceIds] = useState(defaultFilter.datasourceIds); + const [filterDisabled, setFilterDisabled] = useState<0 | 1 | undefined>(defaultFilter.disabled); const [deleteMutesModalVisible, setDeleteMutesModalVisible] = useState(false); const saveFilter = (patch: Partial) => { const prev = JSON.parse(window.sessionStorage.getItem(FILTER_SESSION_STORAGE_KEY) || '{}'); window.sessionStorage.setItem(FILTER_SESSION_STORAGE_KEY, JSON.stringify({ ...prev, ...patch })); }; + const showBusinessGroup = !(businessGroup.isLeaf && gids !== '-2'); const columns: ColumnsType = _.concat( - businessGroup.isLeaf && gids !== '-2' - ? [] - : ([ - { - title: t('common:business_group'), - dataIndex: 'group_id', - render: (id) => { - return _.find(busiGroups, { id })?.name; - }, - }, - ] as any), [ { title: t('note'), dataIndex: 'note', render: (data, record: any) => { + const groupName = _.find(busiGroups, { id: record.group_id })?.name; return ( - - {data} - +
+ + {data} + + {showBusinessGroup && groupName && {groupName}} +
); }, }, - { - title: t('common:datasource.type'), - dataIndex: 'cate', - render: (val) => { - let logoSrc = _.find(allCates, { value: val })?.logo; - if (val === 'host') { - logoSrc = '/image/logos/host.png'; - } - return {val}; - }, - }, + ] as any, + [ { title: t('common:datasource.id'), dataIndex: 'datasource_ids', render(value, record: any) { if (!value) return '-'; + const cate = _.find(allCates, { value: record.cate }); + const cateLabel = record.cate === 'host' ? 'Host' : getCateDisplayLabel(cate, i18n.language); + let logoSrc = cate?.logo; + if (record.cate === 'host') { + logoSrc = '/image/logos/host.png'; + } return ( - { - if (item === 0) return '$all'; - const name = _.find(groupedDatasourceList[record.cate], { id: item })?.name; - if (!name) return ''; - return name; - }), + + {logoSrc && ( + + {record.cate} + )} - /> + { + if (item === 0) return '$all'; + const name = _.find(groupedDatasourceList[record.cate], { id: item })?.name; + if (!name) return ''; + return name; + }), + )} + /> + ); }, }, @@ -137,17 +139,11 @@ const Shield: React.FC = () => { dataIndex: 'tags', render: (text: any) => { return ( -
- {text - ? text.map((tag, index) => { - return tag ? ( -
{`${tag.key} ${tag.func} ${ - tag.func === 'in' ? tag.value.split(' ').join(', ') : tag.value - }`}
- ) : null; - }) - : ''} -
+ (tag ? `${tag.key} ${tag.func} ${tag.func === 'in' ? tag.value.split(' ').join(', ') : tag.value}` : '')))} + /> ); }, }, @@ -237,12 +233,24 @@ const Shield: React.FC = () => { title: t('common:table.update_at'), dataIndex: 'update_at', render: (value) => { - return moment.unix(value).format('YYYY-MM-DD HH:mm:ss'); + const m = moment.unix(value); + return ( +
+
{m.format('YYYY-MM-DD')}
+
{m.format('HH:mm:ss')}
+
+ ); }, }, { title: t('common:table.update_by'), dataIndex: 'update_by', + render: (val, record: any) => ( +
+
{val}
+ {record.update_by_nickname &&
{record.update_by_nickname}
} +
+ ), }, { title: t('common:table.enabled'), @@ -273,60 +281,66 @@ const Shield: React.FC = () => { title: t('common:table.operations'), dataIndex: 'operation', fixed: 'right', + width: 64, render: (text: undefined, record: shieldItem) => { return ( - <> -
-
{ - history.push({ - pathname: `/alert-mutes/edit/${record.id}`, - search: `?mode=clone&bgid=${record.group_id}`, - }); - }} - > - {t('common:btn.clone')} -
-
{ - confirm({ - title: t('common:confirm.delete'), - icon: , - onOk: () => { - deleteShields({ ids: [record.id] }, record.group_id).then((res) => { - refreshList(); - if (res.err) { - message.success(res.err); - } else { - message.success(t('common:success.delete')); - } + + + { + history.push({ + pathname: `/alert-mutes/edit/${record.id}`, + search: `?mode=clone&bgid=${record.group_id}`, }); - }, + }} + > + {t('common:btn.clone')} + + + + + { + confirm({ + title: t('common:confirm.delete'), + icon: , + onOk: () => { + deleteShields({ ids: [record.id] }, record.group_id).then((res) => { + refreshList(); + if (res.err) { + message.success(res.err); + } else { + message.success(t('common:success.delete')); + } + }); + }, - onCancel() {}, - }); - }} - > - {t('common:btn.delete')} -
-
- + onCancel() {}, + }); + }} + > + {t('common:btn.delete')} + + + + } + > + + ); }, }, ], ); - const pagination = usePagination({ pageSizeLocalstorageKey: 'alert-mutes-table-pagesize', defaultPageSize: 30, pageSizeOptions: ['30', '50', '100', '300'] }); + const pagination = usePagination({ pageSizeLocalstorageKey: 'alert-mutes-table-pagesize' }); useEffect(() => { getList(); @@ -334,7 +348,7 @@ const Shield: React.FC = () => { useEffect(() => { filterData(); - }, [query, datasourceIds, currentShieldDataAll]); + }, [query, datasourceIds, filterDisabled, currentShieldDataAll]); const includesProm = (ids) => { return _.some(ids, (id) => { @@ -356,7 +370,7 @@ const Shield: React.FC = () => { return _.includes(datasourceIds, id); }) : true; - return (_.includes(item.note, query) || _.includes(item.cause, query) || !!tagFind) && datsourceFind; + return (_.includes(item.note, query) || _.includes(item.cause, query) || !!tagFind) && datsourceFind && (filterDisabled === undefined || item.disabled === filterDisabled); }); setCurrentShieldData(res || []); }; @@ -384,7 +398,7 @@ const Shield: React.FC = () => { }; return ( - } doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/usage/alert/alert-mute/'> + } doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usage/alert-notify/rules/alert-mute/'>
@@ -413,6 +427,19 @@ const Shield: React.FC = () => { width: 300, }} /> + { + setFilterDisabled(val); + saveFilter({ disabled: val }); + }} + /> {headerExtra} diff --git a/src/pages/warning/subscribe/add.tsx b/src/pages/warning/subscribe/add.tsx index 3a5fba0ad..49f51b9fa 100644 --- a/src/pages/warning/subscribe/add.tsx +++ b/src/pages/warning/subscribe/add.tsx @@ -23,7 +23,7 @@ import './index.less'; const AddSubscribe: React.FC = () => { const { t } = useTranslation('alertSubscribes'); return ( - + ); diff --git a/src/pages/warning/subscribe/edit.tsx b/src/pages/warning/subscribe/edit.tsx index 6a500cdc9..45c3e2f70 100644 --- a/src/pages/warning/subscribe/edit.tsx +++ b/src/pages/warning/subscribe/edit.tsx @@ -59,7 +59,7 @@ const EditSubscribe: React.FC = () => { }; return ( - + {curSubscribeData?.id && } ); diff --git a/src/pages/warning/subscribe/index.tsx b/src/pages/warning/subscribe/index.tsx index 8f0c4ddbf..32f059219 100644 --- a/src/pages/warning/subscribe/index.tsx +++ b/src/pages/warning/subscribe/index.tsx @@ -45,7 +45,7 @@ export default function List() { }, [gids, refreshFlag]); return ( - } doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v8/usecase/subscribe/'> + } doc='https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v9/usecase/subscribe/'>
{ } const query: any = target.query || {}; if (!query.query) return; - const queryStr = replaceTemplateVariables(query.query); + const queryStr = replaceTemplateVariables(query.query, { range: queryOptionsTime ?? time }); const mode = query.mode; if (target.__mode__ === '__expr__') { exps.push({ diff --git a/src/plugins/clickHouse/components/DocumentDrawer/document_zh_HK.md b/src/plugins/clickHouse/components/DocumentDrawer/document_zh_HK.md index 3965f1e14..17adac7cc 100644 --- a/src/plugins/clickHouse/components/DocumentDrawer/document_zh_HK.md +++ b/src/plugins/clickHouse/components/DocumentDrawer/document_zh_HK.md @@ -13,10 +13,10 @@ select * from database_name.table_name limit 10 1. 必須在 SQL 中使用 `as time` 的語法指定哪一列是時間列,然後 `group by time order by time desc` 對時間做排序,例如 ```sql -select count(*) as count, trigger_time as time -from n9e_v6_plus.alert_his_event -group by time -order by time +select count(*) as count, trigger_time as time +from n9e_v6_plus.alert_his_event +group by time +order by time desc LIMIT 100 ``` @@ -29,35 +29,35 @@ SELECT count(*) AS count, trigger_time AS time FROM n9e_v6_plus.alert_his_event ``` > 2.2 查詢某一個時間段的數據,trigger_time 是 unix 時間戳 (1720061167) 的情況 -> WHERE 語句可以直接寫 WHERE trigger_time >= 1720060214 AND trigger_time < 1720061214 -> 或者 trigger_time >= $__unixEpochFrom() AND trigger_time < $__unixEpochTo(), $__unixEpochFrom() 表示開始的 unix 時間戳,$__unixEpochTo() 表示結束時間的 unix 時間戳 +> WHERE 語句可以直接寫 WHERE trigger_time >= 1720060214 AND trigger_time < 1720061214 +> 或者 trigger_time >= $__unixEpochFrom() AND trigger_time < $__unixEpochTo(), $__unixEpochFrom() 表示開始的 unix 時間戳,$\_\_unixEpochTo() 表示結束時間的 unix 時間戳 ```sql -SELECT count(*) AS count, trigger_time AS time -FROM n9e_v6_plus.alert_his_event +SELECT count(*) AS count, trigger_time AS time +FROM n9e_v6_plus.alert_his_event WHERE trigger_time >= $__unixEpochFrom() AND trigger_time < $__unixEpochTo() -GROUP BY time +GROUP BY time ORDER BY time DESC ``` > 2.3 查詢某一個時間段的數據,trigger_time 是 2024-07-04 10:48:01 字符串格式的情況 -> WHERE 語句可以直接寫 WHERE trigger_time >= "2024-07-04 09:48:01" AND trigger_time < "2024-07-04 11:48:01" +> WHERE 語句可以直接寫 WHERE trigger_time >= "2024-07-04 09:48:01" AND trigger_time < "2024-07-04 11:48:01" ```sql -SELECT count(*) AS count, trigger_time AS time -FROM n9e_v6_plus.alert_his_event +SELECT count(*) AS count, trigger_time AS time +FROM n9e_v6_plus.alert_his_event WHERE trigger_time >= "2024-07-04 09:48:01" AND trigger_time < "2024-07-04 11:48:01" -GROUP BY time +GROUP BY time ORDER BY time DESC ``` > 2.4 查詢最近 7 天每分鐘產生的告警數量 ```sql -SELECT FROM_UNIXTIME(trigger_time, '%Y-%m-%d %H:%i:00') AS alert_minute,COUNT(*) AS alert_count -FROM n9e_v6_plus.alert_his_event -WHERE trigger_time >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY)) -GROUP BY alert_minute +SELECT FROM_UNIXTIME(trigger_time, '%Y-%m-%d %H:%i:00') AS alert_minute,COUNT(*) AS alert_count +FROM n9e_v6_plus.alert_his_event +WHERE trigger_time >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY)) +GROUP BY alert_minute ORDER BY alert_minute DESC; ``` @@ -80,7 +80,7 @@ CREATE TABLE `alert_his_event` ( ### 宏變量使用 -在配置儀表板的時候,一般我們要查詢數據的時間範圍不是固定的,需要隨著儀表板右上角的時間返回變化而變化,這個時候就需要使用和時間相關的宏變量了,下面是我們目前支持的宏變量以及使用說明 (備註:下面說明中的 1494410783 是前端傳的 start_time,1494410983 是前端傳的 end_time) +在配置儀表盤的時候,一般我們要查詢數據的時間範圍不是固定的,需要隨著儀表盤右上角的時間返回變化而變化,這個時候就需要使用和時間相關的宏變量了,下面是我們目前支持的宏變量以及使用說明 (備註:下面說明中的 1494410783 是前端傳的 start_time,1494410983 是前端傳的 end_time) ``` 宏變量描述 @@ -102,15 +102,15 @@ $__unixEpochGroup(dateColumn,'5m')在 group by 的時候使用,以 5m 為分 以上面的 alert_his_event 表為例,查詢一段時間範圍內每分鐘的告警數量的 SQL 如下 ```sql -SELECT +SELECT $__unixEpochGroup(trigger_time, '1m') AS time, COUNT(*) AS alert_count -FROM +FROM n9e_v6_plus.alert_his_event -WHERE +WHERE $__unixEpochFilter(trigger_time) -GROUP BY +GROUP BY time -ORDER BY +ORDER BY time ``` diff --git a/src/plugins/doris/AlertRule/EnrichQueries/index.tsx b/src/plugins/doris/AlertRule/EnrichQueries/index.tsx index 8d9c86ae5..fb6ecb8bb 100644 --- a/src/plugins/doris/AlertRule/EnrichQueries/index.tsx +++ b/src/plugins/doris/AlertRule/EnrichQueries/index.tsx @@ -1,9 +1,10 @@ import React, { useContext } from 'react'; -import { Form, Space, Input, Row, Col, InputNumber, Tooltip, Select } from 'antd'; +import { Form, Space, Input, Row, Col, InputNumber, Tooltip, Select, Button } from 'antd'; import { PlusCircleOutlined, CloseCircleOutlined, InfoCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import _ from 'lodash'; import { useTranslation, Trans } from 'react-i18next'; import { SqlMonacoEditor } from '@fc-components/monaco-editor'; +import { WandSparkles } from 'lucide-react'; import { CommonStateContext } from '@/App'; import { alphabet } from '@/utils/constant'; @@ -100,6 +101,14 @@ export default function index({ prefixField = {}, fullPrefixName = [], prefixNam { + return ( + +