Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: --story=119532566 Agent包管理(closed #2488) #2489

Open
wants to merge 2 commits into
base: v2.4.8-dev
Choose a base branch
from
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
33 changes: 33 additions & 0 deletions frontend/src/api/modules/pkg_manage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { request } from '../base';

export const listPackage = request('GET', 'api/agent/package/');
export const listPackageNew = request('POST', 'api/agent/package/search/{{pk}}');
export const updatePackage = request('PUT', 'api/agent/package/{{pk}}/');
export const deletePackage = request('DELETE', 'api/agent/package/{{pk}}/');
export const quickSearchCondition = request('GET', 'api/agent/package/quick_search_condition/');
export const uploadPackage = request('POST', 'api/agent/package/upload/');
export const parsePackage = request('POST', 'api/agent/package/parse/');
export const createAgentRegisterTask = request('POST', 'api/agent/package/create_register_task/');
export const queryAgentRegisterTask = request('GET', 'api/agent/package/query_register_task/');
export const getTags = request('GET', 'api/agent/package/tags/');
export const getVersion = request('POST', 'api/agent/package/version/');
export const getDeployedHostsCount = request('POST', 'api/agent/package/deployed_hosts_count/');
export const createAgentTags = request('POST', 'api/agent/package/create_agent_tags/');
export const versionCompare = request('POST', 'api/agent/package/version_compare/');

export default {
listPackage,
updatePackage,
listPackageNew,
deletePackage,
quickSearchCondition,
uploadPackage,
parsePackage,
createAgentRegisterTask,
queryAgentRegisterTask,
getTags,
getVersion,
getDeployedHostsCount,
createAgentTags,
versionCompare,
};
4 changes: 3 additions & 1 deletion frontend/src/common/demand-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import Vue from 'vue';

import {
bkBadge, bkButton, bkCheckbox, bkCheckboxGroup, bkCol, bkCollapse, bkCollapseItem, bkContainer, bkDatePicker,
bkDialog, bkDropdownMenu, bkException, bkForm, bkFormItem, bkInfoBox, bkInput, bkLoading, bkMessage,
bkDialog, bkDropdownMenu, bkException, bkForm, bkFormItem, bkComposeFormItem,
bkInfoBox, bkInput, bkLoading, bkMessage,
bkNavigation, bkNavigationMenu, bkNavigationMenuItem, bkNotify, bkOption, bkOptionGroup, bkPagination,
bkPopover, bkProcess, bkProgress, bkRadio, bkRadioGroup, bkRoundProgress, bkRow, bkSearchSelect, bkSelect,
bkSideslider, bkSlider, bkSteps, bkSwitcher, bkTab, bkTabPanel, bkTable, bkTableColumn, bkTagInput, bkTimePicker,
Expand All @@ -31,6 +32,7 @@ Vue.use(bkDropdownMenu);
Vue.use(bkException);
Vue.use(bkForm);
Vue.use(bkFormItem);
Vue.use(bkComposeFormItem);
Vue.use(bkInput);
Vue.use(bkNavigation);
Vue.use(bkNavigationMenu);
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/common/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,28 @@ export const sort = (arr: any[], key: string) => {
return (`${pre}`).toString().localeCompare((`${next}`));
});
};
/**
* 单个函数用于解析和比较版本号
* 排序规则:1. 数字
*/
export const compareVersions = (a: string, b: string) => {
if (a === b) return 0;
if (!a || !b) return a ? 1 : -1;
// 解析版本号字符串,返回数字数组
const parseVersion = (version: string) => version?.match(/\d+/g)?.map(Number);

// 获取版本号的数字数组
const versionA = parseVersion(a) || [];
const versionB = parseVersion(b) || [];

// 比较主版本号、次版本号、补丁号和附加编号
for (let i = 0; i < versionA.length; i++) {
const diff = versionA[i] - versionB[i];
if (diff !== 0) return diff;
}

return 0; // 全部相同
}
/**
* 对象深拷贝
* @param {*} obj
Expand Down
214 changes: 133 additions & 81 deletions frontend/src/components/common/flexible-tag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,103 +5,155 @@
content: allTagContext
}">
<div
class="tag-item flexible-tag text-ellipsis"
v-for="(item, index) in flexTags"
:key="index"
:class="['tag-item flexible-tag text-ellipsis', item.className]"
:style="{
maxWidth: `${maxWidth}px`
}"
v-for="(item, index) in list"
:key="index">
{{ idKey ? item[idKey] : item }}
v-bind="item">
{{ item.tagDisplay }}
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import { defineComponent, ref, getCurrentInstance, toRefs, PropType, watch } from 'vue';

@Component({ name: 'flexible-tag' })
export default defineComponent({
props: {
list: {
type: Array as PropType<any[]>,
default: () => [],
},
labelKey: {
type: String,
default: 'name',
},
maxWidth: {
type: Number,
default: 140,
},
deleteAble: {
type: Boolean,
default: false,
},
},
setup(props) {
const { proxy } = (getCurrentInstance() || {});

export default class BkBizSelect extends Vue {
@Prop({ type: Array, default: () => [] }) private readonly list!: any[];
@Prop({ type: String, default: '' }) private readonly idKey!: string;
@Prop({ type: Number, default: 140 }) private readonly maxWidth!: number;
const numTagNode = ref();
const overflowTagIndex = ref(-4);

private numTagNode: any = null;
private overflowTagIndex = -1;
const flexTags = ref<any[]>([]);
const allTagContext = ref('');

private get allTagContext() {
return this.list.map(item => (this.idKey ? item[this.idKey] : item)).join(', ');
}

private mounted() {
this.reCalcOverflow();
}
const removeOverflowTagNode = () => {
if (proxy?.$el && (numTagNode.value?.parentNode === proxy?.$el)) {
proxy?.$el.removeChild(numTagNode.value);
}
};
const getTagDOM = (index?: number) => {
const tags = [].slice.call(proxy?.$el.querySelectorAll('.tag-item'));
return typeof index === 'number' ? tags[index] : tags;
};
// 创建/获取溢出数字节点
const getNumTag = () => {
if (numTagNode.value) {
return numTagNode.value;
}
const nodeNum = document.createElement('span');
nodeNum.className = 'num-tag';
numTagNode.value = nodeNum;
return nodeNum;
};

private reCalcOverflow() {
this.removeOverflowTagNode();
if (this.list.length < 2) {
return false;
}
setTimeout(() => {
const tags: any[] = this.getTagDOM();
const tagIndexInSecondRow = tags.findIndex((tagItem, index) => {
if (!index) {
return false;
const reCalcOverflow = () => {
removeOverflowTagNode();
if (props.list.length < 2) {
return false;
}
setTimeout(() => {
const tags: any[] = getTagDOM();
const tagIndexInSecondRow = tags.findIndex((tagItem, index) => {
if (!index) {
return false;
}
const previousTag = tags[index - 1];
return previousTag.offsetTop !== tagItem.offsetTop;
});
if (tagIndexInSecondRow > -1) {
overflowTagIndex.value = tagIndexInSecondRow;
} else {
overflowTagIndex.value = -1;
}
const previousTag = tags[index - 1];
return previousTag.offsetTop !== tagItem.offsetTop;
if (proxy?.$el) {
proxy.$el.scrollTop = 0;
}
insertOverflowTag();
});
if (tagIndexInSecondRow > -1) {
this.overflowTagIndex = tagIndexInSecondRow;
};
const insertOverflowTag = () => {
if (overflowTagIndex.value < 0) {
return;
}
const overflowTagNode = getNumTag();
const referenceTag: any = getTagDOM(overflowTagIndex.value);
if (referenceTag) {
setOverflowTagContent();
proxy?.$el.insertBefore(overflowTagNode, referenceTag);
} else {
this.overflowTagIndex = -1;
overflowTagIndex.value = -1;
return;
}
this.$el.scrollTop = 0;
this.insertOverflowTag();
});
}
private insertOverflowTag() {
if (this.overflowTagIndex < 0) {
return;
}
const overflowTagNode = this.getNumTag();
const referenceTag: any = this.getTagDOM(this.overflowTagIndex);
if (referenceTag) {
this.setOverflowTagContent();
this.$el.insertBefore(overflowTagNode, referenceTag);
} else {
this.overflowTagIndex = -1;
return;
}
setTimeout(() => {
const previousTag: any = this.getTagDOM(this.overflowTagIndex - 1);
if (overflowTagNode.offsetTop !== previousTag.offsetTop) {
this.overflowTagIndex -= 1;
this.$el.insertBefore(overflowTagNode, overflowTagNode.previousSibling);
this.setOverflowTagContent();
setTimeout(() => {
const previousTag: any = getTagDOM(overflowTagIndex.value - 1);
if (overflowTagNode.offsetTop !== previousTag.offsetTop) {
overflowTagIndex.value -= 1;
proxy?.$el.insertBefore(overflowTagNode, overflowTagNode.previousSibling);
setOverflowTagContent();
}
});
};
const setOverflowTagContent = () => {
numTagNode.value.textContent = `+${props.list.length - overflowTagIndex.value}`;
};

const updateTags = () => {
try {
const isStr = props.list.length ? typeof props.list[0] === 'string' : false;
const tags = isStr
? props.list.map(text => ({
tagDisplay: text,
deleteAble: props.deleteAble,
}))
: props.list.map(item => ({
...item,
tagDisplay: item[props.labelKey || 'name'],
deleteAble: item.readonly ? false : props.deleteAble,
}));
flexTags.value.splice(0, flexTags.value.length, ...tags);
allTagContext.value = tags.map(item => item.tagDisplay).join(', ');
} catch (_) {
flexTags.value.splice(0, flexTags.value.length);
allTagContext.value = '';
}
};

watch(() => props.list, () => {
updateTags();
reCalcOverflow();
}, {
deep: true,
immediate: true,
});
}
private setOverflowTagContent() {
this.numTagNode.textContent = `+${this.list.length - this.overflowTagIndex}`;
}
private getTagDOM(index?: number) {
const tags = [].slice.call(this.$el.querySelectorAll('.tag-item'));
return typeof index === 'number' ? tags[index] : tags;
}
// 创建/获取溢出数字节点
private getNumTag() {
if (this.numTagNode) {
return this.numTagNode;
}
const numTagNode = document.createElement('span');
numTagNode.className = 'num-tag';
this.numTagNode = numTagNode;
return numTagNode;
}
private removeOverflowTagNode() {
if (this.numTagNode && this.numTagNode.parentNode === this.$el) {
this.$el.removeChild(this.numTagNode);
}
}
}

return {
...toRefs(props),
flexTags,
allTagContext,
};
},
});


</script>
Loading
Loading