import { IPublicTypeComponentMetadata, IPublicTypeSnippet, } from '@alilc/lowcode-types' const parsePxNumber = (value: any): number | undefined => { if (typeof value === 'number' && Number.isFinite(value)) { return value } if (typeof value === 'string') { const numberLike = Number(value.trim()) if (Number.isFinite(numberLike)) { return numberLike } const matched = value.match(/^\s*(\d+(?:\.\d+)?)\s*px\s*$/i) if (!matched) return undefined const parsed = Number(matched[1]) return Number.isFinite(parsed) ? parsed : undefined } return undefined } const TeletextListMeta: IPublicTypeComponentMetadata = { group: '低代码组件', componentName: 'TeletextList', title: '图文列表', docUrl: '', screenshot: '', devMode: 'proCode', category: '信息展示', npm: { package: '@dckj-npm/dc-material', version: '0.1.8', exportName: 'TeletextList', main: 'src/index.tsx', destructuring: true, subName: '', }, configure: { props: [ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分组一:数据与内容 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { type: 'group', title: '数据与内容', display: 'accordion', items: [ { title: '标题', name: 'title', description: '列表头部标题文本', setter: { componentName: 'StringSetter', isRequired: false, initialValue: '' }, }, { title: '更多文字', name: 'moreText', description: '更多按钮文字,为空时不显示', setter: { componentName: 'StringSetter', isRequired: false, initialValue: '' }, }, { title: '文本槽位数', name: 'textLines', description: '每项渲染几个文本字段:2=标题+说明,3=加text3,4=加text3+text4。这是"显示几格",不是"每格显示几行"——行数截断请到"标题/说明文字样式"分组修改"最大行数"。', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 2, defaultValue: 2, props: { min: 2, max: 4 }, }, extraProps: { setValue(target, value) { target.getProps().setPropValue('textLines', value) const dataListBindProp = (target?.parent as any)?.items?.find( (item: any) => item.name === 'dataListBind', ) if (dataListBindProp?.setter) { const baseChildren = [ { label: '图片链接', value: 'image' }, { label: '类型', value: 'itemType' }, { label: '标题', value: 'title' }, { label: '说明', value: 'description' }, ] const textChildren = value > 2 ? Array.from({ length: value - 2 }).map((_, i) => ({ label: `文本${i + 3}`, value: `text${i + 3}`, })) : [] dataListBindProp.setter.props.attributes[0].children = [ ...baseChildren, ...textChildren, ] } }, }, }, { title: '数据源绑定', name: 'dataListBind', setter: { componentName: 'SetterFormVariable', props: { attributes: [ { label: '图文数据', value: 'dataList', children: [ { label: '图片链接', value: 'image' }, { label: '行类型(值为"开关"时该行显示开关控件)', value: 'itemType' }, { label: '标题', value: 'title' }, { label: '说明', value: 'description' }, ], }, ], }, }, extraProps: {}, }, { title: { label: { type: 'i18n', 'en-US': 'dataList', 'zh-CN': '静态数据' }, tip: 'dataList | 静态数据' }, name: 'dataList', description: '列表静态数据,绑定数据源后此项被覆盖', setter: { componentName: 'ArraySetter', props: { itemSetter: { componentName: 'ObjectSetter', props: { config: { items: [ { title: { label: { type: 'i18n', 'en-US': 'title', 'zh-CN': '标题' } }, name: 'title', setter: { componentName: 'StringSetter', isRequired: false, initialValue: '' }, }, { title: { label: { type: 'i18n', 'en-US': 'image', 'zh-CN': '图片地址' } }, name: 'image', setter: { componentName: 'CustomImageSetter', isRequired: false, initialValue: '' }, }, { title: { label: { type: 'i18n', 'en-US': 'description', 'zh-CN': '描述' } }, name: 'description', setter: { componentName: 'StringSetter', isRequired: false, initialValue: '' }, }, { title: { label: { type: 'i18n', 'en-US': 'text3', 'zh-CN': '文本3' } }, name: 'text3', description: '文本槽位数 ≥ 3 时显示', setter: { componentName: 'StringSetter', isRequired: false, initialValue: '' }, }, { title: { label: { type: 'i18n', 'en-US': 'text4', 'zh-CN': '文本4' } }, name: 'text4', description: '文本槽位数 = 4 时显示', setter: { componentName: 'StringSetter', isRequired: false, initialValue: '' }, }, { title: { label: { type: 'i18n', 'en-US': 'itemType', 'zh-CN': '行类型' } }, name: 'itemType', description: '值为"开关"时该行显示开关控件,留空为默认文字行', setter: { componentName: 'SelectSetter', props: { options: [ { label: '默认(文字行)', value: '' }, { label: '开关', value: '开关' }, ], }, initialValue: '', }, }, ], extraSetter: { componentName: 'MixedSetter', isRequired: false, props: {} }, }, }, }, }, initialValue: (target) => { const existing = target?.node?.schema?.props?.dataList ?? target?.getProps?.()?.getPropValue?.('dataList') if (existing !== undefined) return existing return DEFAULT_DATA_LIST }, }, extraProps: { ignoreDefaultValue: () => true, getValue: (target: any, currentValue: any) => { if (currentValue !== undefined) return currentValue const fromSchema = target?.node?.schema?.props?.dataList if (fromSchema !== undefined) return fromSchema return undefined }, }, }, { title: '类型', name: 'type', description: '列表类型', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '图文', value: 'textAndImg' }, { label: '仅图片', value: 'imgOnly' }, { label: '仅文字', value: 'textOnly' }, ], }, initialValue: 'textAndImg', }, }, { title: '是否购物车列表', name: 'isShoppingCart', setter: { componentName: 'BoolSetter', isRequired: true, initialValue: false }, }, { title: '是否愿望单列表', name: 'isWishList', setter: { componentName: 'BoolSetter', isRequired: true, initialValue: false }, }, { title: '是否用户菜单列表', name: 'isUserMenu', setter: { componentName: 'BoolSetter', isRequired: true, initialValue: false }, }, ], }, // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分组二:列表布局 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { type: 'group', title: '列表布局', display: 'accordion', items: [ { title: '子项分布', name: 'itemRowAlign', description: '列方向或横向一行滚动', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '纵向列表', value: 'column' }, { label: '横向滚动', value: 'row' }, ], }, initialValue: 'column', }, }, { title: '子项列数', name: 'itemColumns', description: '纵向模式时的列数(1-4)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: (target) => { const p = target.getProps().getPropValue('imagePlacement') return p === 'top' || p === 'bottom' ? 2 : 1 }, defaultValue: (target) => { const p = target.getProps().getPropValue('imagePlacement') return p === 'top' || p === 'bottom' ? 2 : 1 }, props: { min: 1, max: 4 }, }, }, { title: '子项间距', name: 'itemGap', description: '子项之间的间距(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 10, defaultValue: 10, props: { min: 0 } }, }, ], }, // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分组三:列表项样式 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { type: 'group', title: '子项样式', display: 'accordion', items: [ { title: '内边距', name: 'itemPadding', description: '列表项内边距(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 0, defaultValue: 0, props: { min: 0 } }, }, { title: '背景颜色', name: 'itemBgColor', description: '列表项背景颜色', setter: { componentName: 'ColorSetter', isRequired: false, initialValue: '#ffffff' }, }, { title: '圆角', name: 'itemBorderRadius', description: '列表项圆角(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 0, defaultValue: 0, props: { min: 0 } }, }, { title: '边框颜色', name: 'itemBorderColor', description: '列表项边框颜色', setter: { componentName: 'ColorSetter', isRequired: false, initialValue: '' }, }, { title: '边框宽度', name: 'itemBorderWidth', description: '列表项边框宽度(px),设置边框颜色后生效', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 0, defaultValue: 0, props: { min: 0, max: 10 } }, }, ], }, // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分组四:图片样式 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { type: 'group', title: '图片样式', display: 'accordion', items: [ { title: '图片位置', name: 'imagePlacement', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '左', value: 'left' }, { label: '右', value: 'right' }, { label: '上', value: 'top' }, { label: '下', value: 'bottom' }, { label: '无', value: 'none' }, ], }, initialValue: 'left', }, extraProps: {}, }, { title: '图文对齐', name: 'textAlign', description: '图片与文字的交叉轴对齐方式', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '顶对齐', value: 'flex-start' }, { label: '居中', value: 'center' }, { label: '底对齐', value: 'flex-end' }, ], }, initialValue: 'flex-start', }, }, { title: '图文间距', name: 'textImgGap', description: '图片与文字区域之间的间距(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 16, defaultValue: 16, props: { min: 0 } }, }, { title: '宽度', name: 'imgWidth', description: '图片宽度(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 100, props: { min: 10 } }, extraProps: { ignoreDefaultValue: () => true, getValue: (target: any, value: any) => { const parsed = parsePxNumber(value) if (parsed !== undefined) return parsed const fromSchema = parsePxNumber(target?.node?.schema?.props?.imgWidth) if (fromSchema !== undefined) return fromSchema return 100 }, }, }, { title: '高度', name: 'imgHeight', description: '图片高度(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 100, props: { min: 10 } }, extraProps: { ignoreDefaultValue: () => true, getValue: (target: any, value: any) => { const parsed = parsePxNumber(value) if (parsed !== undefined) return parsed const fromSchema = parsePxNumber(target?.node?.schema?.props?.imgHeight) if (fromSchema !== undefined) return fromSchema return 100 }, }, }, { title: '圆角', name: 'imgBorderRadius', description: '图片圆角(px),设为 999 可得到圆形', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 0, defaultValue: 0, props: { min: 0 } }, }, { title: '填充方式', name: 'imgObjectFit', description: '图片在容器内的填充方式', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '覆盖', value: 'cover' }, { label: '适应', value: 'contain' }, { label: '拉伸', value: 'fill' }, { label: '原始', value: 'none' }, ], }, initialValue: 'cover', }, }, ], }, // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分组五:标题文字样式 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { type: 'group', title: '标题文字样式', display: 'accordion', items: [ { title: '颜色', name: 'titleColor', setter: { componentName: 'ColorSetter', isRequired: false, initialValue: '' }, }, { title: '字体大小', name: 'titleFontSize', description: '标题字体大小(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 14, defaultValue: 14, props: { min: 10, max: 60 } }, }, { title: '字重', name: 'titleFontWeight', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '常规', value: 'normal' }, { label: '中等', value: '500' }, { label: '半粗', value: '600' }, { label: '粗体', value: 'bold' }, ], }, initialValue: '600', }, }, { title: '最大行数', name: 'titleLineClamp', description: '超出后显示省略号,0 表示不限制', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 1, defaultValue: 1, props: { min: 0, max: 10 } }, }, ], }, // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分组六:说明文字样式 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { type: 'group', title: '说明文字样式', display: 'accordion', items: [ { title: '颜色', name: 'descriptionColor', setter: { componentName: 'ColorSetter', isRequired: false, initialValue: '' }, }, { title: '字体大小', name: 'descriptionFontSize', description: '说明字体大小(px)', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 13, defaultValue: 13, props: { min: 10, max: 60 } }, }, { title: '字重', name: 'descriptionFontWeight', setter: { componentName: 'RadioGroupSetter', props: { options: [ { label: '常规', value: 'normal' }, { label: '中等', value: '500' }, { label: '半粗', value: '600' }, { label: '粗体', value: 'bold' }, ], }, initialValue: 'normal', }, }, { title: '最大行数', name: 'descriptionLineClamp', description: '超出后显示省略号,0 表示不限制', setter: { componentName: 'NumberSetter', isRequired: false, initialValue: 1, defaultValue: 1, props: { min: 0, max: 10 } }, }, ], }, // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 浮层图标 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { title: '浮层图标', name: 'iconList', description: '在每个列表项上叠加绝对定位图标(如角标、标签等)', extraProps: { display: 'accordion', defaultCollapsed: true }, setter: { componentName: 'ArraySetter', props: { itemSetter: { componentName: 'ObjectSetter', props: { config: { items: [ { name: 'icon', title: '图标', setter: 'IconSetter', isRequired: true, props: { hasClear: false }, }, { name: 'size', title: '图标尺寸', setter: { componentName: 'SelectSetter', props: { options: [ { label: 'xxs', value: 'xxs' }, { label: 'xs', value: 'xs' }, { label: 'small', value: 'small' }, { label: 'medium', value: 'medium' }, { label: 'large', value: 'large' }, { label: 'xl', value: 'xl' }, { label: 'xxl', value: 'xxl' }, { label: 'xxxl', value: 'xxxl' }, { label: 'inherit', value: 'inherit' }, ], }, }, defaultValue: 'medium', }, { name: 'position', title: '图标位置', defaultValue: 'rightTop', isRequired: true, setter: { componentName: 'SelectSetter', props: { options: [ { label: '左上角', value: 'leftTop' }, { label: '左下角', value: 'leftBottom' }, { label: '右上角', value: 'rightTop' }, { label: '右下角', value: 'rightBottom' }, { label: '自定义', value: 'customize' }, ], }, }, extraProps: { setValue: (target, value) => { target.getProps().setPropValue('position', value) }, }, }, { name: 'rightOffset', title: '右偏移', setter: 'NumberSetter', defaultValue: 0, condition: (target) => target.getProps().getPropValue('position') === 'customize', }, { name: 'topOffset', title: '上偏移', setter: 'NumberSetter', defaultValue: 0, condition: (target) => target.getProps().getPropValue('position') === 'customize', }, ], }, }, initialValue: { size: 'medium', position: 'rightTop', rightOffset: 0, topOffset: 0 }, }, }, }, }, ], supports: { style: true, events: [ { name: 'onClick', template: "onClick(key,${extParams}){\n// 点击项目的事件\nconsole.log('onClick', key);}", }, { name: 'handleMoreClick', template: "handleMoreClick(){\n// 查看更多\nconsole.log('handleMoreClick');}", }, ], }, component: { disableBehaviors: ['copy'], }, }, } const DEFAULT_DATA_LIST = [ { image: 'https://img.alicdn.com/tps/TB16TQvOXXXXXbiaFXXXXXXXXXX-120-120.svg', title: '标题名称', description: '说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字', }, { image: 'https://img.alicdn.com/tps/TB16TQvOXXXXXbiaFXXXXXXXXXX-120-120.svg', title: '标题名称', description: '说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字说明文字', }, ] const getSnippets = (textLines = 2): IPublicTypeSnippet[] => [ { title: '图文列表', screenshot: '', schema: { componentName: 'TeletextList', props: { // 将默认数据放到 snippet props 中,而非依赖 setter.initialValue。 // 因为 dataList 使用了 ignoreDefaultValue:()=>true 来阻止引擎在 // SettingFieldView remount 时覆盖已绑定的 JSExpression, // 所以 initDefaultValue 永远不会运行,首次拖入的初始数据必须由 snippet 提供。 dataList: DEFAULT_DATA_LIST, title: '列表标题', }, }, }, ] export default { ...TeletextListMeta, snippets: getSnippets(2), }