chore: add column `Builtin` to `View` (#101)

* chore: add column `Builtin` to `View`

* chore: adjust TopN's UI

---------

Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
yaojp123 2025-01-24 15:56:12 +08:00 committed by GitHub
parent 6d576a7636
commit 098371729e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 62 additions and 28 deletions

View File

@ -43,7 +43,7 @@ export class SimpleSavedObject<T = unknown> {
constructor(
private client: SavedObjectsClientContract,
{ id, type, version, attributes, error, references, migrationVersion, complex_fields }: SavedObjectType<T>
{ id, type, version, attributes, error, references, migrationVersion }: SavedObjectType<T>
) {
this.id = id;
this.type = type;
@ -51,7 +51,6 @@ export class SimpleSavedObject<T = unknown> {
this.references = references || [];
this._version = version;
this.migrationVersion = migrationVersion;
this.attributes.complexFields = complex_fields
if (error) {
this.error = error;
}

View File

@ -140,6 +140,7 @@ export class IndexPattern implements IIndexPattern {
return this.deserializeFieldFormatMap(mapping);
});
this.viewName = spec.viewName || "";
this.builtin = spec.builtin;
}
/**
@ -440,6 +441,7 @@ export class IndexPattern implements IIndexPattern {
fieldFormatMap,
type: this.type,
typeMeta: this.typeMeta ? JSON.stringify(this.typeMeta) : undefined,
builtin: this.builtin
};
}

View File

@ -358,7 +358,8 @@ export class IndexPatternsService {
sourceFilters,
fieldFormatMap,
typeMeta,
complexFields,
complex_fields: complexFields,
builtin
},
type,
} = savedObject;
@ -385,7 +386,8 @@ export class IndexPatternsService {
fields: this.fieldArrayToMap(parsedFields),
typeMeta: parsedTypeMeta,
type,
complexFields: parsedComplexFields
complexFields: parsedComplexFields,
builtin
};
};

View File

@ -40,6 +40,9 @@ const EditIndexPatternCont: React.FC<RouteComponentProps<{ id: string }>> = ({
if (!ip.id) {
ip.id = props.match.params.id;
}
if (ip.builtin) {
props.history.push("");
}
setIndexPattern(ip);
// setBreadcrumbs(getEditBreadcrumbs(ip));
});

View File

@ -211,11 +211,18 @@ export const IndexPatternTable = ({
dataIndex: "title",
sorter: (a: string, b: string) => sorter.string(a, b, "title"),
},
{
title: formatMessage({ id: "explore.createview.field.builtin" }),
dataIndex: "builtin",
render: (val) => {
return formatMessage({ id: `explore.createview.field.builtin.${val === true ? "true" : "false"}` });
},
},
{
title: formatMessage({ id: "table.field.actions" }),
render: (text, record) => (
<div>
{canSave ? (
{canSave && !record.builtin ? (
<>
<a {...reactRouterNavigate(history, `patterns/${record.id}`)}>
{formatMessage({ id: "form.button.edit" })}

View File

@ -38,8 +38,8 @@ export async function getIndexPatterns(
const id = pattern.id;
const title = pattern.get('title');
const viewName = pattern.get('viewName');
const builtin = pattern.get('builtin');
const isDefault = defaultIndex === id;
const tags = (indexPatternManagementStart as IndexPatternManagementStart).list.getIndexPatternTags(
pattern,
isDefault
@ -55,6 +55,7 @@ export async function getIndexPatterns(
// so the sorting will but the default index on top
// or on bottom of a the table
sort: `${isDefault ? '0' : '1'}${title}`,
builtin,
};
})
.sort((a, b) => {

View File

@ -27,6 +27,9 @@ export default {
"explore.createview.field.match_rule": "Match Rule",
"explore.createview.field.match_rule.help":
'Use (*) to match multiple indices and cannot contain spaces or characters , /, ?, ", <, >, |.',
"explore.createview.field.builtin": "Builtin",
"explore.createview.field.builtin.true": "True",
"explore.createview.field.builtin.false": "false",
"explore.createview.status.match_index_num": "Matching {length} indices",
"explore.createview.status.match_special_index":
"The current matching rule did not match the Elasticsearch index.To match the special index, turn on the Include special index switch.",

View File

@ -26,6 +26,9 @@ export default {
"explore.createview.field.match_rule": "匹配规则",
"explore.createview.field.match_rule.help":
'使用 (*) 来匹配多个索引。 不能包含空格或者字符 , /, ?, ", <, >, |.',
"explore.createview.field.builtin": "是否内置",
"explore.createview.field.builtin.true": "是",
"explore.createview.field.builtin.false": "否",
"explore.createview.status.match_index_num": "当前匹配到 {length} 个索引",
"explore.createview.status.match_special_index":
"当前规则没有匹配到任何索引。如需匹配特殊索引,请打开包含特殊索引开关。",

View File

@ -36,18 +36,18 @@ export default ({ value = [], onChange, style = {}, className = '' }) => {
{
value.map((item, index) => (
<ColorPicker key={index} color={item} onRemove={() => onRemove(index)} onChange={(color) => handleChange(color, index)}>
<Button style={{ padding: 3, width: 120, marginBottom: 8 }} size="small" >
<Button style={{ padding: 3, width: 84, marginBottom: 8 }} size="small" >
<div style={{ background: item, width: '100%', height: 16 }}></div>
</Button>
</ColorPicker>
))
}
<ColorPicker onChange={onAdd}>
<Button size="small" icon="plus" style={{ width: 120 }}></Button>
<Button size="small" icon="plus" style={{ width: 84 }}></Button>
</ColorPicker>
</div>
)}>
<div className={className} style={{ padding: '10px 8px', border: '1px solid #d9d9d9', width: 136, height: 32, cursor: 'pointer', ...style }}>
<div className={className} style={{ padding: '10px 8px', border: '1px solid #d9d9d9', width: 100, height: 32, cursor: 'pointer', ...style }}>
<div style={{ background, height: 12}}></div>
</div>
</Popover>

View File

@ -2,7 +2,7 @@
:global {
.ant-popover-inner-content {
padding: 8px;
max-width: 136px;
max-width: 100px;
}
.ant-popover-arrow {
display: none;

View File

@ -72,8 +72,7 @@ export default (props) => {
})
if (res && !res.error && Array.isArray(res.saved_objects) && res.saved_objects[0]) {
const newView = res.saved_objects[0]
let { fieldFormatMap, fields } = newView.attributes || {}
let { complex_fields: complexFields } = newView
let { fieldFormatMap, fields, complex_fields: complexFields } = newView.attributes || {}
try {
fieldFormatMap = JSON.parse(fieldFormatMap) || {}
fields = JSON.parse(fields) || []
@ -413,12 +412,12 @@ export default (props) => {
<Spin spinning={loading}>
<div className={styles.topn}>
<div className={styles.header}>
<Input.Group compact style={{ width: 'auto '}}>
<Input.Group compact>
<Radio.Group
value={currentMode}
onChange={(e) => setCurrentMode(e.target.value)}
className={styles.mode}
style={{ marginRight: 12, marginBottom: 12 }}
style={{ marginRight: 10 }}
>
<Radio.Button value="treemap">
<Icon
@ -439,11 +438,11 @@ export default (props) => {
/>
</Radio.Button>
</Radio.Group>
<div className={styles.label}>
<div className={styles.label} title="Top">
Top
</div>
<InputNumber
style={{ width: "80px", marginBottom: 12, marginRight: 12 }}
style={{ width: "60px", marginRight: 10 }}
className={styles.borderRadiusRight}
value={formData.top}
min={1}
@ -451,13 +450,17 @@ export default (props) => {
precision={0}
onChange={(value) => onFormDataChange({ top: value })}
/>
<div className={styles.label}>
<div className={styles.label} title={formatMessage({ id: "cluster.monitor.topn.area" })}>
{formatMessage({ id: "cluster.monitor.topn.area" })}
</div>
<Select
style={{ width: "150px", marginBottom: 12 }}
style={{ flexGrow: 0, flexShrink: 1, overflow: 'hidden' }}
value={formData.sourceArea?.name}
dropdownMatchSelectWidth={false}
showSearch
filterOption={(input, option) =>
option.props?.children?.toLowerCase().indexOf(input?.toLowerCase()) >= 0
}
onChange={(value, option) => {
if (value) {
const { isComplex } = option?.props?.metric || {}
@ -490,7 +493,7 @@ export default (props) => {
}
</Select>
<Select
style={{ width: "88px", marginBottom: 12, marginRight: 6 }}
style={{ width: "70px", marginRight: 6 }}
className={styles.borderRadiusRight}
value={formData.statisticArea}
dropdownMatchSelectWidth={false}
@ -507,14 +510,18 @@ export default (props) => {
})
}
</Select>
<Button style={{ width: 32, marginBottom: 12, padding: 0, marginRight: 6, borderRadius: 4 }} onClick={() => onMetricExchange()}><Icon style={{ fontSize: 16 }} component={ConvertSvg}/></Button>
<div className={styles.label}>
<Button style={{ width: 32, minWidth: 32, padding: 0, marginRight: 6, borderRadius: 4 }} onClick={() => onMetricExchange()}><Icon style={{ fontSize: 16 }} component={ConvertSvg}/></Button>
<div className={styles.label} title={formatMessage({ id: "cluster.monitor.topn.color" })}>
{formatMessage({ id: "cluster.monitor.topn.color" })}
</div>
<Select
style={{ width: "150px", marginBottom: 12 }}
style={{ flexGrow: 0, flexShrink: 1, overflow: 'hidden' }}
value={formData.sourceColor?.name}
dropdownMatchSelectWidth={false}
showSearch
filterOption={(input, option) =>
option.props?.children?.toLowerCase().indexOf(input?.toLowerCase()) >= 0
}
onChange={(value, option) => {
if (value) {
const { isComplex } = option?.props?.metric || {}
@ -548,7 +555,7 @@ export default (props) => {
}
</Select>
<Select
style={{ width: "88px", marginBottom: 12 }}
style={{ width: "70px" }}
value={formData.statisticColor}
dropdownMatchSelectWidth={false}
onChange={(value) => onFormDataChange({ statisticColor: value })}
@ -564,17 +571,17 @@ export default (props) => {
})
}
</Select>
<div className={styles.label} style={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }}>
<div className={styles.label} title={formatMessage({ id: "cluster.monitor.topn.theme" })} style={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }}>
{formatMessage({ id: "cluster.monitor.topn.theme" })}
</div>
<GradientColorPicker className={styles.borderRadiusRight} style={{ marginRight: 12, marginBottom: 12 }} value={formData.colors || []} onChange={(value) => {
<GradientColorPicker className={styles.borderRadiusRight} style={{ marginRight: 10 }} value={formData.colors || []} onChange={(value) => {
onFormDataChange({ colors: value })
setConfig({
...cloneDeep(config),
colors: value
})
}}/>
<Button style={{ marginBottom: 12 }} className={styles.borderRadiusLeft} type="primary" onClick={() => fetchData(type, clusterID, timeRange, formData)}>{formatMessage({ id: "form.button.apply" })}</Button>
<Button className={styles.borderRadiusLeft} type="primary" onClick={() => fetchData(type, clusterID, timeRange, formData)}>{formatMessage({ id: "form.button.apply" })}</Button>
</Input.Group>
</div>

View File

@ -3,17 +3,20 @@
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
flex-wrap: nowrap;
margin-bottom: 12px;
:global {
.ant-input-group {
display: flex !important;
flex-wrap: wrap !important;
flex-wrap: nowrap !important;
width: 100%;
}
}
.mode {
width: 84px;
min-width: 84px;
:global {
.ant-radio-button-wrapper {
width: 42px;
@ -34,6 +37,10 @@
border-radius: 4px;
border-top-right-radius: 0px !important;
border-bottom-right-radius: 0px !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 0;
}
}