From 0100de65df8517fbb88fc3a241ad022886f618de Mon Sep 17 00:00:00 2001 From: Cano1997 <58964671+Cano1997@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:57:35 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=BA=A7?= =?UTF-8?q?=E8=81=94=E4=B8=8B=E6=8B=89=E7=BC=96=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 + .../dropdown-list-editor.provider.ts | 3 + .../ibiz-cascader-dropdown.scss | 34 +++ .../ibiz-cascader-dropdown.tsx | 210 ++++++++++++++++++ src/editor/dropdown-list/index.ts | 1 + src/editor/index.ts | 8 + 6 files changed, 260 insertions(+) create mode 100644 src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.scss create mode 100644 src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index d541a7d72c..f33b78111c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ - 修复密码框点击切换显示隐藏密码图标时未切换的问题 +### Added + +- 新增级联下拉编辑器 + ## [0.7.41-alpha.10] - 2025-08-27 ### Added diff --git a/src/editor/dropdown-list/dropdown-list-editor.provider.ts b/src/editor/dropdown-list/dropdown-list-editor.provider.ts index ba487344de..e784e0cde8 100644 --- a/src/editor/dropdown-list/dropdown-list-editor.provider.ts +++ b/src/editor/dropdown-list/dropdown-list-editor.provider.ts @@ -32,6 +32,9 @@ export class DropDownListEditorProvider implements IEditorProvider { case 'EMOJI_PICKER': componentName = 'IBizEmojiPicker'; break; + case 'MOBDROPDOWNLIST_CASCADER': + componentName = 'IBizCascaderDropdown'; + break; default: break; } diff --git a/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.scss b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.scss new file mode 100644 index 0000000000..1fe64898fa --- /dev/null +++ b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.scss @@ -0,0 +1,34 @@ +@include b(cascader-dropdown) { + height: 100%; + + .van-field { + font-size: getCssVar('form-item', 'font-size'); + + &::after { + display: none; + } + } + + .van-cell--clickable:active { + background-color: transparent; + } + + @include m(disabled) { + color: getCssVar('form-item', 'disabled-color'); + + --van-field-input-text-color: #{getCssVar('form-item', 'disabled-color')}; + } + + @include m(readonly) { + --van-field-input-text-color: #{getCssVar('form-item', 'readonly-color')}; + + color: getCssVar('form-item', 'readonly-color'); + } + + input { + text-align: getCssVar(form-item-container, editor-align); + } + &.#{bem('cascader','','readonly')} { + text-align: getCssVar(form-item-container, editor-align); + } +} diff --git a/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx new file mode 100644 index 0000000000..06868bd6e7 --- /dev/null +++ b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx @@ -0,0 +1,210 @@ +import { ref, Ref, defineComponent, computed } from 'vue'; +import { + getDropdownProps, + getEditorEmits, + useNamespace, +} from '@ibiz-template/vue3-util'; +import { isNil } from 'ramda'; +import { DropDownListEditorController } from '../dropdown-list-editor.controller'; +import { IBizCommonRightIcon } from '../../common/right-icon/right-icon'; +import { usePopstateListener } from '../../../util'; +import './ibiz-cascader-dropdown.scss'; + +/** + * 移动端级联下拉列表(单选) + * @primary + * @description 使用van-cascader组件,用于选择树形代码表数据。基于`移动端下拉列表(单选)`编辑器扩展,编辑器样式代码名称为:CASCADER + * @ignoreprops autoFocus | overflowMode + * @ignoreemits infoTextChange | enter + */ +export const IBizCascaderDropdown = defineComponent({ + name: 'IBizCascaderDropdown', + props: getDropdownProps(), + emits: getEditorEmits(), + setup(props, { emit }) { + const ns = useNamespace('cascader-dropdown'); + const c: DropDownListEditorController = props.controller!; + + // 树数据(用于维护级联选择器默认选中数据) + const treeData: Ref = ref([]); + // 代码表map数据 + const codeListMap = new Map(); + // 选中值 + const selectValue: Ref = ref(null); + const show = ref(false); + const onClose = () => { + show.value = false; + }; + + // 处理树形结构代码表数据 + const handleTreeData = (nodes: readonly IData[]) => { + if (nodes.length === 0) { + return []; + } + const list: IData[] = []; + nodes.forEach((codeItem: IData) => { + const tempObj: IData = { + label: codeItem.text, + value: codeItem.value, + children: [], + }; + if (codeItem.children && codeItem.children.length > 0) { + tempObj.children = handleTreeData(codeItem.children); + } + list.push(tempObj); + }); + return list; + }; + + // 转化为树形结构数据 + const transformTreeData = (list: readonly IData[], pValueField: string) => { + // 清空map,保证 O(n) 复杂度 + codeListMap.clear(); + + // 第一遍:把每一项先包装成 TreeNode + list.forEach(item => { + codeListMap.set(item.value, { ...item }); + }); + + const rootNodes: IData[] = []; + if (pValueField) { + // 第二遍:根据父属性把节点挂到父节点 children 里 + list.forEach(item => { + const node = codeListMap.get(item.value)!; + if (isNil(item.data[pValueField])) { + // 父属性为空 => 根节点 + rootNodes.push(node); + } else { + const parent = codeListMap.get(item.data[pValueField]); + // 如果父节点不存在,直接丢弃 + if (parent) { + if (!parent.children) { + parent.children = []; + } + parent.children.push(node); + } + } + }); + } else { + // 没有父值属性时,直接平铺 + rootNodes.push(...list); + } + + return rootNodes; + }; + + // 加载代码表数据 + const loadCodeList = async () => { + const codeList = await c.loadCodeList(props.data!); + const index = codeList.findIndex(x => x.children); + if (index !== -1) { + // 存在树形数据直接使用 + treeData.value = handleTreeData(codeList); + } else { + // 存在父值属性时转换为树型数据,否则平铺 + const { pvaluefield } = c.editorParams; + treeData.value = transformTreeData(codeList, pvaluefield); + } + }; + + loadCodeList(); + + // 处理级联选择器值改变 + const onFinish = ($event: IData) => { + const { value } = $event; + onClose(); + + emit('change', value); + }; + + const onChange = ($event: IData) => { + const { value } = $event; + emit('change', value); + }; + + // 当前值 + const curValue = computed(() => { + const item = codeListMap.get(props.value || ''); + return item?.text || ''; + }); + + const onBlur = () => { + emit('blur'); + }; + + const onFocus = () => { + emit('focus'); + }; + + const openPopup = () => { + if (props.disabled || props.readonly) { + return; + } + show.value = !show.value; + }; + + // 监听popstate事件 + usePopstateListener(onClose); + + return { + ns, + c, + show, + treeData, + selectValue, + curValue, + onBlur, + onFocus, + openPopup, + onClose, + onChange, + onFinish, + }; + }, + render() { + return ( +
+ {this.readonly && this.curValue} + {!this.readonly && ( + + {{ + 'right-icon': !this.readonly && ( + + ), + }} + + )} + + + +
+ ); + }, +}); diff --git a/src/editor/dropdown-list/index.ts b/src/editor/dropdown-list/index.ts index 78c0001b3f..80125ad4ab 100644 --- a/src/editor/dropdown-list/index.ts +++ b/src/editor/dropdown-list/index.ts @@ -1,4 +1,5 @@ export { IBizDropdown } from './ibiz-dropdown/ibiz-dropdown'; export { IBizEmojiPicker } from './ibiz-emoji-picker/ibiz-emoji-picker'; +export { IBizCascaderDropdown } from './ibiz-cascader-dropdown/ibiz-cascader-dropdown'; export * from './dropdown-list-editor.controller'; export * from './dropdown-list-editor.provider'; diff --git a/src/editor/index.ts b/src/editor/index.ts index 89104b38b3..d1438e2c2a 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -11,6 +11,7 @@ import { import { IBizDropdown, IBizEmojiPicker, + IBizCascaderDropdown, DropDownListEditorProvider, } from './dropdown-list'; import { CheckBoxListEditorProvider, IBizCheckboxList } from './check-box-list'; @@ -60,6 +61,7 @@ export const IBizEditor = { v.component(IBizDropdown.name, IBizDropdown); v.component(IBizDropdownList.name, IBizDropdownList); v.component(IBizEmojiPicker.name, IBizEmojiPicker); + v.component(IBizCascaderDropdown.name, IBizCascaderDropdown); v.component(IBizCheckboxList.name, IBizCheckboxList); v.component(IBizSlider.name, IBizSlider); v.component(IBizRaw.name, IBizRaw); @@ -142,6 +144,12 @@ export const IBizEditor = { () => new DropDownListEditorProvider('EMOJI_PICKER'), ); + // 级联下拉 + registerEditorProvider( + 'MOBDROPDOWNLIST_CASCADER', + () => new DropDownListEditorProvider('MOBDROPDOWNLIST_CASCADER'), + ); + // 下拉列表框多选(复选框) registerEditorProvider( 'MOBCHECKLIST', -- Gitee From 598d0716c601368b405428d9b18288fe7cf50f4d Mon Sep 17 00:00:00 2001 From: Cano1997 <58964671+Cano1997@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:08:49 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=BA=A7?= =?UTF-8?q?=E8=81=94=E4=B8=8B=E6=8B=89=E7=BC=96=E8=BE=91=E5=99=A8=20--f1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ibiz-cascader-dropdown.tsx | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx index 06868bd6e7..2b00ff32f7 100644 --- a/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx +++ b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx @@ -36,26 +36,6 @@ export const IBizCascaderDropdown = defineComponent({ show.value = false; }; - // 处理树形结构代码表数据 - const handleTreeData = (nodes: readonly IData[]) => { - if (nodes.length === 0) { - return []; - } - const list: IData[] = []; - nodes.forEach((codeItem: IData) => { - const tempObj: IData = { - label: codeItem.text, - value: codeItem.value, - children: [], - }; - if (codeItem.children && codeItem.children.length > 0) { - tempObj.children = handleTreeData(codeItem.children); - } - list.push(tempObj); - }); - return list; - }; - // 转化为树形结构数据 const transformTreeData = (list: readonly IData[], pValueField: string) => { // 清空map,保证 O(n) 复杂度 @@ -96,15 +76,9 @@ export const IBizCascaderDropdown = defineComponent({ // 加载代码表数据 const loadCodeList = async () => { const codeList = await c.loadCodeList(props.data!); - const index = codeList.findIndex(x => x.children); - if (index !== -1) { - // 存在树形数据直接使用 - treeData.value = handleTreeData(codeList); - } else { - // 存在父值属性时转换为树型数据,否则平铺 - const { pvaluefield } = c.editorParams; - treeData.value = transformTreeData(codeList, pvaluefield); - } + // 存在父值属性时转换为树型数据,否则平铺 + const { pvaluefield } = c.editorParams; + treeData.value = transformTreeData(codeList, pvaluefield); }; loadCodeList(); -- Gitee