Skip to content

Commit

Permalink
feat: 插件包描述文件支持定义配置模板填写参数(closed #654)
Browse files Browse the repository at this point in the history
  • Loading branch information
GONGONGONG authored and wyyalt committed Sep 7, 2023
1 parent 6053e0e commit 01aaff8
Show file tree
Hide file tree
Showing 13 changed files with 882 additions and 58 deletions.
85 changes: 85 additions & 0 deletions frontend/src/components/RussianDolls/DollForm.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
.russian-dolls-form {
padding-right: 32px;

.bk-form-item + .bk-form-item {
margin-top: 16px;
}

.nodeman-icon {
font-size: 16px;
cursor: pointer;
&:hover:not(.disabled) {
color: #3a84ff;
}

&.disabled {
color: #c4c6cc;
cursor: not-allowed;
}
& + .nodeman-icon {
margin-left: 10px
}
}
}

.item-array {
& > .bk-form-content {
display: flex;
align-items: center;
}
.array-child-group {
display: flex;
flex-direction: column;
flex: 1;
/* padding-top: 32px; */
}
.array-child {
display: flex;
flex: 1;
padding-right: 16px;
background: #f5f7fa;

& + .array-content-add {
margin-top: 12px;
}
}
}

.array-content-add {
flex: 1;
border: 1px dashed #3a84ff;
border-radius: 2px;
color: #3a84ff;
cursor: pointer;
text-align: center;
.nodeman-icon {
font-size: 16px;
}
}

.array-child {

.item-object {
flex: 1;
/* 特殊操作 */
position: relative;
padding: 16px 0;
}
}

.array-child + .array-child {
margin-top: 12px;
}

.array-content-delete {
position: absolute;
left: 100%;
}
.child-btns {
display: flex;
align-items: flex-start;
padding-top: 8px;
width: 50px;
font-size: 16px;
flex-shrink: 0;
}
181 changes: 181 additions & 0 deletions frontend/src/components/RussianDolls/DollForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
<template>
<bk-form
class="russian-dolls-form"
ref="russianDollsFormRef"
:model="formModel"
:rules="rules"
:label-width="labelWidth">
<DollIndex :value-prop="''" :value="formModel" :item="baseForm" :item-index="0" />
</bk-form>
</template>

<script>
import { defineComponent, provide, ref, toRefs, watch } from 'vue';
import { getRealProp, initSchema } from './create';
import { deepClone } from '@/common/util';
export default defineComponent({
name: 'RussianDollsForm',
props: {
data: () => [],
layout: () => [],
rules: () => ({}),
value: () => ({}),
labelWidth: {
type: Number,
default: 150,
},
},
emits: ['change', 'focus', 'blur'],
setup(props, { emit }) {
const russianDollsFormRef = ref();
const baseForm = ref({});
const formatList = ref([]);
const formModel = ref(deepClone(props.value));
const typeArr = ['object', 'array'];
const getLastLevelData = (propItem) => {
const { property, type } = propItem;
const keys = property.split('.');
// 通过类型 来确定取值是倒数第一层或第二层
const lastIndex = type === 'object' ? keys.length : keys.length - 1;
let lastLevelData = formModel.value;
keys.forEach((key, index) => {
if (lastIndex > index) {
lastLevelData = lastLevelData[key];
}
});
return lastLevelData;
};
// type: add delete assign
// target: index or value
const updateFormData = (propItem, type, target) => {
const data = getLastLevelData(propItem);
const { property } = propItem;
let prop = '';
let value = target;
if (type === 'assign') {
if (Array.isArray(data)) {
const propKey = property?.split('.') || [];
prop = propKey[propKey.length - 1] || 0;
data[prop] = target;
} else {
prop = propItem.prop;
data[propItem.prop] = target;
}
} else {
if (type === 'add') {
const [child] = getDefaultValue(propItem);
data.splice(target, 0, child);
} else {
data.splice(target, 1);
}
value = data;
}
emit('change', { property, prop, value });
};
const getDefaultValue = (propItem, isInit) => {
if (typeArr.includes(propItem.type)) {
if (propItem.type === 'object') {
return propItem.children.reduce((obj, child) => {
obj[child.prop] = getDefaultValue(child, isInit);
return obj;
}, {});
}
return isInit ? [] : [getDefaultValue(propItem.children[0], true)];
}
return propItem.defaultValue;
};
const isDifferentType = (itemConfig, form) => {
const { type, prop } = itemConfig;
let different = false;
if (['array', 'object'].includes(type) && typeof form !== 'object') {
different = true;
}
different = type === 'array'
? !Array.isArray(form[prop])
: (typeof form[prop]) !== type;
return different;
};
// 格式化form-item 属性的相关配置
const initFormList = (data) => {
const schema = initSchema(data);
baseForm.value = schema;
formatList.value.splice(0, formatList.value.length, ...schema.children);
};
// 补齐form需要的数据
const completionArrayData = (list, form) => {
// 很关键。form一定要保证是个obj
const formData = typeof form === 'object' && !Array.isArray(form)
? deepClone(form)
: {};
try {
list.forEach((item) => {
if (isDifferentType(item, formData) || !Object.prototype.hasOwnProperty.call(formData, item.prop)) {
formData[item.prop] = getDefaultValue(item, true);
} else {
if (item.type === 'array') {
formData[item.prop] = formData[item.prop]
.map(formItem => completionArrayData(item.children[0].children, formItem));
}
}
});
return formData;
} catch (err) {
console.log(err);
return {};
}
};
const validate = () => russianDollsFormRef.value?.validate();
const clearValidate = () => russianDollsFormRef.value?.clearError();
const getFormData = () => deepClone(formModel.value);
const inputFocus = (value, event) => {
emit('focus', value, event);
};
const inputBlur = (value, event) => {
emit('blur', value, event);
};
initFormList(props.data);
formModel.value = completionArrayData(formatList.value, props.value);
provide('updateFormData', updateFormData);
provide('inputFocus', inputFocus);
provide('inputBlur', inputBlur);
provide('getRealProp', getRealProp);
watch(() => props.data, (value) => {
initFormList(value);
}, { deep: true });
// 需要补齐数组的数据
watch(() => props.value, () => {
formModel.value = completionArrayData(formatList.value, props.value);
}, { deep: true });
return {
...toRefs(props),
russianDollsFormRef,
formModel,
baseForm,
formatList,
validate,
clearValidate,
getFormData,
};
},
});
</script>
<style lang="postcss">
@import "./DollForm.css";
</style>
Loading

0 comments on commit 01aaff8

Please sign in to comment.