Skip to content

Commit 9325a5e

Browse files
3octavesiSecloud
authored andcommitted
fix(frontend): 修复mongodb整机替换传参问题及交互补充及代码优化 #10331
1 parent 07c1f87 commit 9325a5e

File tree

4 files changed

+191
-89
lines changed
  • dbm-ui/frontend/src

4 files changed

+191
-89
lines changed

dbm-ui/frontend/src/locales/zh-cn.json

+1
Original file line numberDiff line numberDiff line change
@@ -4427,5 +4427,6 @@
44274427
"撤销后,单据将作废处理": "撤销后,单据将作废处理",
44284428
"确定撤销单据": "确定撤销单据",
44294429
"工具集": "工具集",
4430+
"新机规格不能为空": "新机规格不能为空",
44304431
"这行勿动!新增翻译请在上一行添加!": ""
44314432
}

dbm-ui/frontend/src/views/db-manage/mongodb/MONGODB_CUTOFF/Index.vue

+151-62
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
<EditableTable
4141
ref="table"
4242
class="mb-20"
43-
:model="formData.tableData">
43+
:model="formData.tableData"
44+
:rules="rules">
4445
<EditableRow
4546
v-for="(item, index) in formData.tableData"
4647
:key="index">
@@ -61,7 +62,8 @@
6162
</EditableColumn>
6263
<EditableColumn
6364
:label="t('所属集群')"
64-
:min-width="200">
65+
:min-width="200"
66+
:rowspan="item.rowspan">
6567
<div
6668
v-if="item.host.master_domain"
6769
class="cluster-domain">
@@ -145,6 +147,7 @@
145147
shard: string;
146148
spec_config: MongodbInstanceModel['spec_config'];
147149
};
150+
rowspan: number;
148151
spec_id: number;
149152
}
150153

@@ -164,9 +167,71 @@
164167
shard: '',
165168
spec_config: {} as MongodbInstanceModel['spec_config'],
166169
},
170+
rowspan: data.rowspan || 1,
167171
spec_id: data.spec_id || 0,
168172
});
169173

