Skip to content

Commit

Permalink
feat: 替换表单
Browse files Browse the repository at this point in the history
# Reviewed, transaction id: 25012
  • Loading branch information
hyunfa committed Nov 27, 2024
1 parent 847a5d7 commit 6c5fb74
Show file tree
Hide file tree
Showing 7 changed files with 404 additions and 37 deletions.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
}
},
"dependencies": {
"@blueking/bkui-form": "^0.0.42-beta.16",
"@blueking/crypto-js-sdk": "0.0.4",
"@blueking/ip-selector": "0.2.0-beta",
"@blueking/login-modal": "^1.0.1",
Expand Down
114 changes: 114 additions & 0 deletions frontend/src/components/RussianDolls/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,120 @@ export function initSchema(schema: IItem): Doll[] {
// return formatSchema(schema, schema.property)
}

export const transformSchema = (schema: any, parentRequired: any[] = [], key: string = '') => {
if (!schema || typeof schema !== 'object') return schema;

// 处理对象类型的属性
if (schema.type === 'object' && schema.properties) {
const requiredProps = [];
for (const key in schema.properties) {
const prop = schema.properties[key];
if (typeof prop.required !== 'undefined') {
if (prop.required) {
requiredProps.push(key);
if (prop.type === 'array') {
prop['minItems'] = 1;
prop['ui:group'] = {
"props": {
"verifiable": false,
},
};
}
prop['ui:rules'] = ['required']; // 增加 ui:rules
}
// 删除所有的 required,无论是 true 还是 false
delete prop.required;
}
// 递归处理子属性
transformSchema(prop, requiredProps, key);
}
if (schema.required !== undefined) {
if (schema.required) {
parentRequired.push(key);
}
// 处理对象类型中的 required 属性
delete schema.required;
}

if (requiredProps.length > 0) {
schema.required = requiredProps;
}
}

// 处理数组类型的项
if (schema.type === 'array' && schema.items) {
const itemRequiredProps: any = [];
// 递归处理数组的每个项
transformSchema(schema.items, itemRequiredProps, key);
if (schema.required !== undefined) {
delete schema.required; // 删除数组项中的 required
}
// 不展示组校验
schema['ui:group'] = {
"props": {
"verifiable": false,
},
};
// 添加样式修改
schema['ui:props'] = {
"size": 'large'
};
// 处理描述信息
if (schema.description) {
schema['ui:group']['props']['description'] = schema.description;
}
if (schema.items.properties && Object.keys(schema.items.properties).length >= 2) {
// 如果是 key 和 value,添加 ui:component
if (schema.items.properties.key && schema.items.properties.value) {
schema['ui:component'] = { "name": "bfArray" };
}
schema.items['ui:group'] = {
"props": {
"type": "card",
},
"style": {
"background": "#F5F7FA",
},
};
}
if (schema.items.type === 'string' || schema.items.type === 'boolean' || schema.items.type === 'integer') {
// parentRequired.push(key);
}
}

// 转换 ui_component 属性到 ui:component
if (schema.ui_component && schema.ui_component.type === 'select') {
const datasource = [];
for (const optionKey in schema.ui_component.properties) {
const option = schema.ui_component.properties[optionKey];
datasource.push({
label: option.title,
value: option.value
});
}
schema['ui:component'] = {
name: 'select',
props: {
datasource,
clearable: false
}
};
delete schema.ui_component; // 删除原始的 ui_component
}

// 处理字符串、布尔和整数类型的属性,删除 required
if (schema.type === 'string' || schema.type === 'boolean' || schema.type === 'integer') {
if (schema.required !== undefined) {
if (schema.required) {
schema['ui:rules'] = ['required'];
}
// 删除 required
delete schema.required;
}
}

return schema;
}
export const createItem = (property: string, params: IItem, id?: string): Doll => {
console.log('property: ', property, params.type, '; params: ', params);
return {
Expand Down
199 changes: 199 additions & 0 deletions frontend/src/components/common/key-value.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<template>
<div class="bfArray">
<template v-if="modelValue.length">
<div v-for="(item, index) in modelValue" :key="index" class="key-value-item">
<bk-input
v-model="item.key"
:placeholder="`Key ${index + 1}`"
:class="['key-input', { 'is-error': schema.items.required?.includes('key') && item.key === '' && isValid[index]}]"
>
<span
slot="append"
class="error-tip is-error"
v-bk-tooltips="$t('必填项')">
<template v-if="schema.items.required?.includes('key') && item.key === '' && isValid[index]">
<i class="bk-icon icon-exclamation-circle-shape"></i>
</template>
</span>
</bk-input>
<bk-input
v-model="item.value"
:placeholder="`Value ${index + 1}`"
:class="['value-input', { 'is-error': schema.items.required?.includes('value') && item.value === '' && isValid[index] }]"
>
<span
slot="append"
class="error-tip is-error"
v-bk-tooltips="$t('必填项')">
<template v-if="schema.items.required?.includes('value') && item.value === '' && isValid[index]">
<i class="bk-icon icon-exclamation-circle-shape"></i>
</template>
</span>
</bk-input>
<!-- 新增的增加按钮 -->
<bk-button @click="addItemAt(index)" icon="bk-icon icon-plus-circle" class="add-btn"></bk-button>
<bk-button @click="removeItem(index)" icon="bk-icon icon-minus-circle" class="remove-btn"></bk-button>
</div>
</template>
<div v-else>
<div :class="{'add-bar': schema['ui:props'].size === 'large'}" @click.stop="() => addItem()">
<i class="nodeman-icon nc-plus" />
{{ $t('添加') }}{{ schema['ui:props'].size === 'large' ? schema.title : '' }}
</div>
</div>
</div>
</template>
<script>
import { defineComponent, ref, watch, onMounted } from 'vue';
import bus from '@/common/bus';
export default defineComponent({
name: 'KeyValueComponent',
props: {
schema: {
type: Object,
default: () => ({}),
},
// 当前路径(唯一标识)
path: {
type: String,
default: '',
},
// 是否必须字段
required: {
type: Boolean,
default: false,
},
// 全量数据(只读)
rootData: {
type: Object,
default: () => ({}),
},
// 当前值
value: {
type: [String, Number, Array, Object, Boolean],
},
// 布局配置
layout: {
type: Object,
default: () => ({}),
},
// 当前全局变量上下文
context: {
type: Object,
default: () => ({}),
},
// 当前项是否可移除
removeable: {
type: Boolean,
default: false,
},
},
setup(props, { emit }) {
const modelValue = ref([...props.value]);
// 填充校验,true表示已校验
const isValid = ref(new Array(props.value.length).fill(false));
watch(modelValue, (newValue) => {
emit('input', newValue);
}, { deep: true });
const addItem = () => {
isValid.value.push(false);
modelValue.value.push({ key: '', value: '' });
};
// 在指定位置插入新项
const addItemAt = (index) => {
isValid.value.push(false);
modelValue.value.splice(index + 1, 0, { key: '', value: '' });
};
const removeItem = (index) => {
isValid.value.pop();
modelValue.value.splice(index, 1);
};
const handleBlur = (index) => {
isValid.value[index] = true;
}
const handleFocus = (index) => {
isValid.value[index] = false;
}
const validate = (cb) => {
isValid.value.fill(true);
isValid.value = [...isValid.value];
const hasEmpty = modelValue.value.some((item) =>
(item.key === '' && props.schema.items.required?.includes('key'))
|| (item.value === '' && props.schema.items.required?.includes('value')));
if (hasEmpty) {
cb(false);
}else{
cb(true);
}
}
onMounted(() => {
bus.$on('validate', validate)
});
return {
isValid,
modelValue,
handleBlur,
handleFocus,
addItem,
addItemAt,
removeItem
};
}
});
</script>
<style lang="postcss" scoped>
.key-value-item {
display: flex;
align-items: center;
flex: 1;
padding-right: 20px;
background: #f5f7fa;
margin-bottom: 12px;
width: 100%;
}
.key-input{
margin-right: 20px;
}
.add-btn,.remove-btn {
border: none;
background-color: transparent;
}
.add-bar {
border: 1px dashed #3a84ff;
border-radius: 2px;
color: #3a84ff;
cursor: pointer;
text-align: center;
}
.key-input, .value-input {
position: relative;
}
.is-error {
/deep/ input[type=text] {
border-color: #ff5656;
color: #ff5656;
}
&.error-tip {
position: absolute;
right: 8px;
font-size: 16px;
color: #ea3636;
cursor: pointer;
}
}
/deep/ .bk-form-control .group-box {
border: none !important;
}
</style>
1 change: 1 addition & 0 deletions frontend/src/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ export default {
选择版本: 'Choose version',
'静态 - IP 选择': 'Static-IP selection',
'动态 - 拓扑选择': 'Dynamic-Topo selection',
添加: 'Add',
已部署: 'Deployed',
全部主机: 'All hosts',
搜索拓扑节点: 'Search topology node',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n/zh.js
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ export default {
升级版本: '升级版本',
升级目标: '升级目标',
参数配置: '参数配置',
添加: '添加',
执行预览: '执行预览',
部署目标: '部署目标',
部署版本: '部署版本',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ $bgColor: #f5f7fa;
}
}
.rule-create {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
Expand All @@ -433,3 +434,8 @@ $bgColor: #f5f7fa;
}
}
</style>
<style>
article {
height: 100%;
}
</style>
Loading

0 comments on commit 6c5fb74

Please sign in to comment.