diff --git a/mockServer/assets/json/bundle.json b/mockServer/assets/json/bundle.json
index 95c91c0da..6dc3f724a 100644
--- a/mockServer/assets/json/bundle.json
+++ b/mockServer/assets/json/bundle.json
@@ -4318,7 +4318,7 @@
"defaultValue": ""
}
},
- "onBeforeMount": "console.log('table on load'); this.options = source.data"
+ "onBeforeMount": "this.options = source.data"
},
"configure": {
"loop": true,
@@ -5832,6 +5832,7 @@
"required": true,
"readOnly": false,
"disabled": false,
+ "onChange": "function(val) { this.setPropertyProps('modelValue', { 'options': val }) }",
"defaultValue": "",
"cols": 12,
"bindState": false,
@@ -5842,7 +5843,7 @@
"description": {
"zh_CN": "tabs"
},
- "labelPosition": "none"
+ "labelPosition": "top"
},
{
"property": "modelValue",
@@ -5856,13 +5857,25 @@
"disabled": false,
"cols": 12,
"widget": {
- "component": "MetaInput",
- "props": {}
+ "component": "MetaSelect",
+ "props": {
+ "clearable": false,
+ "options": [
+ {
+ "label": "标签页1",
+ "value": "first"
+ },
+ {
+ "label": "标签页2",
+ "value": "second"
+ }
+ ]
+ }
},
"description": {
"zh_CN": "绑定值,选中选项卡的 name"
},
- "labelPosition": "left"
+ "labelPosition": "top"
},
{
"property": "with-add",
@@ -7536,7 +7549,7 @@
"contentMenu": {
"actions": ["create symbol"]
},
- "onBeforeMount": "console.log('table on load'); this.pager = source.pager; this.fetchData = source.fetchData; this.data = source.data ;this.columns = source.columns"
+ "onBeforeMount": "this.pager = source.pager; this.fetchData = source.fetchData; this.data = source.data ;this.columns = source.columns"
},
"configure": {
"loop": true,
@@ -9819,7 +9832,17 @@
"schema": {
"componentName": "TinyTabs",
"props": {
- "modelValue": "first"
+ "modelValue": "first",
+ "tabs": [
+ {
+ "label": "标签页1",
+ "value": "first"
+ },
+ {
+ "label": "标签页2",
+ "value": "second"
+ }
+ ]
},
"children": [
{
diff --git a/packages/common/component/ConfigItem.vue b/packages/common/component/ConfigItem.vue
index c0fd195e7..07776bf33 100644
--- a/packages/common/component/ConfigItem.vue
+++ b/packages/common/component/ConfigItem.vue
@@ -364,7 +364,7 @@ export default {
const executeRelationAction = (value, preValue) => {
const { onChange, rules } = props.property
- const { setProp, delProp } = useProperties()
+ const { setProp, delProp, setPropertyProps } = useProperties()
// 关联
if (onChange && propsObj) {
@@ -374,6 +374,7 @@ export default {
config: {
...widget.value?.props
},
+ setPropertyProps,
setProp: setProp,
delProp
})
diff --git a/packages/common/component/MetaContainer.vue b/packages/common/component/MetaContainer.vue
index be0e300b4..32ffc13a2 100644
--- a/packages/common/component/MetaContainer.vue
+++ b/packages/common/component/MetaContainer.vue
@@ -4,7 +4,7 @@
@@ -20,7 +20,7 @@
diff --git a/packages/common/component/MetaSelect.vue b/packages/common/component/MetaSelect.vue
index d9a59b8f9..9de76d55d 100644
--- a/packages/common/component/MetaSelect.vue
+++ b/packages/common/component/MetaSelect.vue
@@ -4,7 +4,7 @@
:multiple="multi"
:is-drop-inherit-width="true"
:show-alloption="false"
- :clearable="true"
+ :clearable="clearable"
@change="handleChange"
>
@@ -70,6 +70,10 @@ export default {
options: {
type: Array,
default: () => []
+ },
+ clearable: {
+ type: Boolean,
+ default: true
}
},
emits: ['update:modelValue'],
@@ -98,7 +102,12 @@ export default {
}
watchEffect(() => {
- state.selected = props.modelValue ?? ''
+ if (!props.options.find((item) => item.value === props.modelValue)) {
+ state.selected = props.options[0].value || ''
+ emit('update:modelValue', state.selected)
+ } else {
+ state.selected = props.modelValue ?? ''
+ }
})
return {
diff --git a/packages/controller/src/useProperties.js b/packages/controller/src/useProperties.js
index 8c0a91caf..f9e8bf7f2 100644
--- a/packages/controller/src/useProperties.js
+++ b/packages/controller/src/useProperties.js
@@ -11,12 +11,13 @@
*/
import { toRaw, nextTick, shallowReactive, ref } from 'vue'
-import { constants } from '@opentiny/tiny-engine-utils'
+import { constants, utils } from '@opentiny/tiny-engine-utils'
import useCanvas from './useCanvas'
import useResource from './useResource'
import useTranslate from './useTranslate'
const { COMPONENT_NAME } = constants
+const { delNullKey } = utils
const propsUpdateKey = ref(0)
const otherBaseKey = {
@@ -153,6 +154,94 @@ const properties = shallowReactive({
const isPageOrBlock = (schema) => [COMPONENT_NAME.Block, COMPONENT_NAME.Page].includes(schema?.componentName)
+const getPropertyItem = (propertyName, properties) => {
+ const { pageState } = useCanvas()
+ if (!properties) {
+ properties = pageState.properties
+ }
+ let propertyItem = null
+ properties.some((property) => {
+ const findInParentNode = property.content.some((item) => {
+ if (item.property === propertyName) {
+ propertyItem = item
+ return true
+ }
+ return false
+ })
+ return (
+ findInParentNode ??
+ property.content.some((item) => {
+ if (item.properties) {
+ propertyItem = getPropertyItem(propertyName, item.properties)
+ return propertyItem
+ }
+ return false
+ })
+ )
+ })
+ return propertyItem
+}
+
+/**
+ * get property value
+ * @param {string} propertyName
+ * @returns any
+ */
+const getPropertyValue = (propertyName) => {
+ const propertyItem = getPropertyItem(propertyName)
+ return propertyItem?.widget?.props?.modelValue
+}
+
+/**
+ * set property value
+ * @param {string} propertyName
+ * @param {any} value
+ */
+const setPropertyValue = (propertyName, value) => {
+ const propertyItem = getPropertyItem(propertyName)
+ if (propertyItem?.widget?.props) {
+ propertyItem.widget.props.modelValue = value
+ }
+}
+
+/**
+ * get property props
+ * @param {string} propertyName
+ * @returns object
+ */
+const getPropertyProps = (propertyName) => {
+ const propertyItem = getPropertyItem(propertyName)
+ return propertyItem?.widget?.props
+}
+
+/**
+ * set property props
+ * @param {string} propertyName
+ * @param {object} props
+ */
+const setPropertyProps = (propertyName, props = {}) => {
+ const propertyItem = getPropertyItem(propertyName)
+ if (propertyItem?.widget?.props) {
+ const realProps = delNullKey(props, { deep: false, keepRef: true })
+ if (!propertyItem?.widget?.props) {
+ propertyItem.widget.props = {}
+ }
+ Object.assign(propertyItem.widget?.props, realProps)
+ }
+}
+
+const getPropertyHidden = (propertyName) => {
+ const propertyItem = getPropertyItem(propertyName)
+ return propertyItem?.hidden ?? false
+}
+
+const setPropertyHidden = (propertyName, hidden) => {
+ const propertyItem = getPropertyItem(propertyName)
+ if (propertyItem) {
+ propertyItem.hidden = Boolean(hidden)
+ }
+}
+
const getProps = (schema, parent) => {
// 1 现在选中的节点和当前节点一样,不需要重新计算, 2 默认进来由于scheme和properities.schema相等,因此判断如果是“页面或者区块”需要进入if判断
if (schema && (properties.schema !== schema || isPageOrBlock(schema))) {
@@ -219,6 +308,12 @@ const setProps = (schema) => {
export default function () {
return {
+ getPropertyHidden,
+ setPropertyHidden,
+ getPropertyValue,
+ setPropertyValue,
+ getPropertyProps,
+ setPropertyProps,
getProps,
getProp,
setProps,
diff --git a/packages/design-core/public/mock/bundle.json b/packages/design-core/public/mock/bundle.json
index 01d2c2b24..da027960e 100644
--- a/packages/design-core/public/mock/bundle.json
+++ b/packages/design-core/public/mock/bundle.json
@@ -7035,7 +7035,7 @@
"defaultValue": ""
}
},
- "onBeforeMount": "console.log('table on load'); this.options = source.data"
+ "onBeforeMount": "this.options = source.data"
},
"configure": {
"loop": true,
@@ -8529,6 +8529,7 @@
"required": true,
"readOnly": false,
"disabled": false,
+ "labelPosition": "top",
"cols": 12,
"widget": {
"component": "MetaSwitch",
@@ -8536,8 +8537,7 @@
},
"description": {
"zh_CN": "是否显示标题后编辑 ICON"
- },
- "labelPosition": "left"
+ }
},
{
"property": "tabs",
@@ -8549,6 +8549,7 @@
"required": true,
"readOnly": false,
"disabled": false,
+ "onChange": "function(val) { this.setPropertyProps('modelValue', { 'options': val }) }",
"defaultValue": "",
"cols": 12,
"bindState": false,
@@ -8559,7 +8560,7 @@
"description": {
"zh_CN": "tabs"
},
- "labelPosition": "none"
+ "labelPosition": "top"
},
{
"property": "modelValue",
@@ -8573,13 +8574,25 @@
"disabled": false,
"cols": 12,
"widget": {
- "component": "MetaInput",
- "props": {}
+ "component": "MetaSelect",
+ "props": {
+ "clearable": false,
+ "options": [
+ {
+ "label": "标签页1",
+ "value": "first"
+ },
+ {
+ "label": "标签页2",
+ "value": "second"
+ }
+ ]
+ }
},
"description": {
"zh_CN": "绑定值,选中选项卡的 name"
},
- "labelPosition": "left"
+ "labelPosition": "top"
},
{
"property": "with-add",
@@ -10253,7 +10266,7 @@
"contentMenu": {
"actions": ["create symbol"]
},
- "onBeforeMount": "console.log('table on load'); this.pager = source.pager; this.fetchData = source.fetchData; this.data = source.data ;this.columns = source.columns"
+ "onBeforeMount": "this.pager = source.pager; this.fetchData = source.fetchData; this.data = source.data ;this.columns = source.columns"
},
"configure": {
"loop": true,
@@ -13606,7 +13619,17 @@
"schema": {
"componentName": "TinyTabs",
"props": {
- "modelValue": "first"
+ "modelValue": "first",
+ "tabs": [
+ {
+ "label": "标签页1",
+ "value": "first"
+ },
+ {
+ "label": "标签页2",
+ "value": "second"
+ }
+ ]
},
"children": [
{
diff --git a/packages/utils/src/utils/index.js b/packages/utils/src/utils/index.js
index e16359c15..9f6a13322 100644
--- a/packages/utils/src/utils/index.js
+++ b/packages/utils/src/utils/index.js
@@ -12,6 +12,7 @@
import { isRef, isProxy, unref, toRaw } from 'vue'
import { isObject, isArray } from '@opentiny/vue-renderless/grid/static'
+import { isEmptyObject } from '@opentiny/vue-renderless/common/type'
export const fun_ctor = Function
@@ -48,6 +49,54 @@ export function parseFunction(rawCode, context = {}) {
}
}
+/**
+ * 判断传入的值是否可以过滤
+ * @param {any} value
+ * @param {boolean} keepRef 是否过滤空数组或空对象,默认false
+ * @returns boolean
+ */
+export const isOmitValue = (value, keepRef = false) => {
+ if (Array.isArray(value) && value.length === 0 && !keepRef) {
+ return true
+ }
+ if (typeof value === 'object' && isEmptyObject(value) && !keepRef) {
+ return true
+ }
+
+ return ['', null, undefined].includes(value)
+}
+
+/**
+ * 过滤对象属性、数组的空值
+ * 过滤的空值包括: []、{}、''、null、undefined
+ * @param {object | Array} obj
+ * @param {{deep: boolean; keepRef: boolean}} deep 是否深度过滤; keepRef 是否过滤空数组或空对象
+ * @returns 过滤的对象
+ */
+export const delNullKey = (obj = {}, options = {}) => {
+ if (!(obj instanceof Object)) {
+ return obj
+ }
+
+ const { deep = true, keepRef = false } = options
+ if (Array.isArray(obj)) {
+ return obj.map((item) => delNullKey(item, deep)).filter((value) => !isOmitValue(value))
+ }
+
+ const newObj = {}
+ for (let [key, value] of Object.entries(obj)) {
+ if (typeof value === 'object' && value !== null) {
+ const trimValue = deep ? delNullKey(value, deep, keepRef) : value
+ if (!isOmitValue(trimValue, keepRef)) {
+ newObj[key] = trimValue
+ }
+ } else if (!isOmitValue(value)) {
+ newObj[key] = value
+ }
+ }
+ return newObj
+}
+
/**
* 将字符串包含的特殊正则字符逃逸出来 (加上 \\)
* 适用于 new Regexp(`${str}`)的情形,防止变量 str 具有一些特殊的正则字符串导致挂掉或者不符合期望
diff --git a/packages/utils/test/delNullKey.test.js b/packages/utils/test/delNullKey.test.js
new file mode 100644
index 000000000..6b9aa8758
--- /dev/null
+++ b/packages/utils/test/delNullKey.test.js
@@ -0,0 +1,85 @@
+import { expect, test } from 'vitest'
+import { delNullKey } from '../src/utils'
+
+test('should not be object value', () => {
+ expect(delNullKey(12, { deep: true, keepRef: false })).toBe(12)
+ expect(delNullKey('AB', { deep: true, keepRef: false })).toBe('AB')
+ expect(delNullKey(true, { deep: true, keepRef: false })).toBe(true)
+})
+
+const testObj = {
+ number1: 1,
+ number2: 0,
+ string1: '1',
+ string2: '',
+ nullValue: null,
+ undefineValue: undefined,
+ array1: [],
+ array2: [1, 2, 3],
+ object1: {},
+ object2: { name: 'zh', id: 1 },
+ object3: {
+ inside: {
+ insideString: '',
+ insideNull: null,
+ insideUndefined: undefined,
+ insideStringNotEmpty: '12'
+ }
+ }
+}
+
+test('should be object value, deep is false', () => {
+ expect(delNullKey(testObj, { deep: false, keepRef: false })).toEqual({
+ number1: 1,
+ number2: 0,
+ string1: '1',
+ array2: [1, 2, 3],
+ object2: { name: 'zh', id: 1 },
+ object3: {
+ inside: {
+ insideString: '',
+ insideNull: null,
+ insideUndefined: undefined,
+ insideStringNotEmpty: '12'
+ }
+ }
+ })
+})
+
+test('should be object value, keepRef is false', () => {
+ expect(delNullKey(testObj, { deep: true, keepRef: false })).toEqual({
+ array2: [1, 2, 3],
+ number1: 1,
+ number2: 0,
+ object2: {
+ id: 1,
+ name: 'zh'
+ },
+ object3: {
+ inside: {
+ insideStringNotEmpty: '12'
+ }
+ },
+ string1: '1'
+ })
+})
+
+test('should be object value, keepRef is true', () => {
+ expect(delNullKey(testObj, { deep: true, keepRef: true })).toEqual({
+ array1: [],
+ array2: [1, 2, 3],
+ number1: 1,
+ number2: 0,
+ object1: {},
+ object2: {
+ id: 1,
+ name: 'zh'
+ },
+ object3: {
+ inside: {
+ insideStringNotEmpty: '12'
+ }
+ },
+ string1: '1'
+ })
+})
diff --git a/packages/utils/test/isOmitValue.test.js b/packages/utils/test/isOmitValue.test.js
new file mode 100644
index 000000000..d132aac28
--- /dev/null
+++ b/packages/utils/test/isOmitValue.test.js
@@ -0,0 +1,18 @@
+import { expect, test } from 'vitest'
+import { isOmitValue } from '../src/utils'
+
+test('should not be array or object value', () => {
+ expect(isOmitValue(null)).toBe(true)
+ expect(isOmitValue(undefined)).toBe(true)
+ expect(isOmitValue('')).toBe(true)
+})
+
+test('should be array value', () => {
+ expect(isOmitValue([])).toBe(true)
+ expect(isOmitValue([1, 2, 3], true)).toBe(false)
+})
+
+test('should be object value', () => {
+ expect(isOmitValue({})).toBe(true)
+ expect(isOmitValue({ name: 'astro', id: 1 })).toBe(false)
+})