Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion bkmonitor/webpack/scripts/build.vue2.components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { resolve } from 'node:path';
import { defineConfig } from 'vite';
import { analyzer } from 'vite-bundle-analyzer';
import { viteStaticCopy } from 'vite-plugin-static-copy';

import { scopeVue2Css } from './plugin-scope-vue2-css';
const outputDir = resolve(__dirname, '../monitor-vue2-components');
export default defineConfig({
define: {
Expand Down Expand Up @@ -64,7 +66,9 @@ export default defineConfig({
// },
],
}),
analyzer(),
// 供 vue3 宿主使用的作用域样式副本,避免包内 bkui-vue2 样式与宿主的 bkui-vue3 互相污染
scopeVue2Css({ outDir: outputDir }),
analyzer()
],
build: {
copyPublicDir: false,
Expand Down
4 changes: 2 additions & 2 deletions bkmonitor/webpack/scripts/package.vue2.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "@blueking/monitor-vue2-components",
"version": "0.0.1",
"version": "0.0.0-beta.10",
"description": "blueking monitor vue2 components builder",
"scripts": {},
"files": ["index.mjs", "index.css", "monitor-vue2.mjs", "readme.md"],
"files": ["index.mjs", "index.css", "index.scoped.css", "monitor-vue2.mjs", "readme.md"],
"author": "bkfe",
"license": "MIT",
"main": "monitor-vue2.mjs",
Expand Down
95 changes: 95 additions & 0 deletions bkmonitor/webpack/scripts/plugin-scope-vue2-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Tencent is pleased to support the open source community by making
* 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
*
* Copyright (C) 2017-2025 Tencent. All rights reserved.
*
* 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.
*
* License for 蓝鲸智云PaaS平台 (BlueKing PaaS):
*
* ---------------------------------------------------
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/

import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';

import postcss from 'postcss';

import type { Plugin } from 'vite';

/** 宿主容器需要挂上的类名,与产出的选择器前缀一致 */
export const VUE2_CSS_SCOPE = '.monitor-vue2-scope';

const ROOT_SELECTOR = /^(html|body|:root)$/;
const ROOT_DESCENDANT = /^(html|body)\b\s*/;

const scopeSelector = (selector: string) => {
const value = selector.trim();
if (!value || value.startsWith(VUE2_CSS_SCOPE)) return selector;
// 根级选择器落到容器自身,:root 上的 CSS 变量因此仍能被容器内元素继承
if (ROOT_SELECTOR.test(value)) return VUE2_CSS_SCOPE;
if (ROOT_DESCENDANT.test(value)) return `${VUE2_CSS_SCOPE} ${value.replace(ROOT_DESCENDANT, '')}`;
return `${VUE2_CSS_SCOPE} ${value}`;
};

const isInsideKeyframes = (rule: postcss.Rule) => {
for (let node: postcss.Container | postcss.Document | undefined = rule.parent; node; node = node.parent) {
if (node.type === 'atrule' && /keyframes$/.test((node as postcss.AtRule).name)) return true;
}
return false;
};

interface IOptions {
outDir: string;
/** 构建产出的原始样式文件名 */
source?: string;
/** 加了作用域前缀的副本文件名 */
output?: string;
}

/**
* 额外产出一份带作用域前缀的样式副本。
*
* 包内自带整套 bkui-vue2 样式,其中数百个 .bk-* 类名与 bkui-vue3 同名,
* 宿主若是 vue3 项目,全局引入原始样式会双向污染。改用这份副本、并给组件的挂载容器
* (以及 bk-magic-vue 挂在 body 下的全局弹层容器)加上 VUE2_CSS_SCOPE 类名,
* 规则就只在容器内生效,且特异性比裸类名高一级,容器内由 vue2 的样式胜出。
*/
export function scopeVue2Css(options: IOptions): Plugin {
const { outDir, source = 'index.css', output = 'index.scoped.css' } = options;
return {
name: 'monitor-scope-vue2-css',
// lib 模式下多入口会多次触发 writeBundle,这里只需在全部产物落盘后跑一次
closeBundle() {
const sourcePath = resolve(outDir, source);
if (!existsSync(sourcePath)) return;

const root = postcss.parse(readFileSync(sourcePath, 'utf8'));
let scopedCount = 0;
root.walkRules(rule => {
// @keyframes 内是 from/to/百分比,加前缀会让动画失效
if (isInsideKeyframes(rule)) return;
rule.selectors = rule.selectors.map(scopeSelector);
scopedCount += 1;
});

const result = root.toString();
writeFileSync(resolve(outDir, output), result);
console.log(`${output} ${(result.length / 1024).toFixed(2)} kB │ ${scopedCount} 条规则加上 ${VUE2_CSS_SCOPE}`);
},
};
}
3 changes: 2 additions & 1 deletion bkmonitor/webpack/src/monitor-pc/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ import Vue2 from 'vue';

import './common/import-magicbox-ui';

import ExpressionPanel from './pages/query-template/components/expression-panel/expression-panel';
import QueryPanel, { QueryPanelEmits } from './pages/query-template/components/query-panel/query-panel';

import './static/css/global.scss';
import './static/css/reset.scss';
import 'monitor-static/icons/monitor-icons.css';
export * from './pages/query-template/typings';

export { i18n, QueryPanel, QueryPanelEmits, Vue2 };
export { ExpressionPanel, i18n, QueryPanel, QueryPanelEmits, Vue2 };
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

.input-popover {
flex: 1;
width: 100%;

.bk-tooltip-ref {
.bk-tooltip-ref,
.input-text {
width: 100%;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export default class CycleInput extends tsc<IProps, IEvent> {
offset={-1}
placement='bottom-start'
theme='light cycle-list-wrapper'
tippyOptions={{ appendTo: this.appendTo === 'parent' ? 'parent' : document.body }}
tippyOptions={this.appendTo === 'parent' ? { appendTo: 'parent' } : {}}
trigger='click'
>
<slot name='trigger'>
Expand Down Expand Up @@ -243,7 +243,7 @@ export default class CycleInput extends tsc<IProps, IEvent> {
offset={-1}
placement='bottom-end'
theme='light cycle-list-wrapper'
tippyOptions={{ appendTo: this.appendTo === 'parent' ? 'parent' : document.body }}
tippyOptions={this.appendTo === 'parent' ? { appendTo: 'parent' } : {}}
trigger='click'
onHide={() => {
this.unitActive = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import './condition-creator-options.scss';

interface IProps {
allVariables?: { name: string }[];
createVariableFn?: (onCreated: (name: string) => void) => void;
dimensionValueVariables?: { name: string }[];
fields: IFilterField[];
hasVariableOperate?: boolean;
Expand Down Expand Up @@ -84,6 +85,8 @@ export default class UiSelectorOptions extends tsc<IProps> {
@Prop({ type: Array, default: () => [] }) dimensionValueVariables: { name: string }[];
/* 所有变量,用于校验变量名是否重复 */
@Prop({ default: () => [] }) allVariables: { name: string }[];
/* 由外部接管变量创建(如宿主自带变量面板),传入时不再展开内置的命名输入面板 */
@Prop({ default: null, type: Function }) createVariableFn: (onCreated: (name: string) => void) => void;

@Ref('allInput') allInputRef;
@Ref('valueSelector') valueSelectorRef: ValueTagSelector;
Expand Down Expand Up @@ -449,6 +452,12 @@ export default class UiSelectorOptions extends tsc<IProps> {
}

handleClickCreateVariable() {
if (this.createVariableFn) {
this.createVariableFn((name: string) => {
if (name) this.$emit('createVariable', name);
});
return;
}
this.isCreateVariable = true;
this.cursorIndex = 0;
}
Expand Down Expand Up @@ -547,6 +556,7 @@ export default class UiSelectorOptions extends tsc<IProps> {
key={this.rightRefreshKey}
ref='valueSelector'
allVariables={this.allVariables}
createVariableFn={this.createVariableFn}
fieldInfo={this.valueSelectorFieldInfo}
getValueFn={this.getValueFnProxy}
hasVariableOperate={this.hasVariableOperate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import './condition-creator-selector.scss';
interface IProps {
allVariables?: { name: string }[];
clearKey?: string;
createVariableFn?: (onCreated: (name: string) => void) => void;
dimensionValueVariables?: { name: string }[];
fields?: IFilterField[];
hasVariableOperate?: boolean;
Expand Down Expand Up @@ -78,6 +79,8 @@ export default class ConditionCreatorSelector extends tsc<IProps> {
@Prop({ default: () => [] }) allVariables: { name: string }[];
/** 是否展示条件标签 */
@Prop({ default: false, type: Boolean }) showConditionTag: boolean;
/* 由外部接管变量创建(如宿主自带变量面板),传入时不再展开内置的命名输入面板 */
@Prop({ default: null, type: Function }) createVariableFn: (onCreated: (name: string) => void) => void;
@Ref('selector') selectorRef: HTMLDivElement;

/* 是否显示弹出层 */
Expand Down Expand Up @@ -131,6 +134,9 @@ export default class ConditionCreatorSelector extends tsc<IProps> {
this.showSelector = true;
}
destroyPopoverInstance() {
/* 确定/取消属于主动关闭,不该被变量创建状态挡住,
否则宿主的变量面板异常退出后这个弹层就再也关不掉了 */
this.showCreateVariablePop = false;
this.popoverInstance?.hide?.();
this.popoverInstance?.destroy?.();
this.popoverInstance = null;
Expand Down Expand Up @@ -292,6 +298,7 @@ export default class ConditionCreatorSelector extends tsc<IProps> {
<div ref='selector'>
<ConditionCreatorOptions
allVariables={this.allVariables}
createVariableFn={this.createVariableFn}
dimensionValueVariables={this.dimensionValueVariables}
fields={this.fields}
getValueFn={this.getValueFn}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ import { CONDITIONS } from './condition-condition-tag';
import ConditionCreatorSelector from './condition-creator-selector';
import { EFieldType } from './typing';

import type { IFilterItem, IGetValueFnParams } from '../../../../components/retrieval-filter/utils';
import type {
IFilterItem,
IGetValueFnParams,
IWhereValueOptionsItem,
} from '../../../../components/retrieval-filter/utils';
import type { MetricDetailV2 } from '../../typings/metric';
import type { AggCondition } from '../../typings/query-config';
import type { IConditionOptionsItem, IVariablesItem } from '../type/query-config';
Expand All @@ -60,6 +64,8 @@ interface IProps {
showLabel?: boolean;
value?: AggCondition[];
variables?: IVariablesItem[];
createVariableFn?: (onCreated: (name: string) => void) => void;
getValueFn?: (params: IGetValueFnParams) => Promise<IWhereValueOptionsItem>;
onChange?: (val: AggCondition[]) => void;
onCreateValueVariable?: (val: { name: string; related_tag: string }) => void;
onCreateVariable?: (val: string) => void;
Expand All @@ -83,6 +89,12 @@ export default class ConditionCreator extends tsc<IProps> {
@Prop({ default: () => [] }) allVariables: { name: string }[];
/** 是否展示条件标签 */
@Prop({ default: false, type: Boolean }) showConditionTag: boolean;
/* 维度值获取方法,外部注入后可对接自有接口,未注入时走监控默认实现 */
@Prop({ default: null, type: Function }) getValueFn: (
params: IGetValueFnParams
) => Promise<IWhereValueOptionsItem>;
/* 由外部接管变量创建(如宿主自带变量面板),传入时不再展开内置的命名输入面板 */
@Prop({ default: null, type: Function }) createVariableFn: (onCreated: (name: string) => void) => void;

cacheDimensionValues = new Map();

Expand Down Expand Up @@ -141,6 +153,36 @@ export default class ConditionCreator extends tsc<IProps> {
];
}

get valueFn() {
return this.getValueFn ? this.externalGetValueFn : this.defaultGetValueFn;
}

searchOptions(search: string, list: { id: string; name: string }[]) {
if (!search) {
return list;
}
const searchLower = search.toLocaleLowerCase();
return list.filter(
item =>
item.name.toLocaleLowerCase().includes(searchLower) || item.id.toLocaleLowerCase().includes(searchLower)
);
}

/* 外部注入的取值函数只负责取候选值,维度值变量与搜索过滤仍由这里统一补上 */
externalGetValueFn(params: IGetValueFnParams): any {
return Promise.resolve(this.getValueFn(params)).then((res: any) => {
const allOptions = [
...this.dimensionValueVariables.map(item => ({ name: item.name, id: item.name, isVariable: true })),
...(res?.list || []),
];
const filterList = this.searchOptions(params?.where?.[0]?.value?.[0], allOptions);
return {
count: filterList.length,
list: filterList,
};
});
}

handleGetMethodList(type: 'number' | 'string') {
if (type === 'number') {
return NUMBER_CONDITION_METHOD_LIST;
Expand Down Expand Up @@ -173,18 +215,9 @@ export default class ConditionCreator extends tsc<IProps> {
);
}

getValueFn(params: IGetValueFnParams): any {
defaultGetValueFn(params: IGetValueFnParams): any {
return new Promise(resolve => {
const searchList = (search: string, list) => {
if (!search) {
return list;
}
const searchLower = search.toLocaleLowerCase();
return list.filter(
item =>
item.name.toLocaleLowerCase().includes(searchLower) || item.id.toLocaleLowerCase().includes(searchLower)
);
};
const searchList = (search: string, list) => this.searchOptions(search, list);
const searchValue = params?.where?.[0]?.value?.[0];
const dimensionKey = params.fields[0];
const list = this.cacheDimensionValues.get(dimensionKey);
Expand Down Expand Up @@ -227,9 +260,10 @@ export default class ConditionCreator extends tsc<IProps> {
{this.showLabel && <div class='condition-label'>{this.$slots?.label || this.$t('过滤条件')}</div>}
<ConditionCreatorSelector
allVariables={this.allVariables}
createVariableFn={this.createVariableFn}
dimensionValueVariables={this.dimensionValueVariables}
fields={this.fields as IFilterField[]}
getValueFn={this.getValueFn}
getValueFn={this.valueFn}
hasVariableOperate={this.hasVariableOperate}
showConditionTag={this.showConditionTag}
value={this.localValue}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import './value-options.scss';

interface IProps {
allVariables?: { name: string }[];
createVariableFn?: (onCreated: (name: string) => void) => void;
fieldInfo?: IFieldItem;
getValueFn?: TGetValueFn;
hasVariableOperate?: boolean;
Expand Down Expand Up @@ -91,6 +92,8 @@ export default class ValueOptions extends tsc<IProps> {
@Prop({ type: Array, default: () => [] }) variables: { name: string }[];
/* 所有变量,用于校验变量名是否重复 */
@Prop({ default: () => [] }) allVariables: { name: string }[];
/* 由外部接管变量创建(如宿主自带变量面板),传入时不再展开内置的命名输入面板 */
@Prop({ default: null, type: Function }) createVariableFn: (onCreated: (name: string) => void) => void;

localOptions: IValue[] = [];
loading = false;
Expand Down Expand Up @@ -323,6 +326,7 @@ export default class ValueOptions extends tsc<IProps> {
>
<AddVariableOption
allVariables={this.allVariables}
createVariableFn={this.createVariableFn}
popDistance={13}
onAdd={this.handleAddVar}
onOpenChange={this.handleAddVariableOpenChange}
Expand Down Expand Up @@ -355,6 +359,7 @@ export default class ValueOptions extends tsc<IProps> {
>
<AddVariableOption
allVariables={this.allVariables}
createVariableFn={this.createVariableFn}
popDistance={13}
onAdd={this.handleAddVar}
onOpenChange={this.handleAddVariableOpenChange}
Expand Down
Loading
Loading