174+
const getClusterNodeCount = (clusterId: number) => {
175+
const countMap: Record<string, number> = {};
176+
formData.tableData.forEach((tableItem) => {
177+
if (tableItem.host.ip && tableItem.host.cluster_id) {
178+
if (tableItem.host.cluster_id !== clusterId) {
179+
return;
180+
}
181+
const { machine_type: machineType, shard } = tableItem.host;
182+
const nodeKey =
183+
tableItem.host.cluster_type === ClusterTypes.MONGO_SHARED_CLUSTER && tableItem.host.machine_type === 'mongodb'
184+
? shard
185+
: machineType;
186+
if (countMap[nodeKey]) {
187+
countMap[nodeKey] = countMap[nodeKey] + 1;
188+
} else {
189+
countMap[nodeKey] = 1;
190+
}
191+
}
192+
}, {});
193+
194+
const typeCountMap: Record<string, number[]> = {
195+
mongo_config: [],
196+
mongodb: [],
197+
mongos: [],
198+
};
199+
Object.entries(countMap).forEach(([type, count]) => {
200+
if (type in typeCountMap) {
201+
typeCountMap[type].push(count);
202+
} else {
203+
typeCountMap.mongodb.push(count);
204+
}
205+
});
206+
return typeCountMap;
207+
};
208+
209+
const rules = {
210+
'host.ip': [
211+
{
212+
message: '',
213+
trigger: 'change',
214+
validator: (value: string, { rowData }: { rowData: RowData }) => {
215+
const nodeCountMap = getClusterNodeCount(rowData.host.cluster_id);
216+
217+
if (rowData.host.cluster_type === ClusterTypes.MONGO_REPLICA_SET) {
218+
if (nodeCountMap.mongodb.some((mongodbItem) => mongodbItem > 1)) {
219+
return t('同一个副本集,一次只能替换一个节点');
220+
}
221+
return true;
222+
}
223+
if (nodeCountMap.mongo_config.some((mongoConfigItem) => mongoConfigItem > 1)) {
224+
return t('config一次只能替换一个节点');
225+
}
226+
if (nodeCountMap.mongodb.some((mongoConfigItem) => mongoConfigItem > 1)) {
227+
return t('同一个shard,同时只能替换一个节点');
228+
}
229+
return true;
230+
},
231+
},
232+
],
233+
};
234+
170235
const formData = reactive({
171236
clusterType: ClusterTypes.MONGO_REPLICA_SET,
172237
payload: createTickePayload(),
@@ -179,49 +244,23 @@
179244
useTicketDetail<Mongodb.Cutoff>(TicketTypes.MONGODB_CUTOFF, {
180245
onSuccess(ticketDetail) {
181246
const { details } = ticketDetail;
182-
const { clusters, infos, specs } = details;
247+
const { clusters, infos } = details;
183248
Object.assign(formData, {
184-
payload: createTickePayload(ticketDetail),
249+
...createTickePayload(ticketDetail),
250+
clusterType: clusters[infos[0].cluster_id].cluster_type,
251+
tableData: infos.flatMap((infoItem) => {
252+
const machineInfoList = [...infoItem.mongo_config, ...infoItem.mongodb, ...infoItem.mongos];
253+
return machineInfoList.map((machineInfo) =>
254+
createTableRow({
255+
host: {
256+
ip: machineInfo.ip,
257+
master_domain: clusters[infoItem.cluster_id].immute_domain,
258+
} as RowData['host'],
259+
spec_id: machineInfo.spec_id,
260+
}),
261+
);
262+
}),
185263
});
186-
if (infos.length > 0) {
187-
const dataList: RowData[] = [];
188-
infos.forEach((info) => {
189-
const hostList = [...info.mongo_config, ...info.mongodb, ...info.mongos];
190-
let machineType = '';
191-
if (info.mongo_config.length) {
192-
machineType = 'mongo_config';
193-
}
194-
if (info.mongodb.length) {
195-
machineType = 'mongodb';
196-
}
197-
if (info.mongos.length) {
198-
machineType = 'mongos';
199-
}
200-
const clusterInfo = clusters[info.cluster_id];
201-
hostList.forEach((item) => {
202-
const specInfo = specs[info.resource_spec[`${machineType}_${item.ip}`].spec_id];
203-
dataList.push(
204-
createTableRow({
205-
host: {
206-
bk_cloud_id: item.bk_cloud_id,
207-
bk_host_id: item.bk_host_id,
208-
cluster_id: info.cluster_id,
209-
cluster_type: clusterInfo.cluster_type,
210-
ip: item.ip,
211-
machine_type: machineType,
212-
master_domain: clusterInfo.immute_domain,
213-
related_clusters: [],
214-
shard: '',
215-
spec_config: specInfo as unknown as MongodbInstanceModel['spec_config'],
216-
},
217-
spec_id: specInfo.id,
218-
}),
219-
);
220-
});
221-
});
222-
formData.clusterType = dataList[0].host.cluster_type;
223-
formData.tableData = [...dataList];
224-
}
225264
},
226265
});
227266

@@ -242,32 +281,56 @@
242281
ip_source: 'resource_pool';
243282
}>(TicketTypes.MONGODB_CUTOFF);
244283

284+
watch(
285+
() => formData.tableData.length,
286+
() => {
287+
sortTableByCluster();
288+
},
289+
);
290+
291+
const generateRequestParam = () => {
292+
const clusterMap: Record<string, RowData[]> = {};
293+
formData.tableData.forEach((item) => {
294+
if (item.host.ip) {
295+
const domain = item.host.master_domain;
296+
if (!clusterMap[domain]) {
297+
clusterMap[domain] = [item];
298+
} else {
299+
clusterMap[domain].push(item);
300+
}
301+
}
302+
});
303+
const domains = Object.keys(clusterMap);
304+
const infos = domains.map((domain) => {
305+
const sameArr = clusterMap[domain];
306+
const infoItem = {
307+
cluster_id: sameArr[0].host.cluster_id,
308+
mongo_config: [] as SpecHostList,
309+
mongodb: [] as SpecHostList,
310+
mongos: [] as SpecHostList,
311+
};
312+
sameArr.forEach((item) => {
313+
const specObj = {
314+
bk_cloud_id: item.host.bk_cloud_id,
315+
bk_host_id: item.host.bk_host_id,
316+
ip: item.host.ip,
317+
spec_id: item.spec_id,
318+
};
319+
infoItem[item.host.machine_type as keyof Omit<typeof infoItem, 'cluster_id'>].push(specObj);
320+
});
321+
return infoItem;
322+
});
323+
return infos;
324+
};
325+
245326
const handleSubmit = async () => {
246327
const result = await tableRef.value!.validate();
247328
if (!result) {
248329
return;
249330
}
250331
createTicketRun({
251332
details: {
252-
infos: formData.tableData.map((item) => {
253-
const infoItem = {
254-
cluster_id: item.host.cluster_id,
255-
mongo_config: [] as SpecHostList,
256-
mongodb: [] as SpecHostList,
257-
mongos: [] as SpecHostList,
258-
};
259-
Object.assign(infoItem, {
260-
[item.host.machine_type]: [
261-
{
262-
bk_cloud_id: item.host.bk_cloud_id,
263-
bk_host_id: item.host.bk_host_id,
264-
ip: item.host.ip,
265-
spec_id: item.spec_id,
266-
},
267-
],
268-
});
269-
return infoItem;
270-
}),
333+
infos: generateRequestParam(),
271334
ip_source: 'resource_pool',
272335
},
273336
...formData.payload,
@@ -317,6 +380,32 @@
317380
}
318381
return item.host.machine_type || '';
319382
};
383+
384+
const sortTableByCluster = () => {
385+
const clusterMap: Record<string, RowData[]> = {};
386+
const emptyRowList: RowData[] = [];
387+
formData.tableData.forEach((item) => {
388+
Object.assign(item, { rowspan: 1 });
389+
const { master_domain: domain } = item.host;
390+
if (!domain) {
391+
emptyRowList.push(item);
392+
return;
393+
}
394+
if (!clusterMap[domain]) {
395+
clusterMap[domain] = [item];
396+
} else {
397+
clusterMap[domain].push(item);
398+
}
399+
});
400+
401+
const sortedList: RowData[] = [];
402+
Object.values(clusterMap).forEach((list) => {
403+
Object.assign(list[0], { rowspan: list.length });
404+
sortedList.push(...list);
405+
});
406+
407+
return [...sortedList, ...emptyRowList];
408+
};
320409
</script>
321410
<style lang="less" scoped>
322411
.cluster-domain {

dbm-ui/frontend/src/views/db-manage/mongodb/MONGODB_CUTOFF/components/spec-column/Index.vue

+11-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
:label="t('新机规格')"
1818
:loading="loading"
1919
:min-width="200"
20-
required>
20+
required
21+
:rules="rules">
2122
<EditableSelect
2223
v-model="modelValue"
2324
filterable
@@ -77,6 +78,15 @@
7778

7879
const { t } = useI18n();
7980

81+
const rules = [
82+
{
83+
message: t('新机规格不能为空'),
84+
required: true,
85+
trigger: 'change',
86+
validator: (value: number) => value > 0,
87+
},
88+
];
89+
8090
const specList = ref<
8191
{
8292
isCurrentSpec?: boolean;

dbm-ui/frontend/src/views/ticket-center/common/ticket-detail/components/task-info/com-factory/mongodb/Cutoff.vue

+28-26
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,31 @@
1212
-->
1313

1414
<template>
15-
<DbOriginalTable
16-
:columns="columns"
17-
:data="tableData" />
15+
<BkTable
16+
:data="tableData"
17+
:merge-cells="mergeCells"
18+
show-overflow-tooltip>
19+
<BkTableColumn
20+
field="ip"
21+
fixed="left"
22+
:label="t('待替换的主机')" />
23+
<BkTableColumn
24+
field="role"
25+
:label="t('角色类型')" />
26+
<BkTableColumn
27+
field="cluster"
28+
:label="t('所属集群')" />
29+
<BkTableColumn
30+
field="spec"
31+
:label="t('新机规格')" />
32+
</BkTable>
1833
</template>
1934

2035
<script setup lang="tsx">
2136
import { useI18n } from 'vue-i18n';
2237

38+
import type { VxeTablePropTypes } from '@blueking/vxe-table';
39+
2340
import TicketModel, { type Mongodb } from '@services/model/ticket/ticket';
2441

2542
import { TicketTypes } from '@common/const';
@@ -37,30 +54,9 @@
3754

3855
const { t } = useI18n();
3956

40-
const { clusters, infos, specs } = props.ticketDetails.details;
41-
42-
const columns = [
43-
{
44-
field: 'ip',
45-
label: t('待替换的主机'),
46-
showOverflowTooltip: true,
47-
},
48-
{
49-
field: 'role',
50-
label: t('角色类型'),
51-
},
52-
{
53-
field: 'cluster',
54-
label: t('所属集群'),
55-
showOverflowTooltip: true,
56-
},
57-
{
58-
field: 'spec',
59-
label: t('新机规格'),
60-
showOverflowTooltip: true,
61-
},
62-
];
57+
const mergeCells = ref<VxeTablePropTypes.MergeCells>([]);
6358

59+
const { clusters, infos, specs } = props.ticketDetails.details;
6460
const tableData = infos.reduce(
6561
(results, item) => {
6662
const types = ['mongo_config', 'mongodb', 'mongos'] as ['mongo_config', 'mongodb', 'mongos'];
@@ -75,6 +71,12 @@
7571
results.push(...list);
7672
}
7773
});
74+
mergeCells.value.push({
75+
col: 2,
76+
colspan: 1,
77+
row: mergeCells.value.length ? mergeCells.value[mergeCells.value.length - 1].rowspan : 0,
78+
rowspan: item.mongo_config.length + item.mongodb.length + item.mongos.length,
79+
});
7880
return results;
7981
},
8082
[] as {

0 commit comments

Comments
 (0)